diff --git a/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs b/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs index d6e8e4eae..617773c4f 100644 --- a/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs +++ b/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs @@ -556,7 +556,7 @@ mod tests { }, }; use pumpkin_world::inventory::SimpleInventory; - use tokio::sync::Mutex; + use std::sync::Mutex; use crate::{ entity_equipment::EntityEquipment, @@ -911,7 +911,7 @@ mod tests { player_inventory .main_inventory .write() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .fill_with(|| ItemStack::new(64, &Item::COBBLESTONE)); merchant_inventory .set_stack(0, ItemStack::new(9, &Item::EMERALD)) diff --git a/crates/pumpkin-inventory/src/player/player_inventory.rs b/crates/pumpkin-inventory/src/player/player_inventory.rs index f98cdf9bd..ee6cd891b 100644 --- a/crates/pumpkin-inventory/src/player/player_inventory.rs +++ b/crates/pumpkin-inventory/src/player/player_inventory.rs @@ -19,9 +19,9 @@ use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; use std::any::Any; use std::collections::HashMap; use std::pin::Pin; -use std::sync::Arc; use std::sync::atomic::{AtomicU8, Ordering}; -use tokio::sync::{Mutex, RwLock}; +use std::sync::{Arc, Mutex, RwLock}; + use tracing::warn; /// The player's inventory. @@ -99,27 +99,36 @@ impl PlayerInventory { /// Gets the item in the currently selected hotbar slot. /// /// This is the item the player is currently holding in their main hand. - pub async fn held_item(&self) -> ItemStack { - let inv = self.main_inventory.read().await; + pub fn held_item(&self) -> ItemStack { + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv[self.get_selected_slot() as usize].clone() } /// Sets the item in the currently selected hotbar slot. - pub async fn set_held_item(&self, stack: ItemStack) { + pub fn set_held_item(&self, stack: ItemStack) { let selected = self.get_selected_slot() as usize; - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv[selected] = stack; } /// Sets the item in the specified hand. - pub async fn set_stack_in_hand(&self, hand: Hand, stack: ItemStack) { + pub fn set_stack_in_hand(&self, hand: Hand, stack: ItemStack) { match hand { - Hand::Right => self.set_held_item(stack).await, + Hand::Right => self.set_held_item(stack), Hand::Left => { let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else { return; }; - self.entity_equipment.lock().await.put(slot, stack); + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(slot, stack); } } } @@ -128,34 +137,43 @@ impl PlayerInventory { /// /// # Arguments /// - `hand` - Which hand to get the item from - pub async fn get_stack_in_hand(&self, hand: Hand) -> ItemStack { + pub fn get_stack_in_hand(&self, hand: Hand) -> ItemStack { match hand { - Hand::Left => self.off_hand_item().await, - Hand::Right => self.held_item().await, + Hand::Left => self.off_hand_item(), + Hand::Right => self.held_item(), } } /// Gets the item in the off-hand. /// /// Mojang name: `getOffHandStack` - pub async fn off_hand_item(&self) -> ItemStack { + pub fn off_hand_item(&self) -> ItemStack { let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else { return ItemStack::EMPTY.clone(); }; - self.entity_equipment.lock().await.get(slot) + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(slot) } /// Swaps the items between main hand and off-hand. /// /// # Returns /// The new main hand item and new off-hand item. - pub async fn swap_item(&self) -> (ItemStack, ItemStack) { + pub fn swap_item(&self) -> (ItemStack, ItemStack) { let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else { return (ItemStack::EMPTY.clone(), ItemStack::EMPTY.clone()); }; - let mut equipment = self.entity_equipment.lock().await; + let mut equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let selected = self.get_selected_slot() as usize; - let mut main_inv = self.main_inventory.write().await; + let mut main_inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); let main_hand_item = main_inv[selected].clone(); let new_main = equipment.put(slot, main_hand_item.clone()); main_inv[selected] = new_main.clone(); @@ -169,27 +187,30 @@ impl PlayerInventory { } /// Adds a stack to any available slot, prioritizing stacking with existing items. - async fn add_stack(&self, stack: ItemStack) -> usize { - let mut slot_index = self.get_occupied_slot_with_room_for_stack(&stack).await; + fn add_stack(&self, stack: ItemStack) -> usize { + let mut slot_index = self.get_occupied_slot_with_room_for_stack(&stack); if slot_index == -1 { - slot_index = self.get_empty_slot().await; + slot_index = self.get_empty_slot(); } if slot_index == -1 { stack.item_count as usize } else { - self.add_stack_to_slot(slot_index as usize, stack).await + self.add_stack_to_slot(slot_index as usize, stack) } } /// Adds a stack to a specific slot. /// /// Returns the number of items that couldn't fit. - async fn add_stack_to_slot(&self, slot: usize, stack: ItemStack) -> usize { + fn add_stack_to_slot(&self, slot: usize, stack: ItemStack) -> usize { if slot >= Self::MAIN_SIZE { if let Some(slot_type) = self.equipment_slots.get(&slot) { - let mut equipment = self.entity_equipment.lock().await; + let mut equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let current = equipment.get(slot_type); if current.is_empty() { equipment.put(slot_type, stack); @@ -199,7 +220,10 @@ impl PlayerInventory { return stack.item_count as usize; } - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut stack_count = stack.item_count; let self_stack = &mut inv[slot]; @@ -221,8 +245,11 @@ impl PlayerInventory { /// /// # Returns /// The slot index or -1 if inventory is full. - async fn get_empty_slot(&self) -> i16 { - let inv = self.main_inventory.read().await; + fn get_empty_slot(&self) -> i16 { + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (i, stack) in inv.iter().enumerate() { if stack.is_empty() { return i as i16; @@ -242,14 +269,17 @@ impl PlayerInventory { /// Finds a slot with the same item type that has room for more items. /// /// Checks selected slot, off-hand, then other slots. - async fn get_occupied_slot_with_room_for_stack(&self, stack: &ItemStack) -> i16 { + fn get_occupied_slot_with_room_for_stack(&self, stack: &ItemStack) -> i16 { let selected = self.get_selected_slot() as usize; - let inv = self.main_inventory.read().await; + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); if Self::can_stack_add_more(&inv[selected], stack) { return selected as i16; } - let off_hand = self.off_hand_item().await; + let off_hand = self.off_hand_item(); if Self::can_stack_add_more(&off_hand, stack) { return Self::OFF_HAND_SLOT as i16; } @@ -270,8 +300,8 @@ impl PlayerInventory { /// /// # Returns /// `true` if any items were inserted, `false` otherwise. - pub async fn insert_stack_anywhere(&self, stack: &mut ItemStack) -> bool { - self.insert_stack(-1, stack).await + pub fn insert_stack_anywhere(&self, stack: &mut ItemStack) -> bool { + self.insert_stack(-1, stack) } /// Inserts a stack into a specific slot or any slot. @@ -282,7 +312,7 @@ impl PlayerInventory { /// /// # Returns /// `true` if any items were inserted, `false` otherwise. - pub async fn insert_stack(&self, slot: i16, stack: &mut ItemStack) -> bool { + pub fn insert_stack(&self, slot: i16, stack: &mut ItemStack) -> bool { if stack.is_empty() { return false; } @@ -292,9 +322,9 @@ impl PlayerInventory { loop { i = stack.item_count; if slot == -1 { - stack.set_count(self.add_stack(stack.clone()).await as u8); + stack.set_count(self.add_stack(stack.clone()) as u8); } else { - stack.set_count(self.add_stack_to_slot(slot as usize, stack.clone()).await as u8); + stack.set_count(self.add_stack_to_slot(slot as usize, stack.clone()) as u8); } if stack.is_empty() || stack.item_count >= i { @@ -309,8 +339,11 @@ impl PlayerInventory { /// /// # Returns /// The slot index or -1 if not found. - pub async fn get_slot_with_stack(&self, stack: &ItemStack) -> i16 { - let inv = self.main_inventory.read().await; + pub fn get_slot_with_stack(&self, stack: &ItemStack) -> i16 { + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (i, item) in inv.iter().enumerate() { if !item.is_empty() && item.are_items_and_components_equal(stack) { return i as i16; @@ -322,8 +355,11 @@ impl PlayerInventory { /// Finds an empty hotbar slot to swap an item to. /// /// First looks for empty slots, then slots without enchantments. - async fn get_swappable_hotbar_slot(&self) -> usize { - let inv = self.main_inventory.read().await; + fn get_swappable_hotbar_slot(&self) -> usize { + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); let selected_slot = self.get_selected_slot() as usize; for i in 0..Self::HOTBAR_SIZE { let check_index = (i + selected_slot) % 9; @@ -338,11 +374,14 @@ impl PlayerInventory { /// Swaps an item stack with an item on the hotbar. /// /// Finds an empty hotbar slot and places the stack there. - pub async fn swap_stack_with_hotbar(&self, stack: ItemStack) { - let swappable = self.get_swappable_hotbar_slot().await; + pub fn swap_stack_with_hotbar(&self, stack: ItemStack) { + let swappable = self.get_swappable_hotbar_slot(); self.set_selected_slot(swappable as u8); let selected = self.get_selected_slot() as usize; - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(empty_slot) = inv.iter().position(ItemStack::is_empty) && !inv[selected].is_empty() @@ -354,11 +393,14 @@ impl PlayerInventory { } /// Swaps the items at two slot indices. - pub async fn swap_slot_with_hotbar(&self, slot: usize) { - let swappable = self.get_swappable_hotbar_slot().await; + pub fn swap_slot_with_hotbar(&self, slot: usize) { + let swappable = self.get_swappable_hotbar_slot(); self.set_selected_slot(swappable as u8); let selected = self.get_selected_slot() as usize; - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv.swap(selected, slot); } @@ -376,9 +418,9 @@ impl PlayerInventory { pub async fn offer(&self, stack: ItemStack, notify_client: bool, player: &dyn InventoryPlayer) { let mut stack = stack; while !stack.is_empty() { - let mut room_for_stack = self.get_occupied_slot_with_room_for_stack(&stack).await; + let mut room_for_stack = self.get_occupied_slot_with_room_for_stack(&stack); if room_for_stack == -1 { - room_for_stack = self.get_empty_slot().await; + room_for_stack = self.get_empty_slot(); } if room_for_stack == -1 { @@ -388,11 +430,7 @@ impl PlayerInventory { let items_fit = stack.get_max_stack_size() - self.get_stack(room_for_stack as usize).await.item_count; - if self - .insert_stack(room_for_stack, &mut stack.split(items_fit)) - .await - && notify_client - { + if self.insert_stack(room_for_stack, &mut stack.split(items_fit)) && notify_client { player .enqueue_slot_set_packet(&CSetPlayerInventory::new( i32::from(room_for_stack).into(), @@ -407,9 +445,15 @@ impl PlayerInventory { impl Clearable for PlayerInventory { fn clear(&self) -> Pin + Send + '_>> { Box::pin(async move { - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv.fill_with(|| ItemStack::EMPTY.clone()); - self.entity_equipment.lock().await.clear(); + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clear(); }) } } @@ -421,13 +465,20 @@ impl Inventory for PlayerInventory { fn is_empty(&self) -> InventoryFuture<'_, bool> { Box::pin(async move { - let inv = self.main_inventory.read().await; + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); if inv.iter().any(|s| !s.is_empty()) { return false; } for slot in self.equipment_slots.values() { - let eq_item = self.entity_equipment.lock().await.get(slot); + let eq_item = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(slot); if !eq_item.is_empty() { return false; } @@ -440,10 +491,16 @@ impl Inventory for PlayerInventory { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { if slot < Self::MAIN_SIZE { - let inv = self.main_inventory.read().await; + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv[slot].clone() } else if let Some(slot) = self.equipment_slots.get(&slot) { - self.entity_equipment.lock().await.get(slot) + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(slot) } else { ItemStack::EMPTY.clone() } @@ -453,12 +510,15 @@ impl Inventory for PlayerInventory { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { if slot < Self::MAIN_SIZE { - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); std::mem::replace(&mut inv[slot], ItemStack::EMPTY.clone()) } else if let Some(slot) = self.equipment_slots.get(&slot) { self.entity_equipment .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .put(slot, ItemStack::EMPTY.clone()) } else { ItemStack::EMPTY.clone() @@ -469,14 +529,20 @@ impl Inventory for PlayerInventory { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { if slot < Self::MAIN_SIZE { - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); if !inv[slot].is_empty() && amount > 0 { inv[slot].split(amount) } else { ItemStack::EMPTY.clone() } } else if let Some(slot) = self.equipment_slots.get(&slot) { - let mut equipment = self.entity_equipment.lock().await; + let mut equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut stack = equipment.get(slot); if !stack.is_empty() && amount > 0 { @@ -495,10 +561,16 @@ impl Inventory for PlayerInventory { fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { Box::pin(async move { if slot < Self::MAIN_SIZE { - let mut inv = self.main_inventory.write().await; + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); inv[slot] = stack; } else if let Some(slot) = self.equipment_slots.get(&slot) { - self.entity_equipment.lock().await.put(slot, stack); + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(slot, stack); } else { warn!("Failed to get Equipment Slot at {slot}"); } diff --git a/crates/pumpkin/src/block/blocks/abstract_wall_mounting.rs b/crates/pumpkin/src/block/blocks/abstract_wall_mounting.rs index 689e020eb..bbe9eb499 100644 --- a/crates/pumpkin/src/block/blocks/abstract_wall_mounting.rs +++ b/crates/pumpkin/src/block/blocks/abstract_wall_mounting.rs @@ -6,10 +6,7 @@ use pumpkin_data::{ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; -use crate::{ - block::{BlockFuture, GetStateForNeighborUpdateArgs}, - entity::player::Player, -}; +use crate::{block::GetStateForNeighborUpdateArgs, entity::player::Player}; pub trait WallMountedBlock: Send + Sync { fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection; @@ -49,10 +46,10 @@ pub trait WallMountedBlock: Send + Sync { } } - fn can_place_at<'a>( - &'a self, - world: &'a dyn BlockAccessor, - pos: &'a BlockPos, + fn can_place_at( + &self, + world: &dyn BlockAccessor, + pos: &BlockPos, direction: BlockDirection, ) -> bool { let block_pos = pos.offset(direction.to_offset()); @@ -60,18 +57,16 @@ pub trait WallMountedBlock: Send + Sync { block_state.is_side_solid(direction.opposite()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if self.get_direction(args.state_id, args.block).opposite() == args.direction - && !self.can_place_at(args.world, args.position, args.direction) - { - Block::AIR.default_state.id - } else { - args.state_id - } - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if self.get_direction(args.state_id, args.block).opposite() == args.direction + && !self.can_place_at(args.world, args.position, args.direction) + { + Block::AIR.default_state.id + } else { + args.state_id + } } } diff --git a/crates/pumpkin/src/block/blocks/amethyst.rs b/crates/pumpkin/src/block/blocks/amethyst.rs index cb2bfab66..7c785d313 100644 --- a/crates/pumpkin/src/block/blocks/amethyst.rs +++ b/crates/pumpkin/src/block/blocks/amethyst.rs @@ -7,8 +7,8 @@ use pumpkin_world::world::BlockFlags; use rand::RngExt; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, RandomTickArgs, blocks::abstract_wall_mounting::WallMountedBlock, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + RandomTickArgs, blocks::abstract_wall_mounting::WallMountedBlock, }; const ALL_DIRECTIONS: [BlockDirection; 6] = [ @@ -35,16 +35,12 @@ impl BlockMetadata for AmethystBlock { } impl BlockBehaviour for AmethystBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = AmethystClusterLikeProperties::from_state_id( - args.block.default_state.id, - args.block, - ); - props.facing = args.direction.to_facing(); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + AmethystClusterLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.facing = args.direction.to_facing(); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -56,11 +52,11 @@ impl BlockBehaviour for AmethystBlock { WallMountedBlock::can_place_at(self, args.block_accessor, args.position, direction) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + WallMountedBlock::get_state_for_neighbor_update(self, args) } } @@ -75,56 +71,53 @@ impl WallMountedBlock for AmethystBlock { pub struct BuddingAmethystBlock; impl BlockBehaviour for BuddingAmethystBlock { - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..5) == 0 { - let grow_direction = { - let mut rng = rand::rng(); - ALL_DIRECTIONS[rng.random_range(0..ALL_DIRECTIONS.len())] + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..5) == 0 { + let grow_direction = { + let mut rng = rand::rng(); + ALL_DIRECTIONS[rng.random_range(0..ALL_DIRECTIONS.len())] + }; + let grow_pos = args.position.offset(grow_direction.to_offset()); + let (relative_block, relative_state) = args.world.get_block_and_state(&grow_pos); + let relative_state_id = relative_state.id; + + let next_stage_and_water = + if can_cluster_grow_at_state(relative_block, relative_state_id) { + Some((&Block::SMALL_AMETHYST_BUD, relative_block == &Block::WATER)) + } else if relative_block == &Block::SMALL_AMETHYST_BUD { + let props = AmethystClusterLikeProperties::from_state_id( + relative_state_id, + &Block::SMALL_AMETHYST_BUD, + ); + (props.facing == grow_direction.to_facing()) + .then_some((&Block::MEDIUM_AMETHYST_BUD, props.waterlogged)) + } else if relative_block == &Block::MEDIUM_AMETHYST_BUD { + let props = AmethystClusterLikeProperties::from_state_id( + relative_state_id, + &Block::MEDIUM_AMETHYST_BUD, + ); + (props.facing == grow_direction.to_facing()) + .then_some((&Block::LARGE_AMETHYST_BUD, props.waterlogged)) + } else if relative_block == &Block::LARGE_AMETHYST_BUD { + let props = AmethystClusterLikeProperties::from_state_id( + relative_state_id, + &Block::LARGE_AMETHYST_BUD, + ); + (props.facing == grow_direction.to_facing()) + .then_some((&Block::AMETHYST_CLUSTER, props.waterlogged)) + } else { + None }; - let grow_pos = args.position.offset(grow_direction.to_offset()); - let (relative_block, relative_state) = args.world.get_block_and_state(&grow_pos); - let relative_state_id = relative_state.id; - let next_stage_and_water = - if can_cluster_grow_at_state(relative_block, relative_state_id) { - Some((&Block::SMALL_AMETHYST_BUD, relative_block == &Block::WATER)) - } else if relative_block == &Block::SMALL_AMETHYST_BUD { - let props = AmethystClusterLikeProperties::from_state_id( - relative_state_id, - &Block::SMALL_AMETHYST_BUD, - ); - (props.facing == grow_direction.to_facing()) - .then_some((&Block::MEDIUM_AMETHYST_BUD, props.waterlogged)) - } else if relative_block == &Block::MEDIUM_AMETHYST_BUD { - let props = AmethystClusterLikeProperties::from_state_id( - relative_state_id, - &Block::MEDIUM_AMETHYST_BUD, - ); - (props.facing == grow_direction.to_facing()) - .then_some((&Block::LARGE_AMETHYST_BUD, props.waterlogged)) - } else if relative_block == &Block::LARGE_AMETHYST_BUD { - let props = AmethystClusterLikeProperties::from_state_id( - relative_state_id, - &Block::LARGE_AMETHYST_BUD, - ); - (props.facing == grow_direction.to_facing()) - .then_some((&Block::AMETHYST_CLUSTER, props.waterlogged)) - } else { - None - }; - - if let Some((next_stage, waterlogged)) = next_stage_and_water { - let mut target_props = AmethystClusterLikeProperties::default(next_stage); - target_props.facing = grow_direction.to_facing(); - target_props.waterlogged = waterlogged; - let target_state_id = target_props.to_state_id(next_stage); - args.world - .set_block_state(&grow_pos, target_state_id, BlockFlags::NOTIFY_ALL) - .await; - } + if let Some((next_stage, waterlogged)) = next_stage_and_water { + let mut target_props = AmethystClusterLikeProperties::default(next_stage); + target_props.facing = grow_direction.to_facing(); + target_props.waterlogged = waterlogged; + let target_state_id = target_props.to_state_id(next_stage); + args.world + .set_block_state(&grow_pos, target_state_id, BlockFlags::NOTIFY_ALL); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/anvil.rs b/crates/pumpkin/src/block/blocks/anvil.rs index e7146aabd..c75df379b 100644 --- a/crates/pumpkin/src/block/blocks/anvil.rs +++ b/crates/pumpkin/src/block/blocks/anvil.rs @@ -1,8 +1,8 @@ use crate::block::blocks::falling::FallingBlock; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, - OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, + PlacedArgs, }; use pumpkin_data::BlockStateId; @@ -23,58 +23,50 @@ use tokio::sync::Mutex; pub struct AnvilBlock; impl BlockBehaviour for AnvilBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithAnvil as i32, - 1, - ) - .await; - args.player - .open_handled_screen(&AnvilScreenFactory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithAnvil as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&AnvilScreenFactory, Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - FallingBlock::placed(&FallingBlock, args).await; - }) + fn placed(&self, args: PlacedArgs<'_>) { + FallingBlock::placed(&FallingBlock, args); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let dir = args - .player - .living_entity - .entity - .get_horizontal_facing() - .rotate_clockwise(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let dir = args + .player + .living_entity + .entity + .get_horizontal_facing() + .rotate_clockwise(); - let mut props = WallTorchLikeProperties::default(args.block); + let mut props = WallTorchLikeProperties::default(args.block); - props.facing = dir; - props.to_state_id(args.block) - }) + props.facing = dir; + props.to_state_id(args.block) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - FallingBlock::on_scheduled_tick(&FallingBlock, args).await; - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + FallingBlock::on_scheduled_tick(&FallingBlock, args); } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin( - async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await }, - ) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + FallingBlock::get_state_for_neighbor_update(&FallingBlock, args) } } diff --git a/crates/pumpkin/src/block/blocks/banners.rs b/crates/pumpkin/src/block/blocks/banners.rs index 603686695..b8e20aa83 100644 --- a/crates/pumpkin/src/block/blocks/banners.rs +++ b/crates/pumpkin/src/block/blocks/banners.rs @@ -1,6 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, - PlacedArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::entity::EntityBase; use pumpkin_data::BlockStateId; @@ -17,46 +16,39 @@ use std::sync::Arc; pub struct BannerBlock; impl BlockBehaviour for BannerBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = BannerBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = WhiteBannerLikeProperties::default(args.block); - props.rotation = args.player.get_entity().get_flipped_rotation_16(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = WhiteBannerLikeProperties::default(args.block); + props.rotation = args.player.get_entity().get_flipped_rotation_16(); + props.to_state_id(args.block) } fn can_place_at(&self, args: crate::block::CanPlaceAtArgs<'_>) -> bool { can_place_at(args.block_accessor, args.position) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/barrel.rs b/crates/pumpkin/src/block/blocks/barrel.rs index e52dae737..c28a9adb7 100644 --- a/crates/pumpkin/src/block/blocks/barrel.rs +++ b/crates/pumpkin/src/block/blocks/barrel.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::{BlockFuture, GetComparatorOutputArgs, OnPlaceArgs, PlacedArgs}; +use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -50,54 +50,47 @@ impl ScreenHandlerFactory for BarrelScreenFactory { pub struct BarrelBlock; impl BlockBehaviour for BarrelBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = BarrelLikeProperties::default(args.block); - props.facing = args.player.get_entity().get_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = BarrelLikeProperties::default(args.block); + props.facing = args.player.get_entity().get_facing().opposite(); + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::OpenBarrel as i32, - 1, - ) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::OpenBarrel as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&BarrelScreenFactory(inventory), Some(pos)) .await; - args.player - .open_handled_screen(&BarrelScreenFactory(inventory), Some(*args.position)) - .await; - } + }); + } - BlockActionResult::Success - }) + BlockActionResult::Success } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let barrel_block_entity = BarrelBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(barrel_block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let barrel_block_entity = BarrelBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(barrel_block_entity)); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/barrier.rs b/crates/pumpkin/src/block/blocks/barrier.rs index 6b49f4299..d5ade36f9 100644 --- a/crates/pumpkin/src/block/blocks/barrier.rs +++ b/crates/pumpkin/src/block/blocks/barrier.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{ BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties, @@ -11,29 +11,25 @@ use pumpkin_world::tick::TickPriority; pub struct BarrierBlock; impl BlockBehaviour for BarrierBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = BarrierLikeProperties::default(args.block); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = BarrierLikeProperties::default(args.block); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = BarrierLikeProperties::from_state_id(args.state_id, args.block); - if props.waterlogged { - args.world.schedule_fluid_tick( - &Fluid::WATER, - *args.position, - Fluid::WATER.flow_speed as u8, - TickPriority::Normal, - ); - } - props.to_state_id(args.block) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = BarrierLikeProperties::from_state_id(args.state_id, args.block); + if props.waterlogged { + args.world.schedule_fluid_tick( + &Fluid::WATER, + *args.position, + Fluid::WATER.flow_speed as u8, + TickPriority::Normal, + ); + } + props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/beacon.rs b/crates/pumpkin/src/block/blocks/beacon.rs index ed9323eb2..9975d431b 100644 --- a/crates/pumpkin/src/block/blocks/beacon.rs +++ b/crates/pumpkin/src/block/blocks/beacon.rs @@ -12,7 +12,7 @@ use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; // Create the factory just like ChestScreenFactory struct BeaconScreenFactory(Arc); @@ -48,29 +48,29 @@ impl ScreenHandlerFactory for BeaconScreenFactory { pub struct BeaconBlock; impl BlockBehaviour for BeaconBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let block_entity = args.world.get_block_entity(args.position); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let block_entity = args.world.get_block_entity(args.position); - // Extract the inventory from the entity - let Some(inventory) = block_entity.and_then(BlockEntity::get_inventory) else { - return BlockActionResult::Fail; - }; + // Extract the inventory from the entity + let Some(inventory) = block_entity.and_then(BlockEntity::get_inventory) else { + return BlockActionResult::Fail; + }; - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithBeacon as i32, - 1, - ) + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithBeacon as i32, + 1, + ); + + // Open the screen using the factory + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&BeaconScreenFactory(inventory), Some(pos)) .await; + }); - // Open the screen using the factory - args.player - .open_handled_screen(&BeaconScreenFactory(inventory), Some(*args.position)) - .await; - - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/bed.rs b/crates/pumpkin/src/block/blocks/bed.rs index 3a9c9b4a1..2673d7b13 100644 --- a/crates/pumpkin/src/block/blocks/bed.rs +++ b/crates/pumpkin/src/block/blocks/bed.rs @@ -13,7 +13,6 @@ use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; -use crate::block::BlockFuture; use crate::block::OnLandedUponArgs; use crate::block::UpdateEntityMovementAfterFallOnArgs; use crate::block::bounce_entity_after_fall; @@ -22,7 +21,7 @@ use crate::block::{ BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, }; -use crate::entity::{Entity, EntityBase}; +use crate::entity::{Entity, EntityBase, player::Player}; use crate::world::World; type BedProperties = pumpkin_data::block_properties::WhiteBedLikeProperties; @@ -84,36 +83,27 @@ impl BlockBehaviour for BedBlock { false } - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() { - living - .handle_fall_damage(args.entity, args.fall_distance * 0.5, 1.0) - .await; - } - }) + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() { + living.handle_fall_damage(args.entity, args.fall_distance * 0.5, 1.0); + } } - fn update_entity_movement_after_fall_on<'a>( - &'a self, - args: UpdateEntityMovementAfterFallOnArgs<'a>, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { bounce_entity_after_fall(args.entity, 0.66) }) + fn update_entity_movement_after_fall_on(&self, args: UpdateEntityMovementAfterFallOnArgs<'_>) { + bounce_entity_after_fall(args.entity, 0.66); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut bed_props = BedProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut bed_props = BedProperties::default(args.block); - bed_props.facing = args.player.get_entity().get_horizontal_facing(); - bed_props.part = BedPart::Foot; + bed_props.facing = args.player.get_entity().get_horizontal_facing(); + bed_props.part = BedPart::Foot; - bed_props.to_state_id(args.block) - }) + bed_props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let bed_entity = BedBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(bed_entity)); @@ -122,283 +112,275 @@ impl BlockBehaviour for BedBlock { bed_head_props.part = BedPart::Head; let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset()); - args.world - .set_block_state( - &bed_head_pos, - bed_head_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; + args.world.set_block_state( + &bed_head_pos, + bed_head_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); let bed_head_entity = BedBlockEntity::new(bed_head_pos); args.world.add_block_entity(Arc::new(bed_head_entity)); - }) + } } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + { args.world.play_bedrock_level_sound( "place", &args.position.to_centered_f64(), i32::from(pumpkin_data::BlockState::to_be_network_id(args.state_id)), ); - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let bed_props = BedProperties::from_state_id(args.state.id, args.block); - let other_half_pos = if bed_props.part == BedPart::Head { - args.position - .offset(bed_props.facing.opposite().to_offset()) - } else { - args.position.offset(bed_props.facing.to_offset()) - }; + fn broken(&self, args: BrokenArgs<'_>) { + let bed_props = BedProperties::from_state_id(args.state.id, args.block); + let other_half_pos = if bed_props.part == BedPart::Head { + args.position + .offset(bed_props.facing.opposite().to_offset()) + } else { + args.position.offset(bed_props.facing.to_offset()) + }; + let neighbor_state_id = args.world.get_block_state_id(&other_half_pos); + if neighbor_state_id.to_block_id() != args.block.id { + args.world.update_neighbors(&other_half_pos, None); + return; + } - let neighbor_state_id = args.world.get_block_state_id(&other_half_pos); - if neighbor_state_id.to_block_id() != args.block.id { - args.world.update_neighbors(&other_half_pos, None).await; - return; - } + let is_creative = args.player.gamemode.load() == GameMode::Creative; + let flags = if bed_props.part == BedPart::Foot && !is_creative { + // Breaking foot in survival -> allow head to drop + BlockFlags::NOTIFY_NEIGHBORS + } else { + // Breaking head OR creative mode -> skip drops + BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS + }; - let is_creative = args.player.gamemode.load() == GameMode::Creative; - let flags = if bed_props.part == BedPart::Foot && !is_creative { - // Breaking foot in survival -> allow head to drop - BlockFlags::NOTIFY_NEIGHBORS - } else { - // Breaking head OR creative mode -> skip drops - BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS - }; - - args.world - .break_block(&other_half_pos, Some(args.player.clone()), flags) - .await; - }) + args.world + .break_block(&other_half_pos, Some(args.player.clone()), flags); } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.moved { - return; - } + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if args.moved { + return; + } - // If the block is being replaced with air (i.e., broken), the `broken` callback - // will handle breaking the other half with the correct drop flags. Only handle it here - // if the block is being replaced with something else (e.g., piston movement). - let new_state_id = args.world.get_block_state_id(args.position); - let new_block = Block::from_state_id(new_state_id); - if new_block == &Block::AIR { - return; - } + // If the block is being replaced with air (i.e., broken), the `broken` callback + // will handle breaking the other half with the correct drop flags. Only handle it here + // if the block is being replaced with something else (e.g., piston movement). + let new_state_id = args.world.get_block_state_id(args.position); + let new_block = Block::from_state_id(new_state_id); + if new_block == &Block::AIR { + return; + } - let bed_props = BedProperties::from_state_id(args.old_state_id, args.block); - let other_half_pos = if bed_props.part == BedPart::Head { - args.position - .offset(bed_props.facing.opposite().to_offset()) - } else { - args.position.offset(bed_props.facing.to_offset()) - }; + let bed_props = BedProperties::from_state_id(args.old_state_id, args.block); + let other_half_pos = if bed_props.part == BedPart::Head { + args.position + .offset(bed_props.facing.opposite().to_offset()) + } else { + args.position.offset(bed_props.facing.to_offset()) + }; - let (other_block, other_state) = args.world.get_block_and_state(&other_half_pos); - if other_block == args.block { - let other_props = BedProperties::from_state_id(other_state.id, other_block); - if other_props.part != bed_props.part { - args.world - .break_block( - &other_half_pos, - None, - BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } + let (other_block, other_state) = args.world.get_block_and_state(&other_half_pos); + if other_block == args.block { + let other_props = BedProperties::from_state_id(other_state.id, other_block); + if other_props.part != bed_props.part { + args.world.break_block( + &other_half_pos, + None, + BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS, + ); } - }) + } } - #[expect(clippy::too_many_lines)] - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let bed_props = BedProperties::from_state_id(state_id, args.block); - - let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head { - ( - *args.position, - args.position - .offset(bed_props.facing.opposite().to_offset()), - ) - } else { - ( - args.position.offset(bed_props.facing.to_offset()), - *args.position, - ) - }; - - // Explode if not in the overworld - if args.world.dimension != Dimension::OVERWORLD { - args.world - .break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS) - .await; - args.world - .break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS) - .await; - - args.world - .explode( - bed_head_pos.to_centered_f64(), - 5.0, - crate::world::ExplosionInteraction::Block, - ) - .await; - - return BlockActionResult::SuccessServer; - } - - // Make sure the bed is not obstructed - if args.world.get_block_state(&bed_head_pos.up()).is_solid() - || args.world.get_block_state(&bed_foot_pos.up()).is_solid() - { - args.player - .send_system_message_raw( - &pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_BED_OBSTRUCTED, - translation::bedrock::TILE_BED_OBSTRUCTED - ), - true, - ) - .await; - return BlockActionResult::SuccessServer; - } - - // Make sure the bed is not occupied - if bed_props.occupied { - // TODO: Wake up villager - - args.player - .send_system_message_raw( - &pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_BED_OCCUPIED, - translation::bedrock::TILE_BED_OCCUPIED - ), - true, - ) - .await; - return BlockActionResult::SuccessServer; - } - - // Make sure player is close enough - if !args - .player - .position() - .is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0) - && !args - .player - .position() - .is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0) - { - args.player - .send_system_message_raw( - &pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_BED_TOO_FAR_AWAY, - translation::bedrock::TILE_BED_TOOFAR - ), - true, - ) - .await; - return BlockActionResult::SuccessServer; - } - - // Set respawn point - if args - .player - .set_respawn_point( - args.world.dimension.clone(), - bed_head_pos, - args.player.get_entity().yaw.load(), - args.player.get_entity().pitch.load(), - false, - ) - .await - { - args.player - .send_system_message(&pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_SET_SPAWN, - translation::bedrock::TILE_BED_RESPAWNSET - )) - .await; - } - - // Make sure the time and weather allows sleep - if !can_sleep(args.world).await { - args.player - .send_system_message_raw( - &pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_BED_NO_SLEEP, - translation::bedrock::TILE_BED_NOSLEEP - ), - true, - ) - .await; - return BlockActionResult::SuccessServer; - } - - // Make sure there are no monsters nearby - for entity in args.world.entities.load().iter() { - if !entity_prevents_sleep(entity.get_entity()) { - continue; - } - - let pos = entity.get_entity().pos.load(); - if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0) - || pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0) - { - args.player - .send_system_message_raw( - &pumpkin_macros::translate_cross!( - translation::java::BLOCK_MINECRAFT_BED_NOT_SAFE, - translation::bedrock::TILE_BED_NOTSAFE - ), - true, - ) - .await; - return BlockActionResult::SuccessServer; - } - } - - if let Some(server) = args.world.server.upgrade() { - let mut event = - crate::plugin::api::events::player::player_bed::PlayerBedEnterEvent::new( - args.player.clone(), - bed_head_pos, - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - return BlockActionResult::SuccessServer; - } - } - - args.player.sleep(bed_head_pos); - args.player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::SleptInBed, - ) - .await; - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::SleepInBed as i32, - 1, - ) - .await; - Self::set_occupied(true, args.world, args.block, args.position, state_id).await; - - BlockActionResult::SuccessServer - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let player = Arc::clone(args.player); + let world = Arc::clone(args.world); + let block_id = args.block.id; + let position = *args.position; + tokio::spawn(async move { + let block = Block::from_id(block_id); + Self::use_bed(&world, &player, block, &position).await; + }); + BlockActionResult::SuccessServer } } impl BedBlock { - pub async fn set_occupied( + #[expect(clippy::too_many_lines)] + async fn use_bed( + world: &Arc, + player: &Arc, + block: &Block, + position: &BlockPos, + ) -> BlockActionResult { + let state_id = world.get_block_state_id(position); + let bed_props = BedProperties::from_state_id(state_id, block); + + let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head { + ( + *position, + position.offset(bed_props.facing.opposite().to_offset()), + ) + } else { + (position.offset(bed_props.facing.to_offset()), *position) + }; + + // Explode if not in the overworld + if world.dimension != Dimension::OVERWORLD { + world.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS); + world.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS); + + world + .explode( + bed_head_pos.to_centered_f64(), + 5.0, + crate::world::ExplosionInteraction::Block, + ) + .await; + + return BlockActionResult::SuccessServer; + } + + // Make sure the bed is not obstructed + if world.get_block_state(&bed_head_pos.up()).is_solid() + || world.get_block_state(&bed_foot_pos.up()).is_solid() + { + player + .send_system_message_raw( + &pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_BED_OBSTRUCTED, + translation::bedrock::TILE_BED_OBSTRUCTED + ), + true, + ) + .await; + return BlockActionResult::SuccessServer; + } + + // Make sure the bed is not occupied + if bed_props.occupied { + // TODO: Wake up villager + + player + .send_system_message_raw( + &pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_BED_OCCUPIED, + translation::bedrock::TILE_BED_OCCUPIED + ), + true, + ) + .await; + return BlockActionResult::SuccessServer; + } + + // Make sure player is close enough + if !player + .position() + .is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0) + && !player + .position() + .is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0) + { + player + .send_system_message_raw( + &pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_BED_TOO_FAR_AWAY, + translation::bedrock::TILE_BED_TOOFAR + ), + true, + ) + .await; + return BlockActionResult::SuccessServer; + } + + // Set respawn point + if player + .set_respawn_point( + world.dimension.clone(), + bed_head_pos, + player.get_entity().yaw.load(), + player.get_entity().pitch.load(), + false, + ) + .await + { + player + .send_system_message(&pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_SET_SPAWN, + translation::bedrock::TILE_BED_RESPAWNSET + )) + .await; + } + + // Make sure the time and weather allows sleep + if !can_sleep(world) { + player + .send_system_message_raw( + &pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_BED_NO_SLEEP, + translation::bedrock::TILE_BED_NOSLEEP + ), + true, + ) + .await; + return BlockActionResult::SuccessServer; + } + + // Make sure there are no monsters nearby + for entity in world.entities.load().iter() { + if !entity_prevents_sleep(entity.get_entity()) { + continue; + } + + let pos = entity.get_entity().pos.load(); + if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0) + || pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0) + { + player + .send_system_message_raw( + &pumpkin_macros::translate_cross!( + translation::java::BLOCK_MINECRAFT_BED_NOT_SAFE, + translation::bedrock::TILE_BED_NOTSAFE + ), + true, + ) + .await; + return BlockActionResult::SuccessServer; + } + } + + if let Some(server) = world.server.upgrade() { + let mut event = + crate::plugin::api::events::player::player_bed::PlayerBedEnterEvent::new( + player.clone(), + bed_head_pos, + ); + server.plugin_manager.fire(&server, &mut event).await; + if event.cancelled { + return BlockActionResult::SuccessServer; + } + } + + player.sleep(bed_head_pos); + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::SleptInBed, + ); + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::SleepInBed as i32, + 1, + ); + Self::set_occupied(true, world, block, position, state_id); + + BlockActionResult::SuccessServer + } +} + +impl BedBlock { + pub fn set_occupied( occupied: bool, world: &Arc, block: &Block, @@ -407,13 +389,11 @@ impl BedBlock { ) { let mut bed_props = BedProperties::from_state_id(state_id, block); bed_props.occupied = occupied; - world - .set_block_state( - block_pos, - bed_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + block_pos, + bed_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); let other_half_pos = if bed_props.part == BedPart::Head { block_pos.offset(bed_props.facing.opposite().to_offset()) @@ -425,19 +405,23 @@ impl BedBlock { } else { BedPart::Head }; - world - .set_block_state( - &other_half_pos, - bed_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + &other_half_pos, + bed_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); } } -async fn can_sleep(world: &Arc) -> bool { - let time = world.level_time.lock().await; - let weather = world.weather.lock().await; +fn can_sleep(world: &Arc) -> bool { + let time = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let weather = world + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if weather.thundering { true diff --git a/crates/pumpkin/src/block/blocks/beehive.rs b/crates/pumpkin/src/block/blocks/beehive.rs index 4842ca29a..263025b22 100644 --- a/crates/pumpkin/src/block/blocks/beehive.rs +++ b/crates/pumpkin/src/block/blocks/beehive.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, GetComparatorOutputArgs}; +use crate::block::{BlockBehaviour, BlockMetadata, GetComparatorOutputArgs}; use pumpkin_data::BlockId; use pumpkin_data::block_properties::{BeeNestLikeProperties, BlockProperties}; @@ -11,14 +11,11 @@ impl BlockMetadata for BeehiveBlock { } impl BlockBehaviour for BeehiveBlock { - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + { let state_id = args.world.get_block_state_id(args.position); let props = BeeNestLikeProperties::from_state_id(state_id, args.block); Some(props.honey_level) - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/blast_furnace.rs b/crates/pumpkin/src/block/blocks/blast_furnace.rs index b7f714811..2f5a54e5e 100644 --- a/crates/pumpkin/src/block/blocks/blast_furnace.rs +++ b/crates/pumpkin/src/block/blocks/blast_furnace.rs @@ -21,8 +21,8 @@ use tokio::sync::Mutex; use crate::{ block::{ - BlockBehaviour, BlockFuture, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, - OnPlaceArgs, PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -83,83 +83,74 @@ impl ScreenHandlerFactory for BlastingFurnaceScreenFactory { pub struct BlastFurnaceBlock; impl BlockBehaviour for BlastFurnaceBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithBlastFurnace as i32, - 1, - ) - .await; - let blasting_furnace_screen_factory = BlastingFurnaceScreenFactory::new( - inventory, - property_delegate, - experience_container, - ); - args.player - .open_handled_screen(&blasting_furnace_screen_factory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.clone().get_inventory() + && let Some(property_delegate) = block_entity.clone().to_property_delegate() + && let Some(experience_container) = block_entity.to_experience_container() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithBlastFurnace as i32, + 1, + ); + let blasting_furnace_screen_factory = BlastingFurnaceScreenFactory::new( + inventory, + property_delegate, + experience_container, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&blasting_furnace_screen_factory, Some(pos)) .await; + }); + } + crate::block::registry::BlockActionResult::Consume + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = FurnaceLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + + props.to_state_id(args.block) + } + + fn placed(&self, args: PlacedArgs<'_>) { + let blasting_furnace_block_entity = BlastingFurnaceBlockEntity::new(*args.position); + args.world + .add_block_entity(Arc::new(blasting_furnace_block_entity)); + } + + fn broken(&self, args: BrokenArgs<'_>) { + // Extract and drop accumulated XP as orbs before removing the block entity + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(experience_container) = block_entity.to_experience_container() + { + let xp = experience_container.extract_experience(); + if xp > 0 { + let pos = args.position.to_f64(); + ExperienceOrbEntity::spawn(args.world, pos, xp as u32); } - crate::block::registry::BlockActionResult::Consume - }) + } + args.world.remove_block_entity(args.position); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = FurnaceLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - - props.to_state_id(args.block) - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let blasting_furnace_block_entity = BlastingFurnaceBlockEntity::new(*args.position); - args.world - .add_block_entity(Arc::new(blasting_furnace_block_entity)); - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Extract and drop accumulated XP as orbs before removing the block entity - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(experience_container) = block_entity.to_experience_container() - { - let xp = experience_container.extract_experience(); - if xp > 0 { - let pos = args.position.to_f64(); - ExperienceOrbEntity::spawn(args.world, pos, xp as u32).await; - } - } - args.world.remove_block_entity(args.position); - }) - } - - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/brewing_stand.rs b/crates/pumpkin/src/block/blocks/brewing_stand.rs index e633167f1..73c347d02 100644 --- a/crates/pumpkin/src/block/blocks/brewing_stand.rs +++ b/crates/pumpkin/src/block/blocks/brewing_stand.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::{BlockFuture, GetComparatorOutputArgs, PlacedArgs}; +use crate::block::{GetComparatorOutputArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -49,55 +49,48 @@ impl ScreenHandlerFactory for BrewingScreenFactory { pub struct BrewingStandBlock; impl BlockBehaviour for BrewingStandBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(pd) = block_entity.clone().to_property_delegate() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithBrewingstand as i32, - 1, - ) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.clone().get_inventory() + && let Some(pd) = block_entity.clone().to_property_delegate() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithBrewingstand as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&BrewingScreenFactory(inventory, pd), Some(pos)) .await; - args.player - .open_handled_screen(&BrewingScreenFactory(inventory, pd), Some(*args.position)) - .await; - } + }); + } - BlockActionResult::Success - }) + BlockActionResult::Success } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let be = BrewingStandBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(be)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let be = BrewingStandBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(be)); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - let mut bottles = 0u8; - // Bottle slots are 0, 1, 2 in brewing stands - for slot in 0..3 { - let stack = inventory.get_stack(slot).await; - if !stack.is_empty() { - bottles += 1; - } + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + let mut bottles = 0u8; + // Bottle slots are 0, 1, 2 in brewing stands + for slot in 0..3 { + let stack = futures::executor::block_on(inventory.get_stack(slot)); + if !stack.is_empty() { + bottles += 1; } - Some(bottles) - } else { - None } - }) + Some(bottles) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/brushable_block.rs b/crates/pumpkin/src/block/blocks/brushable_block.rs index dd22f333d..a53b316d7 100644 --- a/crates/pumpkin/src/block/blocks/brushable_block.rs +++ b/crates/pumpkin/src/block/blocks/brushable_block.rs @@ -6,9 +6,7 @@ use pumpkin_data::{Block, BlockId, BlockStateId}; use pumpkin_world::world::BlockFlags; use crate::block::entities::brushable_block::BrushableBlockBlockEntity; -use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, OnPlaceArgs, PlacedArgs, -}; +use crate::block::{BlockBehaviour, BlockMetadata, BrokenArgs, OnPlaceArgs, PlacedArgs}; pub struct BrushableBlock; @@ -38,7 +36,7 @@ impl BrushableBlock { if *hits >= 4 { let item = brush_be.item.lock().await.take(); if let Some(item_stack) = item { - world.drop_stack(pos, item_stack).await; + world.drop_stack(pos, item_stack); } let target_block = if is_gravel { @@ -47,9 +45,7 @@ impl BrushableBlock { &Block::SAND }; - world - .set_block_state(pos, target_block.default_state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, target_block.default_state.id, BlockFlags::NOTIFY_ALL); let sound = if is_gravel { Sound::ItemBrushBrushingGravelComplete @@ -60,9 +56,7 @@ impl BrushableBlock { world.play_sound(sound, SoundCategory::Blocks, &pos.to_f64()); } else { props.dusted = (*hits as u8).min(3); - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); let sound = if is_gravel { Sound::ItemBrushBrushingGravel @@ -77,28 +71,22 @@ impl BrushableBlock { } impl BlockBehaviour for BrushableBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = SuspiciousSandLikeProperties::default(args.block); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let props = SuspiciousSandLikeProperties::default(args.block); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = BrushableBlockBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let entity = BrushableBlockBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(be) = args.world.get_block_entity(args.position) - && let Some(brush_be) = be.as_any().downcast_ref::() - && let Some(contained) = brush_be.item.lock().await.take() - { - args.world.drop_stack(args.position, contained).await; - } - }) + fn broken(&self, args: BrokenArgs<'_>) { + if let Some(be) = args.world.get_block_entity(args.position) + && let Some(brush_be) = be.as_any().downcast_ref::() + && let Some(contained) = brush_be.item.blocking_lock().take() + { + args.world.drop_stack(args.position, contained); + } } } diff --git a/crates/pumpkin/src/block/blocks/bubble_column.rs b/crates/pumpkin/src/block/blocks/bubble_column.rs index f021539c8..1d529b761 100644 --- a/crates/pumpkin/src/block/blocks/bubble_column.rs +++ b/crates/pumpkin/src/block/blocks/bubble_column.rs @@ -10,7 +10,7 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, OnEntityCollisionArgs, OnNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, OnEntityCollisionArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::world::World; @@ -161,8 +161,8 @@ fn bubble_column_velocity( } impl BlockBehaviour for BubbleColumnBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + { if args.block != &Block::BUBBLE_COLUMN { return; } @@ -179,19 +179,19 @@ impl BlockBehaviour for BubbleColumnBlock { if let Some(player) = args.entity.get_player() { player.breath_manager.reset(player); } - }) + } } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { if args.block == &Block::WATER && is_source_water(args.world, *args.position) { schedule_reconcile(args.world, *args.position, CREATE_DELAY_TICKS); } - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state = args.world.get_block_state_id(args.position); if args.block == &Block::BUBBLE_COLUMN { schedule_reconcile(args.world, *args.position, REMOVE_DELAY_TICKS); @@ -202,42 +202,37 @@ impl BlockBehaviour for BubbleColumnBlock { { schedule_reconcile(args.world, *args.position, CREATE_DELAY_TICKS); } - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state_id(args.position); - let block = Block::from_state_id(state); - if block != &Block::BUBBLE_COLUMN && block != &Block::WATER { - return; - } + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state_id(args.position); + let block = Block::from_state_id(state); + if block != &Block::BUBBLE_COLUMN && block != &Block::WATER { + return; + } - let below_pos = args.position.down(); - let below_state = args.world.get_block_state_id(&below_pos); - let below_block = Block::from_state_id(below_state); + let below_pos = args.position.down(); + let below_state = args.world.get_block_state_id(&below_pos); + let below_block = Block::from_state_id(below_state); - match reconcile_action(block, state, below_block, below_state) { - ReconcileAction::SetBubble(kind) => { - let new_state = bubble_column_state(kind); - args.world - .set_block_state(args.position, new_state, BlockFlags::NOTIFY_ALL) - .await; - schedule_reconcile(args.world, args.position.up(), CREATE_DELAY_TICKS); - } - ReconcileAction::RestoreWater => { - args.world - .set_block_state( - args.position, - source_water_state(), - BlockFlags::NOTIFY_ALL, - ) - .await; - schedule_reconcile(args.world, args.position.up(), REMOVE_DELAY_TICKS); - } - ReconcileAction::Stop => {} + match reconcile_action(block, state, below_block, below_state) { + ReconcileAction::SetBubble(kind) => { + let new_state = bubble_column_state(kind); + args.world + .set_block_state(args.position, new_state, BlockFlags::NOTIFY_ALL); + schedule_reconcile(args.world, args.position.up(), CREATE_DELAY_TICKS); } - }) + ReconcileAction::RestoreWater => { + args.world.set_block_state( + args.position, + source_water_state(), + BlockFlags::NOTIFY_ALL, + ); + schedule_reconcile(args.world, args.position.up(), REMOVE_DELAY_TICKS); + } + ReconcileAction::Stop => {} + } } } diff --git a/crates/pumpkin/src/block/blocks/cake.rs b/crates/pumpkin/src/block/blocks/cake.rs index a404809dd..3697513fc 100644 --- a/crates/pumpkin/src/block/blocks/cake.rs +++ b/crates/pumpkin/src/block/blocks/cake.rs @@ -2,9 +2,9 @@ use std::sync::Arc; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetComparatorOutputArgs, - GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, - UseWithItemArgs, blocks::candle_cakes::cake_from_candle, registry::BlockActionResult, + BlockBehaviour, CanPlaceAtArgs, GetComparatorOutputArgs, GetStateForNeighborUpdateArgs, + NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, UseWithItemArgs, + blocks::candle_cakes::cake_from_candle, registry::BlockActionResult, }, entity::player::Player, world::World, @@ -21,13 +21,11 @@ use pumpkin_world::{ tick::TickPriority, world::{BlockAccessor, BlockFlags}, }; -use rand::{RngExt, rng}; - #[pumpkin_block("minecraft:cake")] pub struct CakeBlock; impl CakeBlock { - pub async fn consume_if_hungry( + pub fn consume_if_hungry( world: &Arc, player: &Player, block: &Block, @@ -45,7 +43,7 @@ impl CakeBlock { .hunger_manager .saturation .store(player.hunger_manager.saturation.load() + 0.4); - player.send_health().await; + player.send_health(); } GameMode::Creative | GameMode::Spectator => {} } @@ -53,38 +51,30 @@ impl CakeBlock { let mut properties = CakeLikeProperties::from_state_id(state_id, block); match properties.bites { 0..=5 => { - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32, - 1, - ) - .await; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32, + 1, + ); properties.bites += 1; - world - .set_block_state( - location, - properties.to_state_id(block), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + location, + properties.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume } 6 => { - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32, - 1, - ) - .await; - world - .set_block_state( - location, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32, + 1, + ); + world.set_block_state( + location, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume } _ => BlockActionResult::Pass, @@ -93,113 +83,83 @@ impl CakeBlock { } impl BlockBehaviour for CakeBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - return Block::AIR.default_state.id; - } - Block::CAKE.default_state.id - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if !can_place_at(args.world, args.position) { + return Block::AIR.default_state.id; + } + Block::CAKE.default_state.id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { can_place_at(args.block_accessor, args.position) } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let properties = CakeLikeProperties::from_state_id(state_id, args.block); - let item = args.item_stack.item; - match item.id { - id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => { - if properties.bites != 0 { - return Self::consume_if_hungry( - args.world, - args.player, - args.block, - args.position, - state_id, - ) - .await; - } - - if args.player.gamemode.load() != GameMode::Creative { - args.item_stack.decrement(1); - } - args.world - .set_block_state( - args.position, - cake_from_candle(item).default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - let seed: f64 = rng().random(); - args.player - .play_sound( - Sound::BlockCakeAddCandle as u16, - SoundCategory::Blocks, - &args.position.to_f64(), - 1.0, - 1.0, - seed, - ) - .await; - BlockActionResult::Consume - } - _ => { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + let properties = CakeLikeProperties::from_state_id(state_id, args.block); + let item = args.item_stack.item; + match item.id { + id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => { + if properties.bites != 0 { return Self::consume_if_hungry( args.world, args.player, args.block, args.position, state_id, - ) - .await; + ); } + + if args.player.gamemode.load() != GameMode::Creative { + args.item_stack.decrement(1); + } + args.world.set_block_state( + args.position, + cake_from_candle(item).default_state.id, + BlockFlags::NOTIFY_ALL, + ); + args.world.play_sound( + Sound::BlockCakeAddCandle, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + BlockActionResult::Consume } - }) + _ => Self::consume_if_hungry( + args.world, + args.player, + args.block, + args.position, + state_id, + ), + } } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id) - .await - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + { let state_id = args.world.get_block_state_id(args.position); let properties = CakeLikeProperties::from_state_id(state_id, args.block); if properties.bites <= 6 { @@ -207,7 +167,7 @@ impl BlockBehaviour for CakeBlock { } else { Some(0) } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/campfire.rs b/crates/pumpkin/src/block/blocks/campfire.rs index dfac7557c..8e0099485 100644 --- a/crates/pumpkin/src/block/blocks/campfire.rs +++ b/crates/pumpkin/src/block/blocks/campfire.rs @@ -12,8 +12,8 @@ use pumpkin_world::tick::TickPriority; use crate::block::entities::campfire::CampfireBlockEntity; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockIsReplacing, GetStateForNeighborUpdateArgs, - OnEntityCollisionArgs, OnPlaceArgs, PlacedArgs, + BlockBehaviour, BlockIsReplacing, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, + OnPlaceArgs, PlacedArgs, }, entity::EntityBase, }; @@ -23,21 +23,24 @@ use std::sync::Arc; pub struct CampfireBlock; impl BlockBehaviour for CampfireBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = CampfireBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } // TODO: cooking food on campfire (CampfireBlockEntity) - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + { if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit && let Some(living_entity) = args.entity.get_living_entity() { let has_frost_walker_enchantment = { - let equipment = living_entity.entity_equipment.lock().await; + let equipment = living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment .equipment .get(&EquipmentSlot::FEET) @@ -47,7 +50,6 @@ impl BlockBehaviour for CampfireBlock { }; let has_fire_res = living_entity .get_effect(&StatusEffect::FIRE_RESISTANCE) - .await .is_some(); if has_frost_walker_enchantment || has_fire_res { //campfire burning doesn't work if entity's boots has frost walker enchantment or entity has fire resistance. source: https://minecraft.wiki/w/Campfire#Damage @@ -59,49 +61,43 @@ impl BlockBehaviour for CampfireBlock { 1.0 }; args.entity - .damage(args.entity, damage_amount, DamageType::CAMPFIRE) - .await; + .damage(args.entity, damage_amount, DamageType::CAMPFIRE); } - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_)); - let mut props = - CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block); - props.waterlogged = is_replacing_water; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_)); + let mut props = + CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.waterlogged = is_replacing_water; + props.signal_fire = is_signal_fire_base_block(args.world.get_block(&args.position.down())); + props.lit = !is_replacing_water; + props.facing = args.player.get_entity().get_horizontal_facing(); + props.to_state_id(args.block) + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block); + if props.waterlogged { + props.lit = false; + args.world.schedule_fluid_tick( + &Fluid::WATER, + *args.position, + Fluid::WATER.flow_speed as u8, + TickPriority::Normal, + ); + } + + if args.direction == BlockDirection::Down { props.signal_fire = - is_signal_fire_base_block(args.world.get_block(&args.position.down())); - props.lit = !is_replacing_water; - props.facing = args.player.get_entity().get_horizontal_facing(); - props.to_state_id(args.block) - }) - } + is_signal_fire_base_block(args.world.get_block(args.neighbor_position)); + } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block); - if props.waterlogged { - props.lit = false; - args.world.schedule_fluid_tick( - &Fluid::WATER, - *args.position, - Fluid::WATER.flow_speed as u8, - TickPriority::Normal, - ); - } - - if args.direction == BlockDirection::Down { - props.signal_fire = - is_signal_fire_base_block(args.world.get_block(args.neighbor_position)); - } - - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } // TODO: onProjectileHit diff --git a/crates/pumpkin/src/block/blocks/candle_cakes.rs b/crates/pumpkin/src/block/blocks/candle_cakes.rs index 7ffc110f3..7424b5a14 100644 --- a/crates/pumpkin/src/block/blocks/candle_cakes.rs +++ b/crates/pumpkin/src/block/blocks/candle_cakes.rs @@ -10,8 +10,8 @@ use pumpkin_world::{ use crate::{ block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, - OnScheduledTickArgs, UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult, + BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnScheduledTickArgs, + UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult, }, entity::player::Player, world::World, @@ -55,7 +55,7 @@ pub fn candle_from_cake(block: &Block) -> &'static Item { pub struct CandleCakeBlock; impl CandleCakeBlock { - async fn consume_and_drop_candle( + fn consume_and_drop_candle( block: &Block, player: &Player, location: &BlockPos, @@ -75,65 +75,51 @@ impl CandleCakeBlock { let item_stack = ItemStack::new(1, candle_item); - world.drop_stack(location, item_stack).await; + world.drop_stack(location, item_stack); - world - .set_block_state( - location, - Block::CAKE.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + location, + Block::CAKE.default_state.id, + BlockFlags::NOTIFY_ALL, + ); let (block, state) = world.get_block_and_state_id(location); - CakeBlock::consume_if_hungry(world, player, block, location, state).await + CakeBlock::consume_if_hungry(world, player, block, location, state) } } impl BlockBehaviour for CandleCakeBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let item_id = args.item_stack.item.id; - match item_id { - id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => { - BlockActionResult::Pass - } // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL - _ => BlockActionResult::PassToDefaultBlockAction, - } - }) + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let item_id = args.item_stack.item.id; + match item_id { + id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => { + BlockActionResult::Pass + } // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL + _ => BlockActionResult::PassToDefaultBlockAction, + } } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + Self::consume_and_drop_candle(args.block, args.player, args.position, args.world) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/candles.rs b/crates/pumpkin/src/block/blocks/candles.rs index 3070662f5..041604280 100644 --- a/crates/pumpkin/src/block/blocks/candles.rs +++ b/crates/pumpkin/src/block/blocks/candles.rs @@ -10,7 +10,7 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockAccessor; use pumpkin_world::world::BlockFlags; -use crate::block::{BlockFuture, GetStateForNeighborUpdateArgs, OnScheduledTickArgs}; +use crate::block::{GetStateForNeighborUpdateArgs, OnScheduledTickArgs}; use crate::{ block::{ BlockIsReplacing, @@ -27,29 +27,24 @@ use crate::{ pub struct CandleBlock; impl BlockBehaviour for CandleBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.player.get_entity().pose.load() != EntityPose::Crouching - && let BlockIsReplacing::Itself(state_id) = args.replacing - { - let mut properties = CandleLikeProperties::from_state_id(state_id, args.block); - if properties.candles < 4 { - properties.candles += 1; - } - return properties.to_state_id(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.player.get_entity().pose.load() != EntityPose::Crouching + && let BlockIsReplacing::Itself(state_id) = args.replacing + { + let mut properties = CandleLikeProperties::from_state_id(state_id, args.block); + if properties.candles < 4 { + properties.candles += 1; } + return properties.to_state_id(args.block); + } - let mut properties = CandleLikeProperties::default(args.block); - properties.waterlogged = args.replacing.water_source(); - properties.to_state_id(args.block) - }) + let mut properties = CandleLikeProperties::default(args.block); + properties.waterlogged = args.replacing.water_source(); + properties.to_state_id(args.block) } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { let state = args.world.get_block_state(args.position); let mut properties = CandleLikeProperties::from_state_id(state.id, args.block); @@ -67,13 +62,11 @@ impl BlockBehaviour for CandleBlock { properties.lit = was_lit; - args.world - .set_block_state( - args.position, - properties.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + properties.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume } @@ -84,22 +77,20 @@ impl BlockBehaviour for CandleBlock { return BlockActionResult::Pass; } - args.world - .set_block_state( - args.position, - properties.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + properties.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume } } - }) + } } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { let state_id = args.world.get_block_state_id(args.position); let mut properties = CandleLikeProperties::from_state_id(state_id, args.block); @@ -107,16 +98,14 @@ impl BlockBehaviour for CandleBlock { properties.lit = false; } - args.world - .set_block_state( - args.position, - properties.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + properties.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume - }) + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -130,27 +119,22 @@ impl BlockBehaviour for CandleBlock { && args.block.id == b.id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/carpet.rs b/crates/pumpkin/src/block/blocks/carpet.rs index 679690115..728f7dc9e 100644 --- a/crates/pumpkin/src/block/blocks/carpet.rs +++ b/crates/pumpkin/src/block/blocks/carpet.rs @@ -1,5 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::is_air; @@ -16,27 +16,22 @@ impl BlockBehaviour for CarpetBlock { can_place_at(args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } @@ -48,27 +43,22 @@ impl BlockBehaviour for MossCarpetBlock { can_place_at(args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } @@ -80,27 +70,22 @@ impl BlockBehaviour for PaleMossCarpetBlock { can_place_at(args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } diff --git a/crates/pumpkin/src/block/blocks/cartography_table.rs b/crates/pumpkin/src/block/blocks/cartography_table.rs index d66378ba2..16397d7b4 100644 --- a/crates/pumpkin/src/block/blocks/cartography_table.rs +++ b/crates/pumpkin/src/block/blocks/cartography_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::cartography_table_screen_handler::CartographyTableScreenHandler; @@ -16,21 +16,21 @@ use tokio::sync::Mutex; pub struct CartographyTableBlock; impl BlockBehaviour for CartographyTableBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithCartographyTable as i32, - 1, - ) - .await; - args.player - .open_handled_screen(&CartographyTableScreenFactory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithCartographyTable as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&CartographyTableScreenFactory, Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/carved_pumpkin.rs b/crates/pumpkin/src/block/blocks/carved_pumpkin.rs index 24f0609fa..6cbc48490 100644 --- a/crates/pumpkin/src/block/blocks/carved_pumpkin.rs +++ b/crates/pumpkin/src/block/blocks/carved_pumpkin.rs @@ -7,7 +7,7 @@ use pumpkin_data::{ use pumpkin_world::world::BlockFlags; use crate::{ - block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs, PlacedArgs}, + block::{BlockBehaviour, BlockMetadata, OnPlaceArgs, PlacedArgs}, entity::{ Entity, passive::{iron_golem::IronGolemEntity, snow_golem::SnowGolemEntity}, @@ -23,35 +23,31 @@ impl BlockMetadata for CarvedPumpkinBlock { } impl BlockBehaviour for CarvedPumpkinBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = WallTorchLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = WallTorchLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + fn placed(&self, args: PlacedArgs<'_>) { // Mojang uses some BlockPattern magic, way too complex tbh - Box::pin(async { + { let down_pos = args.position.down(); let upper = args.world.get_block(&down_pos); let lower = args.world.get_block(&down_pos.down()); if upper == &Block::SNOW_BLOCK && lower == &Block::SNOW_BLOCK { for i in 0..3 { let pos = args.position.down_height(i); - args.world - .set_block_state( - &pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + &pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_LISTENERS, + ); args.world.sync_world_event( WorldEvent::ParticlesDestroyBlock, pos, @@ -64,7 +60,7 @@ impl BlockBehaviour for CarvedPumpkinBlock { &EntityType::SNOW_GOLEM, ); let golem = SnowGolemEntity::new(entity); - args.world.spawn_entity(golem).await; + args.world.spawn_entity(golem); return; } @@ -80,13 +76,11 @@ impl BlockBehaviour for CarvedPumpkinBlock { let pattern = [*args.position, down_pos, down_pos.down(), arm1, arm2]; for p in pattern { - args.world - .set_block_state( - &p, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + &p, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_LISTENERS, + ); args.world.sync_world_event( WorldEvent::ParticlesDestroyBlock, p, @@ -100,11 +94,11 @@ impl BlockBehaviour for CarvedPumpkinBlock { &EntityType::IRON_GOLEM, ); let golem = IronGolemEntity::new(entity); - args.world.spawn_entity(golem).await; + args.world.spawn_entity(golem); return; } } } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/cauldron.rs b/crates/pumpkin/src/block/blocks/cauldron.rs index 1cf237146..e6d26005b 100644 --- a/crates/pumpkin/src/block/blocks/cauldron.rs +++ b/crates/pumpkin/src/block/blocks/cauldron.rs @@ -1,7 +1,7 @@ +use std::sync::Arc; + use crate::block::registry::BlockActionResult; -use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, GetComparatorOutputArgs, UseWithItemArgs, -}; +use crate::block::{BlockBehaviour, BlockMetadata, GetComparatorOutputArgs, UseWithItemArgs}; use pumpkin_data::Block; use pumpkin_data::BlockId; use pumpkin_data::block_properties::{BlockProperties, WaterCauldronLikeProperties}; @@ -24,7 +24,7 @@ impl BlockMetadata for CauldronBlock { } } -async fn fire_cauldron_change( +fn fire_cauldron_change( world: &std::sync::Arc, pos: pumpkin_util::math::position::BlockPos, old_level: i32, @@ -42,210 +42,208 @@ async fn fire_cauldron_change( cancelled: false, }; if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } !event.cancelled } impl BlockBehaviour for CauldronBlock { #[allow(clippy::too_many_lines)] - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let item_id = args.item_stack.item.id; - let block_id = args.block.id; - let gamemode = args.player.gamemode.load(); + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let item_id = args.item_stack.item.id; + let block_id = args.block.id; + let gamemode = args.player.gamemode.load(); - // Filling empty cauldron with buckets - if block_id == BlockId::CAULDRON { - if item_id == Item::WATER_BUCKET.id { - if !fire_cauldron_change( - args.world, - *args.position, - 0, - 3, - crate::plugin::block::cauldron_level_change::CauldronChangeReason::BucketEmpty, - Some(args.player.clone()), - ) - .await - { - return BlockActionResult::Pass; - } - let state_id = Block::WATER_CAULDRON - .from_properties(&[("level", "3")]) - .to_state_id(&Block::WATER_CAULDRON); - args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - args.world.play_sound( - Sound::ItemBucketEmpty, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player + // Filling empty cauldron with buckets + if block_id == BlockId::CAULDRON { + if item_id == Item::WATER_BUCKET.id { + if !fire_cauldron_change( + args.world, + *args.position, + 0, + 3, + crate::plugin::block::cauldron_level_change::CauldronChangeReason::BucketEmpty, + Some(Arc::clone(args.player) as Arc), + ) { + return BlockActionResult::Pass; + } + let state_id = Block::WATER_CAULDRON + .from_properties(&[("level", "3")]) + .to_state_id(&Block::WATER_CAULDRON); + args.world + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL); + args.world.play_sound( + Sound::ItemBucketEmpty, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), args.player.as_ref()) + .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) .await; - return BlockActionResult::Success; - } else if item_id == Item::LAVA_BUCKET.id { - args.world - .set_block_state( - args.position, - Block::LAVA_CAULDRON.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world.play_sound( - Sound::ItemBucketEmptyLava, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player + }); + return BlockActionResult::Success; + } else if item_id == Item::LAVA_BUCKET.id { + args.world.set_block_state( + args.position, + Block::LAVA_CAULDRON.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + args.world.play_sound( + Sound::ItemBucketEmptyLava, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), args.player.as_ref()) + .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) .await; - return BlockActionResult::Success; - } else if item_id == Item::POWDER_SNOW_BUCKET.id { - let state_id = Block::POWDER_SNOW_CAULDRON - .from_properties(&[("level", "3")]) - .to_state_id(&Block::POWDER_SNOW_CAULDRON); - args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - args.world.play_sound( - Sound::ItemBucketEmptyPowderSnow, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player + }); + return BlockActionResult::Success; + } else if item_id == Item::POWDER_SNOW_BUCKET.id { + let state_id = Block::POWDER_SNOW_CAULDRON + .from_properties(&[("level", "3")]) + .to_state_id(&Block::POWDER_SNOW_CAULDRON); + args.world + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL); + args.world.play_sound( + Sound::ItemBucketEmptyPowderSnow, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), args.player.as_ref()) + .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) .await; - return BlockActionResult::Success; - } else if item_id == Item::POTION.id { - let state_id = Block::WATER_CAULDRON - .from_properties(&[("level", "1")]) - .to_state_id(&Block::WATER_CAULDRON); - args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - args.world.play_sound( - Sound::ItemBottleEmpty, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player + }); + return BlockActionResult::Success; + } else if item_id == Item::POTION.id { + let state_id = Block::WATER_CAULDRON + .from_properties(&[("level", "1")]) + .to_state_id(&Block::WATER_CAULDRON); + args.world + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL); + args.world.play_sound( + Sound::ItemBottleEmpty, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .inventory .offer_or_drop_stack( ItemStack::new(1, &Item::GLASS_BOTTLE), - args.player.as_ref(), + player.as_ref(), ) .await; - return BlockActionResult::Success; - } + }); + return BlockActionResult::Success; } + } - // Collecting fluid from full cauldrons into empty bucket - if item_id == Item::BUCKET.id { - let state_id = args.world.get_block_state_id(args.position); - let (filled_item, sound) = if block_id == BlockId::WATER_CAULDRON { - let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); - if props.level == 3 { - (Some(&Item::WATER_BUCKET), Sound::ItemBucketFill) - } else { - (None, Sound::ItemBucketFill) - } - } else if block_id == BlockId::LAVA_CAULDRON { - (Some(&Item::LAVA_BUCKET), Sound::ItemBucketFillLava) - } else if block_id == BlockId::POWDER_SNOW_CAULDRON { - let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); - if props.level == 3 { - ( - Some(&Item::POWDER_SNOW_BUCKET), - Sound::ItemBucketFillPowderSnow, - ) - } else { - (None, Sound::ItemBucketFillPowderSnow) - } + // Collecting fluid from full cauldrons into empty bucket + if item_id == Item::BUCKET.id { + let state_id = args.world.get_block_state_id(args.position); + let (filled_item, sound) = if block_id == BlockId::WATER_CAULDRON { + let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); + if props.level == 3 { + (Some(&Item::WATER_BUCKET), Sound::ItemBucketFill) } else { (None, Sound::ItemBucketFill) - }; - - if let Some(result_item) = filled_item { - args.world - .set_block_state( - args.position, - Block::CAULDRON.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world - .play_sound(sound, SoundCategory::Blocks, &args.position.to_f64()); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player - .inventory - .offer_or_drop_stack(ItemStack::new(1, result_item), args.player.as_ref()) - .await; - return BlockActionResult::Success; } - } - - // Adding water bottle to non-full water cauldron - if block_id == BlockId::WATER_CAULDRON && item_id == Item::POTION.id { - let state_id = args.world.get_block_state_id(args.position); + } else if block_id == BlockId::LAVA_CAULDRON { + (Some(&Item::LAVA_BUCKET), Sound::ItemBucketFillLava) + } else if block_id == BlockId::POWDER_SNOW_CAULDRON { let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); - if props.level < 3 { - let next_level_str = match props.level { - 1 => "2", - _ => "3", - }; - let new_state_id = Block::WATER_CAULDRON - .from_properties(&[("level", next_level_str)]) - .to_state_id(&Block::WATER_CAULDRON); - args.world - .set_block_state(args.position, new_state_id, BlockFlags::NOTIFY_ALL) + if props.level == 3 { + ( + Some(&Item::POWDER_SNOW_BUCKET), + Sound::ItemBucketFillPowderSnow, + ) + } else { + (None, Sound::ItemBucketFillPowderSnow) + } + } else { + (None, Sound::ItemBucketFill) + }; + + if let Some(result_item) = filled_item { + args.world.set_block_state( + args.position, + Block::CAULDRON.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + args.world + .play_sound(sound, SoundCategory::Blocks, &args.position.to_f64()); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player + .inventory + .offer_or_drop_stack(ItemStack::new(1, result_item), player.as_ref()) .await; - args.world.play_sound( - Sound::ItemBottleEmpty, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.item_stack.decrement_unless_creative(gamemode, 1); - args.player + }); + return BlockActionResult::Success; + } + } + + // Adding water bottle to non-full water cauldron + if block_id == BlockId::WATER_CAULDRON && item_id == Item::POTION.id { + let state_id = args.world.get_block_state_id(args.position); + let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); + if props.level < 3 { + let next_level_str = match props.level { + 1 => "2", + _ => "3", + }; + let new_state_id = Block::WATER_CAULDRON + .from_properties(&[("level", next_level_str)]) + .to_state_id(&Block::WATER_CAULDRON); + args.world + .set_block_state(args.position, new_state_id, BlockFlags::NOTIFY_ALL); + args.world.play_sound( + Sound::ItemBottleEmpty, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.item_stack.decrement_unless_creative(gamemode, 1); + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .inventory .offer_or_drop_stack( ItemStack::new(1, &Item::GLASS_BOTTLE), - args.player.as_ref(), + player.as_ref(), ) .await; - return BlockActionResult::Success; - } + }); + return BlockActionResult::Success; } + } - BlockActionResult::PassToDefaultBlockAction - }) + BlockActionResult::PassToDefaultBlockAction } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - match args.block.id { - BlockId::WATER_CAULDRON | BlockId::POWDER_SNOW_CAULDRON => { - let state_id = args.world.get_block_state_id(args.position); - let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); - Some(props.level) - } - BlockId::LAVA_CAULDRON => Some(3), - _ => Some(0), + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + match args.block.id { + BlockId::WATER_CAULDRON | BlockId::POWDER_SNOW_CAULDRON => { + let state_id = args.world.get_block_state_id(args.position); + let props = WaterCauldronLikeProperties::from_state_id(state_id, args.block); + Some(props.level) } - }) + BlockId::LAVA_CAULDRON => Some(3), + _ => Some(0), + } } } diff --git a/crates/pumpkin/src/block/blocks/chain.rs b/crates/pumpkin/src/block/blocks/chain.rs index f9680a686..aa9de361b 100644 --- a/crates/pumpkin/src/block/blocks/chain.rs +++ b/crates/pumpkin/src/block/blocks/chain.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::{BlockBehaviour, OnPlaceArgs}; use pumpkin_data::BlockDirection; use pumpkin_data::BlockStateId; @@ -10,18 +9,16 @@ use pumpkin_macros::pumpkin_block; pub struct ChainBlock; impl BlockBehaviour for ChainBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - pumpkin_data::block_properties::IronChainLikeProperties::default(args.block); - props.r#waterlogged = args.replacing.water_source(); - props.r#axis = match args.direction { - BlockDirection::East | BlockDirection::West => Axis::X, - BlockDirection::Up | BlockDirection::Down => Axis::Y, - BlockDirection::North | BlockDirection::South => Axis::Z, - }; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + pumpkin_data::block_properties::IronChainLikeProperties::default(args.block); + props.r#waterlogged = args.replacing.water_source(); + props.r#axis = match args.direction { + BlockDirection::East | BlockDirection::West => Axis::X, + BlockDirection::Up | BlockDirection::Down => Axis::Y, + BlockDirection::North | BlockDirection::South => Axis::Z, + }; - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/chests.rs b/crates/pumpkin/src/block/blocks/chests.rs index 891ffdf36..60bf81313 100644 --- a/crates/pumpkin/src/block/blocks/chests.rs +++ b/crates/pumpkin/src/block/blocks/chests.rs @@ -24,7 +24,7 @@ use pumpkin_world::world::BlockFlags; use tokio::sync::Mutex; use crate::block::{ - BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, + BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, PlayerPlacedArgs, RandomTickArgs, }; @@ -93,8 +93,8 @@ fn on_place_chest_impl(args: &OnPlaceArgs<'_>) -> BlockStateId { chest_props.to_state_id(args.block) } -async fn placed_chest_impl( - args: PlacedArgs<'_>, +fn placed_chest_impl( + args: &PlacedArgs<'_>, create_entity: impl FnOnce(BlockPos) -> E, ) { let chest = create_entity(*args.position); @@ -117,13 +117,11 @@ async fn placed_chest_impl( ) { neighbor_props.r#type = chest_props.r#type.opposite(); - args.world - .set_block_state( - &args.position.offset(connected_towards.to_offset()), - neighbor_props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + &args.position.offset(connected_towards.to_offset()), + neighbor_props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); } } @@ -140,7 +138,7 @@ fn player_placed_chest_impl(args: &PlayerPlacedArgs<'_>) { ); } -async fn get_chest_comparator_output(args: GetComparatorOutputArgs<'_>) -> Option { +fn get_chest_comparator_output(args: &GetComparatorOutputArgs<'_>) -> Option { let state = args.world.get_block_state_id(args.position); let first_chest = args.world.get_block_entity(args.position); let first_inventory = first_chest.and_then(BlockEntity::get_inventory)?; @@ -163,20 +161,22 @@ async fn get_chest_comparator_output(args: GetComparatorOutputArgs<'_>) -> Optio } else { DoubleInventory::new(second_inventory, first_inventory) }; - Some(crate::block::calculate_comparator_output(double_inventory.as_ref()).await) + Some(crate::block::calculate_comparator_output( + double_inventory.as_ref(), + )) } else { - Some(crate::block::calculate_comparator_output(first_inventory.as_ref()).await) + Some(crate::block::calculate_comparator_output( + first_inventory.as_ref(), + )) } } -async fn normal_use_chest_impl(args: NormalUseArgs<'_>) -> BlockActionResult { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::OpenChest as i32, - 1, - ) - .await; +fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::OpenChest as i32, + 1, + ); let state = args.world.get_block_state_id(args.position); let first_chest = args.world.get_block_entity(args.position); @@ -196,9 +196,10 @@ async fn normal_use_chest_impl(args: NormalUseArgs<'_>) -> BlockActionResult { && let Some(table) = get_chest_loot_table(&loot_key) && let Some(inv) = entity.clone().get_inventory() { - fill_chest_inventory(&inv, table, seed).await; - // Mark the block entity dirty so the generated items persist. - inv.mark_dirty(); + tokio::spawn(async move { + fill_chest_inventory(&inv, table, seed).await; + inv.mark_dirty(); + }); } let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else { @@ -239,14 +240,18 @@ async fn normal_use_chest_impl(args: NormalUseArgs<'_>) -> BlockActionResult { first_inventory }; - args.player - .open_handled_screen(&ChestScreenFactory(inventory), Some(*args.position)) - .await; + let player = args.player.clone(); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&ChestScreenFactory(inventory), Some(pos)) + .await; + }); BlockActionResult::Success } -async fn broken_chest_impl(args: BrokenArgs<'_>) { +fn broken_chest_impl(args: &BrokenArgs<'_>) { let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block); let connected_towards = match chest_props.r#type { ChestType::Single => return, @@ -264,13 +269,11 @@ async fn broken_chest_impl(args: BrokenArgs<'_>) { ) { neighbor_props.r#type = ChestType::Single; - args.world - .set_block_state( - &args.position.offset(connected_towards.to_offset()), - neighbor_props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + &args.position.offset(connected_towards.to_offset()), + neighbor_props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); } } @@ -278,38 +281,32 @@ async fn broken_chest_impl(args: BrokenArgs<'_>) { pub struct ChestBlock; impl BlockBehaviour for ChestBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { on_place_chest_impl(&args) }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + on_place_chest_impl(&args) } - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { args.r#type == LID_ANIMATION_EVENT_TYPE }) + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + args.r#type == LID_ANIMATION_EVENT_TYPE } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(placed_chest_impl(args, ChestBlockEntity::new)) + fn placed(&self, args: PlacedArgs<'_>) { + placed_chest_impl(&args, ChestBlockEntity::new); } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { player_placed_chest_impl(&args) }) + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + player_placed_chest_impl(&args); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(normal_use_chest_impl(args)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + normal_use_chest_impl(&args) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(broken_chest_impl(args)) + fn broken(&self, args: BrokenArgs<'_>) { + broken_chest_impl(&args); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { get_chest_comparator_output(args).await }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + get_chest_comparator_output(&args) } } @@ -352,65 +349,56 @@ impl impl crate::block::blocks::weathering_copper::WeatheringCopper for CopperChestBlock {} impl BlockBehaviour for CopperChestBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { on_place_chest_impl(&args) }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + on_place_chest_impl(&args) } - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { args.r#type == LID_ANIMATION_EVENT_TYPE }) + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + args.r#type == LID_ANIMATION_EVENT_TYPE } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(placed_chest_impl(args, ChestBlockEntity::new)) + fn placed(&self, args: PlacedArgs<'_>) { + placed_chest_impl(&args, ChestBlockEntity::new); } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { player_placed_chest_impl(&args) }) + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + player_placed_chest_impl(&args); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(normal_use_chest_impl(args)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + normal_use_chest_impl(&args) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(broken_chest_impl(args)) + fn broken(&self, args: BrokenArgs<'_>) { + broken_chest_impl(&args); } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let current_state_id = args.world.get_block_state_id(args.position); - let chest_props = ChestLikeProperties::from_state_id(current_state_id, args.block); + fn random_tick(&self, args: RandomTickArgs<'_>) { + let current_state_id = args.world.get_block_state_id(args.position); + let chest_props = ChestLikeProperties::from_state_id(current_state_id, args.block); - // Only oxidize LEFT or SINGLE chests (not RIGHT) to prevent double oxidation - if chest_props.r#type == ChestType::Right { - return; - } + // Only oxidize LEFT or SINGLE chests (not RIGHT) to prevent double oxidation + if chest_props.r#type == ChestType::Right { + return; + } - // Only oxidize if no players are viewing the chest - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(chest_entity) = block_entity.as_any().downcast_ref::() - && chest_entity.get_viewer_count() > 0 - { - return; - } + // Only oxidize if no players are viewing the chest + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(chest_entity) = block_entity.as_any().downcast_ref::() + && chest_entity.get_viewer_count() > 0 + { + return; + } - crate::block::blocks::weathering_copper::change_over_time( - args.world, - args.position, - args.block, - ) - .await; - }) + crate::block::blocks::weathering_copper::change_over_time( + args.world, + args.position, + args.block, + ); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { get_chest_comparator_output(args).await }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + get_chest_comparator_output(&args) } } @@ -419,84 +407,64 @@ impl BlockBehaviour for CopperChestBlock { pub struct TrappedChestBlock; impl BlockBehaviour for TrappedChestBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { on_place_chest_impl(&args) }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + on_place_chest_impl(&args) } - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { args.r#type == LID_ANIMATION_EVENT_TYPE }) + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + args.r#type == LID_ANIMATION_EVENT_TYPE } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + fn placed(&self, args: PlacedArgs<'_>) { use crate::block::entities::trapped_chest::TrappedChestBlockEntity; - Box::pin(placed_chest_impl(args, TrappedChestBlockEntity::new)) + placed_chest_impl(&args, TrappedChestBlockEntity::new); } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { player_placed_chest_impl(&args) }) + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + player_placed_chest_impl(&args); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(normal_use_chest_impl(args)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + normal_use_chest_impl(&args) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(broken_chest_impl(args)) + fn broken(&self, args: BrokenArgs<'_>) { + broken_chest_impl(&args); } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - use crate::block::entities::trapped_chest::TrappedChestBlockEntity; + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + use crate::block::entities::trapped_chest::TrappedChestBlockEntity; - // Get viewer count from this chest - let viewer_count = if let Some(block_entity) = - args.world.get_block_entity(args.position) - && let Some(trapped_chest) = block_entity - .as_any() - .downcast_ref::() - { - trapped_chest.get_viewer_count() - } else { - 0 - }; + // Get viewer count from this chest + let viewer_count = if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(trapped_chest) = block_entity + .as_any() + .downcast_ref::() + { + trapped_chest.get_viewer_count() + } else { + 0 + }; - viewer_count.min(15) as u8 - }) + viewer_count.min(15) as u8 } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - // Strong power emitted to the block beneath the trapped chest - // The block below queries with direction Up (from below looking up at the chest) - if args.direction == BlockDirection::Up { - self.get_weak_redstone_power(args).await - } else { - 0 - } - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + // Strong power emitted to the block beneath the trapped chest + // The block below queries with direction Up (from below looking up at the chest) + if args.direction == BlockDirection::Up { + self.get_weak_redstone_power(args) + } else { + 0 + } } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { get_chest_comparator_output(args).await }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + get_chest_comparator_output(&args) } } diff --git a/crates/pumpkin/src/block/blocks/chiseled_bookshelf.rs b/crates/pumpkin/src/block/blocks/chiseled_bookshelf.rs index 491635955..4b8003d7e 100644 --- a/crates/pumpkin/src/block/blocks/chiseled_bookshelf.rs +++ b/crates/pumpkin/src/block/blocks/chiseled_bookshelf.rs @@ -5,8 +5,8 @@ use pumpkin_macros::pumpkin_block; use crate::block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, - OnPlaceArgs, PlacedArgs, UseWithItemArgs, registry::BlockActionResult, + BlockBehaviour, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + PlacedArgs, UseWithItemArgs, registry::BlockActionResult, }, entity::{EntityBase, player::Player}, world::World, @@ -22,121 +22,102 @@ use pumpkin_data::{ }; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; -use pumpkin_world::inventory::Inventory; #[pumpkin_block("minecraft:chiseled_bookshelf")] pub struct ChiseledBookshelfBlock; impl BlockBehaviour for ChiseledBookshelfBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut properties = ChiseledBookshelfLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut properties = ChiseledBookshelfLikeProperties::default(args.block); - // Face in the opposite direction the player is facing - properties.facing = args.player.get_entity().get_horizontal_facing().opposite(); + // Face in the opposite direction the player is facing + properties.facing = args.player.get_entity().get_horizontal_facing().opposite(); - properties.to_state_id(args.block) - }) + properties.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); - if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) { - if Self::is_slot_used(properties, slot) { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(block_entity) = block_entity - .as_any() - .downcast_ref::() - { - Self::try_remove_book( - args.world, - args.player, - args.position, - block_entity, - properties, - slot, - ) - .await; - return BlockActionResult::Success; - } - } else { - return BlockActionResult::Consume; - } - } - BlockActionResult::Pass - }) - } - - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); - - if !args - .item_stack - .get_item() - .has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS) - { - return BlockActionResult::PassToDefaultBlockAction; - } - if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) { - if Self::is_slot_used(properties, slot) { - return BlockActionResult::PassToDefaultBlockAction; - } else if let Some(block_entity) = args.world.get_block_entity(args.position) + if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) { + if Self::is_slot_used(properties, slot) { + if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(block_entity) = block_entity .as_any() .downcast_ref::() { - Self::try_add_book( + Self::try_remove_book( args.world, args.player, args.position, block_entity, properties, slot, - args.item_stack, - ) - .await; + ); return BlockActionResult::Success; } + } else { + return BlockActionResult::Consume; } - - BlockActionResult::Pass - }) + } + BlockActionResult::Pass } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_entity = ChiseledBookshelfBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(block_entity)); - }) - } + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) + if !args + .item_stack + .get_item() + .has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS) + { + return BlockActionResult::PassToDefaultBlockAction; + } + if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) { + if Self::is_slot_used(properties, slot) { + return BlockActionResult::PassToDefaultBlockAction; + } else if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(block_entity) = block_entity .as_any() .downcast_ref::() { - return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8); + Self::try_add_book( + args.world, + args.player, + args.position, + block_entity, + properties, + slot, + args.item_stack, + ); + return BlockActionResult::Success; } - None - }) + } + + BlockActionResult::Pass + } + + fn placed(&self, args: PlacedArgs<'_>) { + let block_entity = ChiseledBookshelfBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(block_entity)); + } + + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(block_entity) = block_entity + .as_any() + .downcast_ref::() + { + return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8); + } + None } } impl ChiseledBookshelfBlock { - async fn try_add_book( + fn try_add_book( world: &Arc, player: &Player, position: &BlockPos, @@ -153,28 +134,24 @@ impl ChiseledBookshelfBlock { Sound::BlockChiseledBookshelfPickup }; - entity - .set_stack( - slot as usize, - item.split_unless_creative(player.gamemode.load(), 1), - ) - .await; - entity - .update_state(properties, world.clone(), slot as usize) - .await; + entity.set_book( + slot as usize, + item.split_unless_creative(player.gamemode.load(), 1), + ); + entity.update_state(properties, world, slot as usize); world.play_sound(sound, SoundCategory::Blocks, &position.to_centered_f64()); } - async fn try_remove_book( + fn try_remove_book( world: &Arc, - player: &Player, + player: &Arc, position: &BlockPos, entity: &ChiseledBookshelfBlockEntity, properties: ChiseledBookshelfLikeProperties, slot: i8, ) { - let mut stack = entity.remove_stack_specific(slot as usize, 1).await; + let mut stack = entity.remove_book(slot as usize, 1); let sound = if stack.get_item() == &Item::ENCHANTED_BOOK { Sound::BlockChiseledBookshelfPickupEnchanted @@ -182,17 +159,11 @@ impl ChiseledBookshelfBlock { Sound::BlockChiseledBookshelfPickup }; - if !player - .get_inventory() - .insert_stack_anywhere(&mut stack) - .await - { + if !player.get_inventory().insert_stack_anywhere(&mut stack) { // Drop the item on the ground if the player cannot hold it because of a full inventory - player.drop_item(stack).await; + player.drop_item(stack); } - entity - .update_state(properties, world.clone(), slot as usize) - .await; + entity.update_state(properties, world, slot as usize); world.play_sound(sound, SoundCategory::Blocks, &position.to_centered_f64()); } diff --git a/crates/pumpkin/src/block/blocks/cobweb.rs b/crates/pumpkin/src/block/blocks/cobweb.rs index f49b95074..7a4b2881d 100644 --- a/crates/pumpkin/src/block/blocks/cobweb.rs +++ b/crates/pumpkin/src/block/blocks/cobweb.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, OnEntityCollisionArgs}; +use crate::block::{BlockBehaviour, OnEntityCollisionArgs}; use crate::entity::EntityBase; use pumpkin_data::effect::StatusEffect; use pumpkin_macros::pumpkin_block; @@ -8,17 +8,15 @@ use pumpkin_util::math::vector3::Vector3; pub struct CobwebBlock; impl BlockBehaviour for CobwebBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = args.entity.get_entity(); - let vec = if let Some(living) = entity.get_living_entity() - && living.has_effect(&StatusEffect::WEAVING).await - { - Vector3::new(0.5, 0.25, 0.5) - } else { - Vector3::new(0.25, 0.05, 0.25) - }; - entity.slow_movement(args.state, vec).await; - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let entity = args.entity.get_entity(); + let vec = if let Some(living) = entity.get_living_entity() + && living.has_effect(&StatusEffect::WEAVING) + { + Vector3::new(0.5, 0.25, 0.5) + } else { + Vector3::new(0.25, 0.05, 0.25) + }; + entity.slow_movement(args.state, vec); } } diff --git a/crates/pumpkin/src/block/blocks/command.rs b/crates/pumpkin/src/block/blocks/command.rs index 926175541..7ceba2313 100644 --- a/crates/pumpkin/src/block/blocks/command.rs +++ b/crates/pumpkin/src/block/blocks/command.rs @@ -6,9 +6,8 @@ use crate::command::CommandSender; use crate::entity::EntityBase; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, NormalUseArgs, - OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, - registry::BlockActionResult, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, NormalUseArgs, OnNeighborUpdateArgs, + OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, registry::BlockActionResult, }, server::Server, world::World, @@ -30,7 +29,7 @@ impl CommandBlock { dir: Facing, ) -> Option<(BlockPos, CommandBlockLikeProperties)> { let target_pos = pos.offset(dir.to_block_direction().to_offset()); - let block = world.get_block(&target_pos); + let (block, state_id) = world.get_block_and_state_id(&target_pos); let allowed_blocks = [ Block::COMMAND_BLOCK.name, @@ -41,7 +40,6 @@ impl CommandBlock { return None; } - let state_id = world.get_block_state_id(&target_pos); let props = CommandBlockLikeProperties::from_state_id(state_id, block); Some((target_pos, props)) @@ -161,7 +159,7 @@ impl CommandBlock { if !command_blocks_work { return; } - let block = world.get_block(&pos); + let (block, state_id) = world.get_block_and_state_id(&pos); if block.id != Block::CHAIN_COMMAND_BLOCK.id { break; @@ -178,13 +176,16 @@ impl CommandBlock { }; let powered = command_entity.powered.load(Ordering::Relaxed); let auto = command_entity.auto.load(Ordering::Relaxed); - let state_id = world.get_block_state_id(&pos); let props = CommandBlockLikeProperties::from_state_id(state_id, block); if powered || auto { let conditions_met = Self::conditions_met(&world, &pos, direction); if conditions_met { - let command = command_entity.command.lock().await; + let command = command_entity + .command + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let Some(entity) = world.get_block_entity(&pos) else { warn!("Command block entity disappeared during execution"); break; @@ -220,16 +221,14 @@ impl BlockMetadata for CommandBlock { } impl BlockBehaviour for CommandBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CommandBlockLikeProperties::default(args.block); - props.facing = args.player.get_entity().get_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = CommandBlockLikeProperties::default(args.block); + props.facing = args.player.get_entity().get_facing().opposite(); + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { if args.player.permission_lvl.load() < PermissionLvl::Two { return BlockActionResult::Pass; } @@ -238,11 +237,11 @@ impl BlockBehaviour for CommandBlock { }; args.world.update_block_entity(&block_entity); BlockActionResult::SuccessServer - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let command_blocks_work = { args.world.level_info.load().game_rules.command_blocks_work }; if !command_blocks_work { @@ -264,64 +263,68 @@ impl BlockBehaviour for CommandBlock { args.block, command_entity, args.position, - block_receives_redstone_power(args.world, args.position).await, + block_receives_redstone_power(args.world, args.position), ); } - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let command_blocks_work = - { args.world.level_info.load().game_rules.command_blocks_work }; - if !command_blocks_work { - return; - } - let Some(block_entity) = args.world.get_block_entity(args.position) else { - return; - }; - if block_entity.resource_location() != CommandBlockEntity::ID { - return; - } + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let command_blocks_work = { args.world.level_info.load().game_rules.command_blocks_work }; + if !command_blocks_work { + return; + } + let Some(block_entity) = args.world.get_block_entity(args.position) else { + return; + }; + if block_entity.resource_location() != CommandBlockEntity::ID { + return; + } - let Some(command_entity) = block_entity.as_any().downcast_ref::() + let Some(command_entity) = block_entity.as_any().downcast_ref::() + else { + warn!("Block entity at {} is not a command block", args.position); + return; + }; + let Some(server) = args.world.server.upgrade() else { + return; + }; + let props = CommandBlockLikeProperties::from_state_id( + args.world.get_block_state_id(args.position), + args.block, + ); + + let world = args.world.clone(); + let entity_clone = block_entity.clone(); + let position = *args.position; + let facing = props.facing; + tokio::spawn(async move { + let Some(command_entity) = entity_clone.as_any().downcast_ref::() else { - warn!("Block entity at {} is not a command block", args.position); return; }; - let Some(server) = args.world.server.upgrade() else { - return; - }; - let props = CommandBlockLikeProperties::from_state_id( - args.world.get_block_state_id(args.position), - args.block, - ); - - Self::execute( - &server, - args.world.clone(), - block_entity.clone(), - &command_entity.command.lock().await, - ) - .await; - + let command = command_entity + .command + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + Self::execute(&server, world.clone(), entity_clone, &command).await; Self::chain_execute( &server, - args.world.clone(), - args.position - .offset(props.facing.to_block_direction().to_offset()), - props.facing, + world, + position.offset(facing.to_block_direction().to_offset()), + facing, ) .await; + }); - let block = args.world.get_block(args.position); - let is_auto = command_entity.auto.load(Ordering::Relaxed); - let can_run = command_entity.powered.load(Ordering::Relaxed) || is_auto; - if block == &Block::REPEATING_COMMAND_BLOCK && can_run { - args.world - .schedule_block_tick(block, *args.position, 1, TickPriority::Normal); - } - }) + let block = args.world.get_block(args.position); + let is_auto = command_entity.auto.load(Ordering::Relaxed); + let can_run = command_entity.powered.load(Ordering::Relaxed) || is_auto; + if block == &Block::REPEATING_COMMAND_BLOCK && can_run { + args.world + .schedule_block_tick(block, *args.position, 1, TickPriority::Normal); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -334,8 +337,8 @@ impl BlockBehaviour for CommandBlock { false } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let send_command_feedback = { let game_rules = &args.world.level_info.load().game_rules; game_rules.send_command_feedback @@ -347,14 +350,11 @@ impl BlockBehaviour for CommandBlock { args.block.id == Block::CHAIN_COMMAND_BLOCK.id, ); args.world.add_block_entity(Arc::new(entity)); - }) + } } - fn get_comparator_output<'a>( - &'a self, - args: crate::block::GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async { + fn get_comparator_output(&self, args: crate::block::GetComparatorOutputArgs<'_>) -> Option { + { let entity = args.world.get_block_entity(args.position); entity.map_or_else( @@ -368,6 +368,6 @@ impl BlockBehaviour for CommandBlock { command_block_entity.map(|e| e.success_count.load(Ordering::Acquire) as u8) }, ) - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/composter.rs b/crates/pumpkin/src/block/blocks/composter.rs index fd5ba8b41..f86bec53f 100644 --- a/crates/pumpkin/src/block/blocks/composter.rs +++ b/crates/pumpkin/src/block/blocks/composter.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ block::{ - BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs, + BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs, UseWithItemArgs, registry::BlockActionResult, }, entity::{Entity, item::ItemEntity}, @@ -27,32 +27,27 @@ use rand::RngExt; pub struct ComposterBlock; impl BlockBehaviour for ComposterBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { let state_id = args.world.get_block_state_id(args.position); let props = ComposterLikeProperties::from_state_id(state_id, args.block); if props.level == 8 { - self.clear_composter(args.world, args.position, state_id, args.block) - .await; + self.clear_composter(args.world, args.position, state_id, args.block); } BlockActionResult::Pass - }) + } } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { let state_id = args.world.get_block_state_id(args.position); let props = ComposterLikeProperties::from_state_id(state_id, args.block); let level = props.level; // Check if the composter is full if level == 8 { - self.clear_composter(args.world, args.position, state_id, args.block) - .await; + self.clear_composter(args.world, args.position, state_id, args.block); return BlockActionResult::Consume; } @@ -77,48 +72,35 @@ impl BlockBehaviour for ComposterBlock { state_id, args.block, level + 1, - ) - .await; + ); args.world .sync_world_event(WorldEvent::ComposterFill, *args.position, 1); } // Consume the item BlockActionResult::Consume - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let props = ComposterLikeProperties::from_state_id(state_id, args.block); - let level = props.level; - if level == 7 { - self.update_level_composter( - args.world, - args.position, - state_id, - args.block, - level + 1, - ) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let props = ComposterLikeProperties::from_state_id(state_id, args.block); + let level = props.level; + if level == 7 { + self.update_level_composter(args.world, args.position, state_id, args.block, level + 1); + } } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + { let props = ComposterLikeProperties::from_state_id(args.state.id, args.block); Some(props.level) - }) + } } } impl ComposterBlock { - pub async fn update_level_composter( + pub fn update_level_composter( &self, world: &Arc, location: &BlockPos, @@ -128,23 +110,20 @@ impl ComposterBlock { ) { let mut props = ComposterLikeProperties::from_state_id(state_id, block); props.level = level; - world - .set_block_state(location, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(location, props.to_state_id(block), BlockFlags::NOTIFY_ALL); if level == 7 { world.schedule_block_tick(block, *location, 20, TickPriority::Normal); } } - pub async fn clear_composter( + pub fn clear_composter( &self, world: &Arc, location: &BlockPos, state_id: BlockStateId, block: &Block, ) { - self.update_level_composter(world, location, state_id, block, 0) - .await; + self.update_level_composter(world, location, state_id, block, 0); let item_position = { let mut rng = rand::rng(); @@ -160,6 +139,6 @@ impl ComposterBlock { ItemStack::new(1, &Item::BONE_MEAL), ); - world.spawn_entity(Arc::new(item_entity)).await; + world.spawn_entity(Arc::new(item_entity)); } } diff --git a/crates/pumpkin/src/block/blocks/conduit.rs b/crates/pumpkin/src/block/blocks/conduit.rs index 6df0299ce..a571002b9 100644 --- a/crates/pumpkin/src/block/blocks/conduit.rs +++ b/crates/pumpkin/src/block/blocks/conduit.rs @@ -1,5 +1,5 @@ use crate::block::entities::conduit::ConduitBlockEntity; -use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::BlockProperties; use pumpkin_macros::pumpkin_block; @@ -9,20 +9,18 @@ use std::sync::Arc; pub struct ConduitBlock; impl BlockBehaviour for ConduitBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - pumpkin_data::block_properties::MangroveRootsLikeProperties::default(args.block); - props.r#waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + pumpkin_data::block_properties::MangroveRootsLikeProperties::default(args.block); + props.r#waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = ConduitBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/coral/coral_block.rs b/crates/pumpkin/src/block/blocks/coral/coral_block.rs index 3c91b0667..b1517fc3c 100644 --- a/crates/pumpkin/src/block/blocks/coral/coral_block.rs +++ b/crates/pumpkin/src/block/blocks/coral/coral_block.rs @@ -1,5 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, OnScheduledTickArgs, PlacedArgs, blocks::coral::{is_dead_coral, scan_for_water, try_schedule_die_tick}, }; use pumpkin_data::{Block, BlockId, tag}; @@ -18,25 +18,22 @@ impl BlockMetadata for CoralBlock { } } impl BlockBehaviour for CoralBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { - try_schedule_die_tick(args.block, args.world, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + { + if !scan_for_water(args.world, args.position) && !is_dead_coral(args.block) { + try_schedule_die_tick(args.block, args.world, args.position); } - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { - let Some(dead_block) = get_dead_coral_block_type(args.block.id) else { - return; - }; - let dead_block_state_id = dead_block.default_state.id; - args.world - .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !scan_for_water(args.world, args.position) && !is_dead_coral(args.block) { + let Some(dead_block) = get_dead_coral_block_type(args.block.id) else { + return; + }; + let dead_block_state_id = dead_block.default_state.id; + args.world + .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()); + } } } const fn get_dead_coral_block_type(id: BlockId) -> Option<&'static Block> { diff --git a/crates/pumpkin/src/block/blocks/coral/coral_fan.rs b/crates/pumpkin/src/block/blocks/coral/coral_fan.rs index 43af2397b..1e9f0d73c 100644 --- a/crates/pumpkin/src/block/blocks/coral/coral_fan.rs +++ b/crates/pumpkin/src/block/blocks/coral/coral_fan.rs @@ -1,6 +1,6 @@ use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockIsReplacing, BlockMetadata, CanPlaceAtArgs, + BlockBehaviour, BlockIsReplacing, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, blocks::coral::{is_dead_coral, scan_for_water, try_schedule_die_tick}, }, @@ -47,102 +47,90 @@ pub type CoralWallFanLikeProperties = LadderLikeProperties; pub type CoralFanLikeProperties = MangroveRootsLikeProperties; impl BlockBehaviour for CoralFanBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return get_default_coral_fan_state_id( - args.block, - args.replacing.water_source(), - ); - } - } - let mut directions = args.player.get_entity().get_entity_facing_order(); - - if args.replacing == BlockIsReplacing::None { - let face = args.direction.to_facing(); - let mut i = 0; - while i < directions.len() && directions[i] != face { - i += 1; - } - - if i > 0 { - directions.copy_within(0..i, 1); - directions[0] = face; - } - } else if directions[0] == Facing::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return get_default_coral_fan_state_id( - args.block, - args.replacing.water_source(), - ); - } - } - - for dir in directions { - if let (Some(h_facing), Some(opp_facing)) = ( - dir.to_horizontal_facing(), - dir.opposite().to_horizontal_facing(), - ) && can_place_at(args.world, args.position, h_facing) - { - let Some(wall_block) = get_corresponding_wall_fan_type(args.block.id) else { - return BlockStateId::AIR; - }; - let mut coral_wall_fan_props = CoralWallFanLikeProperties::default(wall_block); - coral_wall_fan_props.waterlogged = args.replacing.water_source(); - coral_wall_fan_props.facing = opp_facing; - return coral_wall_fan_props.to_state_id(wall_block); - } - } - + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.direction == BlockDirection::Down { let support_block = args.world.get_block_state(&args.position.down()); if support_block.is_center_solid(BlockDirection::Up) { return get_default_coral_fan_state_id(args.block, args.replacing.water_source()); } - BlockStateId::AIR - }) - } + } + let mut directions = args.player.get_entity().get_entity_facing_order(); - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await { - try_schedule_die_tick(args.block, args.world, args.position).await; + if args.replacing == BlockIsReplacing::None { + let face = args.direction.to_facing(); + let mut i = 0; + while i < directions.len() && directions[i] != face { + i += 1; } - }) - } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { - let current_state = args.world.get_block_state(args.position); - - let Some(dead_block) = get_dead_type(args.block.id) else { - return; - }; - - // VANILLA FIX: Explicitly set waterlogged to false when dying - let dead_block_state_id = if is_wall_fan(args.block) { - let mut props = - CoralWallFanLikeProperties::from_state_id(current_state.id, args.block); - props.waterlogged = false; - props.to_state_id(dead_block) - } else { - let mut props = - CoralFanLikeProperties::from_state_id(current_state.id, args.block); - props.waterlogged = false; - props.to_state_id(dead_block) - }; - - args.world - .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()) - .await; + if i > 0 { + directions.copy_within(0..i, 1); + directions[0] = face; } - }) + } else if directions[0] == Facing::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if support_block.is_center_solid(BlockDirection::Up) { + return get_default_coral_fan_state_id(args.block, args.replacing.water_source()); + } + } + + for dir in directions { + if let (Some(h_facing), Some(opp_facing)) = ( + dir.to_horizontal_facing(), + dir.opposite().to_horizontal_facing(), + ) && can_place_at(args.world, args.position, h_facing) + { + let Some(wall_block) = get_corresponding_wall_fan_type(args.block.id) else { + return BlockStateId::AIR; + }; + let mut coral_wall_fan_props = CoralWallFanLikeProperties::default(wall_block); + coral_wall_fan_props.waterlogged = args.replacing.water_source(); + coral_wall_fan_props.facing = opp_facing; + return coral_wall_fan_props.to_state_id(wall_block); + } + } + + let support_block = args.world.get_block_state(&args.position.down()); + if support_block.is_center_solid(BlockDirection::Up) { + return get_default_coral_fan_state_id(args.block, args.replacing.water_source()); + } + BlockStateId::AIR } - fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> bool { + fn placed(&self, args: PlacedArgs<'_>) { + { + if !scan_for_water(args.world, args.position) { + try_schedule_die_tick(args.block, args.world, args.position); + } + } + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !scan_for_water(args.world, args.position) && !is_dead_coral(args.block) { + let current_state = args.world.get_block_state(args.position); + + let Some(dead_block) = get_dead_type(args.block.id) else { + return; + }; + + // VANILLA FIX: Explicitly set waterlogged to false when dying + let dead_block_state_id = if is_wall_fan(args.block) { + let mut props = + CoralWallFanLikeProperties::from_state_id(current_state.id, args.block); + props.waterlogged = false; + props.to_state_id(dead_block) + } else { + let mut props = CoralFanLikeProperties::from_state_id(current_state.id, args.block); + props.waterlogged = false; + props.to_state_id(dead_block) + }; + + args.world + .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()); + } + } + + fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { let support_block = args.block_accessor.get_block_state(&args.position.down()); if support_block.is_center_solid(BlockDirection::Up) && !is_wall_fan(args.block) { return true; @@ -155,27 +143,25 @@ impl BlockBehaviour for CoralFanBlock { false } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if is_wall_fan(args.block) { - let props = CoralWallFanLikeProperties::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at(args.world, args.position, props.facing.opposite()) - { - return BlockStateId::AIR; - } - } else if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if !support_block.is_center_solid(BlockDirection::Up) { - return BlockStateId::AIR; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if is_wall_fan(args.block) { + let props = CoralWallFanLikeProperties::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_at(args.world, args.position, props.facing.opposite()) + { + return BlockStateId::AIR; } + } else if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if !support_block.is_center_solid(BlockDirection::Up) { + return BlockStateId::AIR; + } + } - args.state_id - }) + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/coral/coral_plant.rs b/crates/pumpkin/src/block/blocks/coral/coral_plant.rs index c5e70965a..cd1c70d6e 100644 --- a/crates/pumpkin/src/block/blocks/coral/coral_plant.rs +++ b/crates/pumpkin/src/block/blocks/coral/coral_plant.rs @@ -6,8 +6,8 @@ use pumpkin_data::{ use pumpkin_world::world::BlockFlags; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, PlacedArgs, blocks::coral::{is_dead_coral, scan_for_water, try_schedule_die_tick}, }; pub struct CoralPlantBlock; @@ -26,36 +26,30 @@ impl BlockMetadata for CoralPlantBlock { pub type CoralPlantLikeProperties = MangroveRootsLikeProperties; impl BlockBehaviour for CoralPlantBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CoralPlantLikeProperties::default(args.block); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = CoralPlantLikeProperties::default(args.block); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { - try_schedule_die_tick(args.block, args.world, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + { + if !scan_for_water(args.world, args.position) && !is_dead_coral(args.block) { + try_schedule_die_tick(args.block, args.world, args.position); } - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { - let current_state = args.world.get_block_state(args.position); - let dead_block_state_id = { - let props = - CoralPlantLikeProperties::from_state_id(current_state.id, args.block); - props.to_state_id(get_dead_type(args.block.id).unwrap_or_default().to_block()) - }; - args.world - .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !scan_for_water(args.world, args.position) && !is_dead_coral(args.block) { + let current_state = args.world.get_block_state(args.position); + let dead_block_state_id = { + let props = CoralPlantLikeProperties::from_state_id(current_state.id, args.block); + props.to_state_id(get_dead_type(args.block.id).unwrap_or_default().to_block()) + }; + args.world + .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()); + } } - fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> bool { + fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { let support_block = args.block_accessor.get_block_state(&args.position.down()); if support_block.is_center_solid(BlockDirection::Up) { return true; @@ -63,19 +57,17 @@ impl BlockBehaviour for CoralPlantBlock { false } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if !support_block.is_center_solid(BlockDirection::Up) { - return BlockStateId::AIR; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if !support_block.is_center_solid(BlockDirection::Up) { + return BlockStateId::AIR; } - args.state_id - }) + } + args.state_id } } const fn get_dead_type(id: BlockId) -> Option { diff --git a/crates/pumpkin/src/block/blocks/coral/mod.rs b/crates/pumpkin/src/block/blocks/coral/mod.rs index a4658e662..244a787ab 100644 --- a/crates/pumpkin/src/block/blocks/coral/mod.rs +++ b/crates/pumpkin/src/block/blocks/coral/mod.rs @@ -12,7 +12,7 @@ pub mod coral_block; pub mod coral_fan; pub mod coral_plant; -pub async fn scan_for_water(world: &Arc, pos: &BlockPos) -> bool { +pub fn scan_for_water(world: &Arc, pos: &BlockPos) -> bool { for direction in BlockDirection::all() { let neighbor_pos = pos.offset(direction.to_offset()); let block = world.get_fluid(&neighbor_pos); @@ -44,7 +44,7 @@ fn is_dead_coral(block: &Block) -> bool { || block == &Block::DEAD_TUBE_CORAL_FAN || block == &Block::DEAD_TUBE_CORAL_WALL_FAN } -async fn try_schedule_die_tick(block: &Block, world: &Arc, pos: &BlockPos) { +pub fn try_schedule_die_tick(block: &Block, world: &Arc, pos: &BlockPos) { let tick_delay = 60 + rand::rng().random_range(0..40); world.schedule_block_tick( block, diff --git a/crates/pumpkin/src/block/blocks/crafting_table.rs b/crates/pumpkin/src/block/blocks/crafting_table.rs index cb2b687b3..6a67fd6cd 100644 --- a/crates/pumpkin/src/block/blocks/crafting_table.rs +++ b/crates/pumpkin/src/block/blocks/crafting_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler; @@ -16,24 +16,22 @@ use tokio::sync::Mutex; pub struct CraftingTableBlock; impl BlockBehaviour for CraftingTableBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithCraftingTable as i32, - 1, - ) - .await; - args.player - .open_handled_screen( - &CraftingTableScreenFactory(args.server.recipe_manager.clone()), - Some(*args.position), - ) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithCraftingTable as i32, + 1, + ); + let player = Arc::clone(args.player); + let recipe_manager = args.server.recipe_manager.clone(); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&CraftingTableScreenFactory(recipe_manager), Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/creaking_heart.rs b/crates/pumpkin/src/block/blocks/creaking_heart.rs index ffb982358..572cee708 100644 --- a/crates/pumpkin/src/block/blocks/creaking_heart.rs +++ b/crates/pumpkin/src/block/blocks/creaking_heart.rs @@ -11,9 +11,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; use crate::block::entities::creaking_heart::CreakingHeartBlockEntity; -use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, -}; +use crate::block::{BlockBehaviour, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs}; #[pumpkin_block("minecraft:creaking_heart")] pub struct CreakingHeartBlock; @@ -45,24 +43,20 @@ impl CreakingHeartBlock { } impl BlockBehaviour for CreakingHeartBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - CreakingHeartLikeProperties::from_state_id(args.block.default_state.id, args.block); - props.axis = match args.direction { - pumpkin_data::BlockDirection::North | pumpkin_data::BlockDirection::South => { - Axis::Z - } - pumpkin_data::BlockDirection::East | pumpkin_data::BlockDirection::West => Axis::X, - pumpkin_data::BlockDirection::Up | pumpkin_data::BlockDirection::Down => Axis::Y, - }; - props.creaking_heart_state = CreakingHeartState::Uprooted; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + CreakingHeartLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.axis = match args.direction { + pumpkin_data::BlockDirection::North | pumpkin_data::BlockDirection::South => Axis::Z, + pumpkin_data::BlockDirection::East | pumpkin_data::BlockDirection::West => Axis::X, + pumpkin_data::BlockDirection::Up | pumpkin_data::BlockDirection::Down => Axis::Y, + }; + props.creaking_heart_state = CreakingHeartState::Uprooted; + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = CreakingHeartBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); @@ -71,13 +65,11 @@ impl BlockBehaviour for CreakingHeartBlock { if Self::check_active_logs(args.world.as_ref(), args.position, props.axis) { props.creaking_heart_state = CreakingHeartState::Dormant; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); args.world.play_sound( Sound::BlockCreakingHeartSpawn, @@ -85,11 +77,11 @@ impl BlockBehaviour for CreakingHeartBlock { &args.position.to_f64(), ); } - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state_id = args.world.get_block_state_id(args.position); let mut props = CreakingHeartLikeProperties::from_state_id(state_id, args.block); @@ -110,27 +102,24 @@ impl BlockBehaviour for CreakingHeartBlock { if props.creaking_heart_state != new_state { props.creaking_heart_state = new_state; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { args.world.play_sound( Sound::BlockCreakingHeartBreak, SoundCategory::Blocks, &args.position.to_f64(), ); args.world - .drop_stack(args.position, ItemStack::new(1, &Item::CREAKING_HEART)) - .await; - }) + .drop_stack(args.position, ItemStack::new(1, &Item::CREAKING_HEART)); + } } } diff --git a/crates/pumpkin/src/block/blocks/decorated_pot.rs b/crates/pumpkin/src/block/blocks/decorated_pot.rs index ead347a55..879b2f09a 100644 --- a/crates/pumpkin/src/block/blocks/decorated_pot.rs +++ b/crates/pumpkin/src/block/blocks/decorated_pot.rs @@ -10,125 +10,104 @@ use pumpkin_macros::pumpkin_block; use crate::block::entities::decorated_pot::DecoratedPotBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - PlacedArgs, UseWithItemArgs, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, + UseWithItemArgs, }; #[pumpkin_block("minecraft:decorated_pot")] pub struct DecoratedPotBlock; impl BlockBehaviour for DecoratedPotBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - DecoratedPotLikeProperties::from_state_id(args.block.default_state.id, args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + DecoratedPotLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = DecoratedPotBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let entity = DecoratedPotBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if args.item_stack.item_count == 0 { - return self - .normal_use(NormalUseArgs { - server: args.server, - world: args.world, - block: args.block, - position: args.position, - player: args.player, - hit: args.hit, - }) - .await; - } + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + if args.item_stack.item_count == 0 { + return self.normal_use(NormalUseArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + hit: args.hit, + }); + } - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = block_entity - .as_any() - .downcast_ref::() - { - if pot_entity.try_insert_item(args.item_stack, 1).await { - args.world.play_sound( - Sound::BlockDecoratedPotInsert, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } else { - args.world.play_sound( - Sound::BlockDecoratedPotInsertFail, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } - return BlockActionResult::Success; - } - - BlockActionResult::Pass - }) - } - - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.world.play_sound( - Sound::BlockDecoratedPotInsertFail, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - BlockActionResult::Success - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = block_entity - .as_any() - .downcast_ref::() - && let Some(contained) = pot_entity.take_item().await - { - args.world.drop_stack(args.position, contained).await; - } - - args.world.play_sound( - Sound::BlockDecoratedPotShatter, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.world - .drop_stack(args.position, ItemStack::new(4, &Item::BRICK)) - .await; - }) - } - - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = block_entity - .as_any() - .downcast_ref::() - { - Some(pot_entity.get_comparator_output().await) + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() + { + if pot_entity.try_insert_item(args.item_stack, 1) { + args.world.play_sound( + Sound::BlockDecoratedPotInsert, + SoundCategory::Blocks, + &args.position.to_f64(), + ); } else { - Some(0) + args.world.play_sound( + Sound::BlockDecoratedPotInsertFail, + SoundCategory::Blocks, + &args.position.to_f64(), + ); } - }) + return BlockActionResult::Success; + } + + BlockActionResult::Pass + } + + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.world.play_sound( + Sound::BlockDecoratedPotInsertFail, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + BlockActionResult::Success + } + + fn broken(&self, args: BrokenArgs<'_>) { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() + && let Some(contained) = pot_entity.take_item() + { + args.world.drop_stack(args.position, contained); + } + + args.world.play_sound( + Sound::BlockDecoratedPotShatter, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world + .drop_stack(args.position, ItemStack::new(4, &Item::BRICK)); + } + + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() + { + Some(pot_entity.get_comparator_output()) + } else { + Some(0) + } } } diff --git a/crates/pumpkin/src/block/blocks/dirt_path.rs b/crates/pumpkin/src/block/blocks/dirt_path.rs index 94cecbaa7..4c70ff741 100644 --- a/crates/pumpkin/src/block/blocks/dirt_path.rs +++ b/crates/pumpkin/src/block/blocks/dirt_path.rs @@ -1,5 +1,4 @@ use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -17,40 +16,32 @@ use pumpkin_world::world::BlockFlags; pub struct DirtPathBlock; impl BlockBehaviour for DirtPathBlock { - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // TODO: push up entities + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + // TODO: push up entities + args.world.set_block_state( + args.position, + Block::DIRT.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if !can_place_at(args.world, args.position) { + return Block::DIRT.default_state.id; + } + + args.block.default_state.id + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position) { args.world - .set_block_state( - args.position, - Block::DIRT.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - }) - } - - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - return Block::DIRT.default_state.id; - } - - args.block.default_state.id - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/doors.rs b/crates/pumpkin/src/block/blocks/doors.rs index 7eabbc23d..25e1dfafd 100644 --- a/crates/pumpkin/src/block/blocks/doors.rs +++ b/crates/pumpkin/src/block/blocks/doors.rs @@ -1,4 +1,3 @@ -use crate::entity::EntityBase; use pumpkin_data::BlockDirection; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::Axis; @@ -17,7 +16,6 @@ use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::BrokenArgs; use crate::block::CanPlaceAtArgs; use crate::block::GetStateForNeighborUpdateArgs; @@ -36,7 +34,7 @@ use pumpkin_util::GameMode; type DoorProperties = pumpkin_data::block_properties::OakDoorLikeProperties; -async fn toggle_door(player: &Player, world: &Arc, block_pos: &BlockPos) { +fn toggle_door(player: &Player, world: &Arc, block_pos: &BlockPos) { let (block, block_state) = world.get_block_and_state_id(block_pos); let mut door_props = DoorProperties::from_state_id(block_state, block); door_props.open = !door_props.open; @@ -58,20 +56,16 @@ async fn toggle_door(player: &Player, world: &Arc, block_pos: &BlockPos) *block_pos, ); - world - .set_block_state( - block_pos, - door_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - world - .set_block_state( - &other_pos, - other_door_props.to_state_id(other_block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + block_pos, + door_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); + world.set_block_state( + &other_pos, + other_door_props.to_state_id(other_block), + BlockFlags::NOTIFY_LISTENERS, + ); } fn can_open_door(block: &Block) -> bool { @@ -103,7 +97,7 @@ fn get_sound(block: &Block, open: bool) -> Sound { #[expect(clippy::pedantic)] #[inline] -async fn get_hinge( +fn get_hinge( world: &World, pos: &BlockPos, use_item: &SUseItemOn, @@ -177,7 +171,7 @@ impl DoorBlock { door_props.open } - pub async fn set_open(world: &Arc, block_pos: &BlockPos, open: bool) { + pub fn set_open(world: &Arc, block_pos: &BlockPos, open: bool) { let (block, block_state) = world.get_block_and_state_id(block_pos); if !block.has_tag(&tag::Block::MINECRAFT_DOORS) { return; @@ -198,46 +192,39 @@ impl DoorBlock { world.play_block_sound(get_sound(block, open), SoundCategory::Blocks, *block_pos); - world - .set_block_state( - block_pos, - door_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + block_pos, + door_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); if other_block.id == block.id { let mut other_door_props = DoorProperties::from_state_id(other_state_id, other_block); other_door_props.open = open; - world - .set_block_state( - &other_pos, - other_door_props.to_state_id(other_block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + &other_pos, + other_door_props.to_state_id(other_block), + BlockFlags::NOTIFY_LISTENERS, + ); } } } impl BlockBehaviour for DoorBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let powered = block_receives_redstone_power(args.world, args.position).await - || block_receives_redstone_power(args.world, &args.position.up()).await; - - let direction = args.player.get_entity().get_horizontal_facing(); - let hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await; - - let mut door_props = DoorProperties::default(args.block); - door_props.half = DoubleBlockHalf::Lower; - door_props.facing = direction; - door_props.hinge = hinge; - door_props.powered = powered; - door_props.open = powered; - - door_props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut door_props = DoorProperties::default(args.block); + let facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + door_props.facing = facing; + door_props.half = DoubleBlockHalf::Lower; + door_props.hinge = get_hinge(args.world, args.position, args.use_item_on, facing); + door_props.open = false; + door_props.powered = false; + door_props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -248,65 +235,59 @@ impl BlockBehaviour for DoorBlock { .replaceable() } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let mut door_props = DoorProperties::from_state_id(args.state_id, args.block); door_props.half = DoubleBlockHalf::Upper; - args.world - .set_block_state( - &args.position.offset(BlockDirection::Up.to_offset()), - door_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; - }) + args.world.set_block_state( + &args.position.offset(BlockDirection::Up.to_offset()), + door_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); + } } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { if !can_open_door(args.block) { return BlockActionResult::Pass; } - toggle_door(args.player, args.world, args.position).await; + toggle_door(args.player, args.world, args.position); BlockActionResult::Success - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let door_props = DoorProperties::from_state_id(args.state.id, args.block); - let other_half_pos = match door_props.half { - DoubleBlockHalf::Upper => args.position.down(), - DoubleBlockHalf::Lower => args.position.up(), - }; + fn broken(&self, args: BrokenArgs<'_>) { + let door_props = DoorProperties::from_state_id(args.state.id, args.block); + let other_half_pos = match door_props.half { + DoubleBlockHalf::Upper => args.position.down(), + DoubleBlockHalf::Lower => args.position.up(), + }; - let neighbor_state_id = args.world.get_block_state_id(&other_half_pos); - if neighbor_state_id.to_block_id() != args.block.id { - args.world.update_neighbors(&other_half_pos, None).await; - return; // Neighbor is already gone or is a different block - } + let neighbor_state_id = args.world.get_block_state_id(&other_half_pos); + if neighbor_state_id.to_block_id() != args.block.id { + args.world.update_neighbors(&other_half_pos, None); + return; // Neighbor is already gone or is a different block + } - let is_creative = args.player.gamemode.load() == GameMode::Creative; - let flags = if door_props.half == DoubleBlockHalf::Upper && !is_creative { - BlockFlags::NOTIFY_ALL - } else { - BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_ALL - }; + let is_creative = args.player.gamemode.load() == GameMode::Creative; + let flags = if door_props.half == DoubleBlockHalf::Upper && !is_creative { + BlockFlags::NOTIFY_ALL + } else { + BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_ALL + }; - args.world - .break_block(&other_half_pos, Some(args.player.clone()), flags) - .await; - }) + args.world + .break_block(&other_half_pos, Some(args.player.clone()), flags); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let block_state = args.world.get_block_state(args.position); let mut door_props = DoorProperties::from_state_id(block_state.id, args.block); - let other_half = match door_props.half { DoubleBlockHalf::Upper => BlockDirection::Down, DoubleBlockHalf::Lower => BlockDirection::Up, @@ -314,23 +295,28 @@ impl BlockBehaviour for DoorBlock { let other_pos = args.position.offset(other_half.to_offset()); let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_pos); - if other_block.id != args.block.id { - return; - } + let powered = block_receives_redstone_power(args.world, args.position) + || block_receives_redstone_power(args.world, &other_pos); - let powered = block_receives_redstone_power(args.world, args.position).await - || block_receives_redstone_power(args.world, &other_pos).await; + if door_props.powered != powered { + let sound_half = if door_props.open { + DoubleBlockHalf::Lower + } else { + DoubleBlockHalf::Upper + }; - if args.block.id == other_block.id && powered != door_props.powered { let mut other_door_props = DoorProperties::from_state_id(other_state_id, other_block); - door_props.powered = !door_props.powered; - other_door_props.powered = door_props.powered; - if powered != door_props.open { - door_props.open = door_props.powered; - other_door_props.open = other_door_props.powered; + door_props.powered = powered; + other_door_props.powered = powered; + if door_props.open != powered { + door_props.open = powered; + other_door_props.open = powered; + } + + if door_props.half == sound_half { args.world.play_block_sound( get_sound(args.block, powered), SoundCategory::Blocks, @@ -338,55 +324,48 @@ impl BlockBehaviour for DoorBlock { ); } - args.world - .set_block_state( - args.position, - door_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world - .set_block_state( - &other_pos, - other_door_props.to_state_id(other_block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + door_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + args.world.set_block_state( + &other_pos, + other_door_props.to_state_id(other_block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let lv = DoorProperties::from_state_id(args.state_id, args.block).half; - if args.direction.to_axis() != Axis::Y - || (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let lv = DoorProperties::from_state_id(args.state_id, args.block).half; + if args.direction.to_axis() != Axis::Y + || (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up) + { + if lv == DoubleBlockHalf::Lower + && args.direction == BlockDirection::Down + && !has_support(args.world, args.position) { - if lv == DoubleBlockHalf::Lower - && args.direction == BlockDirection::Down - && !has_support(args.world, args.position) - { - return BlockStateId::AIR; - } - } else if Block::from_state_id(args.neighbor_state_id).id == args.block.id - && DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv - { - let mut new_state = - DoorProperties::from_state_id(args.neighbor_state_id, args.block); - new_state.half = lv; - return new_state.to_state_id(args.block); - } else { return BlockStateId::AIR; } - args.state_id - }) + } else if Block::from_state_id(args.neighbor_state_id).id == args.block.id + && DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv + { + let mut new_state = DoorProperties::from_state_id(args.neighbor_state_id, args.block); + new_state.half = lv; + return new_state.to_state_id(args.block); + } else { + return BlockStateId::AIR; + } + args.state_id } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + { if args.moved { return; } @@ -403,14 +382,12 @@ impl BlockBehaviour for DoorBlock { DoubleBlockHalf::Lower => args.position.up(), }; - args.world - .break_block( - &other_half_pos, - None, - BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_ALL, - ) - .await; - }) + args.world.break_block( + &other_half_pos, + None, + BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/dragon_egg.rs b/crates/pumpkin/src/block/blocks/dragon_egg.rs index 75f600ad2..fc971556b 100644 --- a/crates/pumpkin/src/block/blocks/dragon_egg.rs +++ b/crates/pumpkin/src/block/blocks/dragon_egg.rs @@ -1,6 +1,6 @@ use crate::block::blocks::falling::FallingBlock; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, BrokenArgs, NormalUseArgs, PlacedArgs}; use crate::world::World; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; @@ -12,7 +12,7 @@ use std::sync::Arc; pub struct DragonEggBlock; impl DragonEggBlock { - async fn teleport(&self, world: &Arc, pos: &BlockPos) { + fn teleport(world: &Arc, pos: &BlockPos) { for _ in 0..1000 { let x = pos.0.x + rng().random_range(-16..16); let y = pos.0.y + rng().random_range(-8..8); @@ -24,20 +24,16 @@ impl DragonEggBlock { if state.is_air() && !below_state.is_air() { let current_state = world.get_block_state(pos); - world - .set_block_state( - &test_pos, - current_state.id, - pumpkin_world::world::BlockFlags::NOTIFY_ALL, - ) - .await; - world - .set_block_state( - pos, - pumpkin_data::Block::AIR.default_state.id, - pumpkin_world::world::BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &test_pos, + current_state.id, + pumpkin_world::world::BlockFlags::NOTIFY_ALL, + ); + world.set_block_state( + pos, + pumpkin_data::Block::AIR.default_state.id, + pumpkin_world::world::BlockFlags::NOTIFY_ALL, + ); return; } } @@ -45,33 +41,22 @@ impl DragonEggBlock { } impl BlockBehaviour for DragonEggBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world - .schedule_block_tick(args.block, *args.position, 5, TickPriority::Normal); - }) + fn placed(&self, args: PlacedArgs<'_>) { + args.world + .schedule_block_tick(args.block, *args.position, 5, TickPriority::Normal); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - self.teleport(args.world, args.position).await; - BlockActionResult::Success - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + Self::teleport(args.world, args.position); + BlockActionResult::Success } // Dragon egg is typically teleported when attacked - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.teleport(args.world, args.position).await; - }) + fn broken(&self, args: BrokenArgs<'_>) { + Self::teleport(args.world, args.position); } - fn on_scheduled_tick<'a>( - &'a self, - args: crate::block::OnScheduledTickArgs<'a>, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - FallingBlock::on_scheduled_tick(&FallingBlock, args).await; - }) + fn on_scheduled_tick(&self, args: crate::block::OnScheduledTickArgs<'_>) { + FallingBlock::on_scheduled_tick(&FallingBlock, args); } } diff --git a/crates/pumpkin/src/block/blocks/dripstone.rs b/crates/pumpkin/src/block/blocks/dripstone.rs index 3d5cb205b..70f039ac4 100644 --- a/crates/pumpkin/src/block/blocks/dripstone.rs +++ b/crates/pumpkin/src/block/blocks/dripstone.rs @@ -2,8 +2,8 @@ use std::sync::Arc; use crate::{ block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, PlacedArgs, + BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + PlacedArgs, }, entity::player::Player, world::World, @@ -30,26 +30,24 @@ impl BlockBehaviour for DripstoneBlock { args.player, ) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut dripstone_props = PointedDripstoneLikeProperties::default(args.block); - dripstone_props.waterlogged = args.replacing.water_source(); - let Some(support_block_ver_dir) = get_support_block_vertical_direction( - args.world, - args.position, - Some(args.direction), - Some(args.player), - ) else { - //this shouldn't happen - return Block::AIR.default_state.id; - }; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut dripstone_props = PointedDripstoneLikeProperties::default(args.block); + dripstone_props.waterlogged = args.replacing.water_source(); + let Some(support_block_ver_dir) = get_support_block_vertical_direction( + args.world, + args.position, + Some(args.direction), + Some(args.player), + ) else { + //this shouldn't happen + return Block::AIR.default_state.id; + }; - dripstone_props.vertical_direction = flip_dir(support_block_ver_dir); - dripstone_props.to_state_id(&Block::POINTED_DRIPSTONE) - }) + dripstone_props.vertical_direction = flip_dir(support_block_ver_dir); + dripstone_props.to_state_id(&Block::POINTED_DRIPSTONE) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let (len, vertical_dir) = get_stalagmite_or_stalactice_len_and_dir_from_tip_pos( args.world, args.position, @@ -57,16 +55,16 @@ impl BlockBehaviour for DripstoneBlock { ); match vertical_dir { VerticalDirection::Up => { - update_stalagmite(args.world, len, args.position).await; + update_stalagmite(args.world, len, args.position); } VerticalDirection::Down => { - update_stalactite(args.world, len, args.position).await; + update_stalactite(args.world, len, args.position); } } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { let broken_dripstone_props = PointedDripstoneLikeProperties::from_state_id(args.state.id, args.block); let new_tip_pos = match broken_dripstone_props.vertical_direction { @@ -81,54 +79,52 @@ impl BlockBehaviour for DripstoneBlock { ); match vertical_dir { VerticalDirection::Up => { - update_stalagmite(args.world, len, &new_tip_pos).await; + update_stalagmite(args.world, len, &new_tip_pos); } VerticalDirection::Down => { - update_stalactite(args.world, len, &new_tip_pos).await; + update_stalactite(args.world, len, &new_tip_pos); } } - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at_pos(args.world, args.position, None, None) { - return Block::AIR.default_state.id; - } - let mut dripstone_props = - PointedDripstoneLikeProperties::from_state_id(args.state_id, args.block); - if dripstone_props.thickness != SpeleothemThickness::TipMerge { - return args.state_id; - } - match dripstone_props.vertical_direction { - VerticalDirection::Up => { - let block_above = args.world.get_block(&args.position.up()); - if block_above != &Block::POINTED_DRIPSTONE { - dripstone_props.thickness = SpeleothemThickness::Tip; - return dripstone_props.to_state_id(args.block); - } - } - VerticalDirection::Down => { - let block_below = args.world.get_block(&args.position.down()); - if block_below != &Block::POINTED_DRIPSTONE { - dripstone_props.thickness = SpeleothemThickness::Tip; - return dripstone_props.to_state_id(args.block); - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at_pos(args.world, args.position, None, None) { + return Block::AIR.default_state.id; + } + let mut dripstone_props = + PointedDripstoneLikeProperties::from_state_id(args.state_id, args.block); + if dripstone_props.thickness != SpeleothemThickness::TipMerge { + return args.state_id; + } + match dripstone_props.vertical_direction { + VerticalDirection::Up => { + let block_above = args.world.get_block(&args.position.up()); + if block_above != &Block::POINTED_DRIPSTONE { + dripstone_props.thickness = SpeleothemThickness::Tip; + return dripstone_props.to_state_id(args.block); } } - args.state_id - }) + VerticalDirection::Down => { + let block_below = args.world.get_block(&args.position.down()); + if block_below != &Block::POINTED_DRIPSTONE { + dripstone_props.thickness = SpeleothemThickness::Tip; + return dripstone_props.to_state_id(args.block); + } + } + } + args.state_id } } -async fn update_stalagmite(world: &Arc, stalagmite_len: u8, tip_pos: &BlockPos) { +fn update_stalagmite(world: &Arc, stalagmite_len: u8, tip_pos: &BlockPos) { let block_above = world.get_block(&tip_pos.up()); if block_above == &Block::POINTED_DRIPSTONE { - modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::TipMerge).await; - modify_dripstone_thickness_to(world, &tip_pos.up(), SpeleothemThickness::TipMerge).await; + modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::TipMerge); + modify_dripstone_thickness_to(world, &tip_pos.up(), SpeleothemThickness::TipMerge); } else { - modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::Tip).await; + modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::Tip); } match stalagmite_len { 2 => { @@ -136,74 +132,65 @@ async fn update_stalagmite(world: &Arc, stalagmite_len: u8, tip_pos: &Blo world, &tip_pos.down_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); } 3 => { modify_dripstone_thickness_to( world, &tip_pos.down_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.down_height(2), SpeleothemThickness::Base, - ) - .await; + ); } 4 => { modify_dripstone_thickness_to( world, &tip_pos.down_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.down_height(2), SpeleothemThickness::Middle, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.down_height(3), SpeleothemThickness::Base, - ) - .await; + ); } 5 => { modify_dripstone_thickness_to( world, &tip_pos.down_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.down_height(2), SpeleothemThickness::Middle, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.down_height(3), SpeleothemThickness::Middle, - ) - .await; + ); } _ => {} } } -async fn update_stalactite(world: &Arc, stalagmite_len: u8, tip_pos: &BlockPos) { +fn update_stalactite(world: &Arc, stalagmite_len: u8, tip_pos: &BlockPos) { let block_below = world.get_block(&tip_pos.down()); if block_below == &Block::POINTED_DRIPSTONE { - modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::TipMerge).await; - modify_dripstone_thickness_to(world, &tip_pos.down(), SpeleothemThickness::TipMerge).await; + modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::TipMerge); + modify_dripstone_thickness_to(world, &tip_pos.down(), SpeleothemThickness::TipMerge); } else { - modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::Tip).await; + modify_dripstone_thickness_to(world, tip_pos, SpeleothemThickness::Tip); } match stalagmite_len { 2 => { @@ -211,54 +198,45 @@ async fn update_stalactite(world: &Arc, stalagmite_len: u8, tip_pos: &Blo world, &tip_pos.up_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); } 3 => { modify_dripstone_thickness_to( world, &tip_pos.up_height(1), SpeleothemThickness::Frustum, - ) - .await; - modify_dripstone_thickness_to(world, &tip_pos.up_height(2), SpeleothemThickness::Base) - .await; + ); + modify_dripstone_thickness_to(world, &tip_pos.up_height(2), SpeleothemThickness::Base); } 4 => { modify_dripstone_thickness_to( world, &tip_pos.up_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.up_height(2), SpeleothemThickness::Middle, - ) - .await; - modify_dripstone_thickness_to(world, &tip_pos.up_height(3), SpeleothemThickness::Base) - .await; + ); + modify_dripstone_thickness_to(world, &tip_pos.up_height(3), SpeleothemThickness::Base); } 5 => { modify_dripstone_thickness_to( world, &tip_pos.up_height(1), SpeleothemThickness::Frustum, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.up_height(2), SpeleothemThickness::Middle, - ) - .await; + ); modify_dripstone_thickness_to( world, &tip_pos.up_height(3), SpeleothemThickness::Middle, - ) - .await; + ); } _ => {} } @@ -382,7 +360,7 @@ fn can_support_dripstone(support_block: &Block) -> bool { } false } -async fn modify_dripstone_thickness_to( +fn modify_dripstone_thickness_to( world: &Arc, pos: &BlockPos, new_thickness: SpeleothemThickness, @@ -399,13 +377,11 @@ async fn modify_dripstone_thickness_to( return; } support_props.thickness = new_thickness; - world - .set_block_state( - pos, - support_props.to_state_id(&Block::POINTED_DRIPSTONE), - BlockFlags::empty(), - ) - .await; + world.set_block_state( + pos, + support_props.to_state_id(&Block::POINTED_DRIPSTONE), + BlockFlags::empty(), + ); } fn offset_pos_by_vertical_dir(pos: &BlockPos, ver_dir: VerticalDirection) -> BlockPos { match ver_dir { diff --git a/crates/pumpkin/src/block/blocks/enchanting_table.rs b/crates/pumpkin/src/block/blocks/enchanting_table.rs index f521c9c8a..dea630648 100644 --- a/crates/pumpkin/src/block/blocks/enchanting_table.rs +++ b/crates/pumpkin/src/block/blocks/enchanting_table.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::block::entities::enchanting_table::EnchantingTableBlockEntity; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, PlacedArgs}; use pumpkin_data::{Block, BlockStateId, translation}; use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; @@ -19,70 +19,71 @@ use tokio::sync::Mutex; pub struct EnchantingTableBlock; impl BlockBehaviour for EnchantingTableBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = EnchantingTableBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let entity = EnchantingTableBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let mut bookshelf_count = 0; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let mut bookshelf_count = 0; - for off_z in -1..=1 { - for off_x in -1..=1 { - if (off_z != 0 || off_x != 0) - && args - .world - .get_block_state(&args.position.add(off_x, 0, off_z)) - .id - == BlockStateId::AIR - && args - .world - .get_block_state(&args.position.add(off_x, 1, off_z)) - .id - == BlockStateId::AIR - // Air - { - for off_y in 0..=1 { + for off_z in -1..=1 { + for off_x in -1..=1 { + if (off_z != 0 || off_x != 0) + && args + .world + .get_block_state(&args.position.add(off_x, 0, off_z)) + .id + == BlockStateId::AIR + && args + .world + .get_block_state(&args.position.add(off_x, 1, off_z)) + .id + == BlockStateId::AIR + // Air + { + for off_y in 0..=1 { + if Self::is_bookshelf( + args.world, + &args.position.add(off_x * 2, off_y, off_z * 2), + ) { + bookshelf_count += 1; + } + if off_x != 0 && off_z != 0 { if Self::is_bookshelf( args.world, - &args.position.add(off_x * 2, off_y, off_z * 2), + &args.position.add(off_x * 2, off_y, off_z), ) { bookshelf_count += 1; } - if off_x != 0 && off_z != 0 { - if Self::is_bookshelf( - args.world, - &args.position.add(off_x * 2, off_y, off_z), - ) { - bookshelf_count += 1; - } - if Self::is_bookshelf( - args.world, - &args.position.add(off_x, off_y, off_z * 2), - ) { - bookshelf_count += 1; - } + if Self::is_bookshelf( + args.world, + &args.position.add(off_x, off_y, off_z * 2), + ) { + bookshelf_count += 1; } } } } } - let bookshelf_count = bookshelf_count.min(15); + } + let bookshelf_count = bookshelf_count.min(15); - args.player + let player = Arc::clone(args.player); + let pos = *args.position; + let seed = args.player.enchantment_seed(); + tokio::spawn(async move { + player .open_handled_screen( &EnchantingTableScreenFactory { bookshelf_count, - seed: args.player.enchantment_seed(), + seed, }, - Some(*args.position), + Some(pos), ) .await; - BlockActionResult::Success - }) + }); + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/end_portal.rs b/crates/pumpkin/src/block/blocks/end_portal.rs index 04316ce49..efb92e73c 100644 --- a/crates/pumpkin/src/block/blocks/end_portal.rs +++ b/crates/pumpkin/src/block/blocks/end_portal.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::OnEntityCollisionArgs; use crate::block::PlacedArgs; use crate::block::entities::end_portal::EndPortalBlockEntity; @@ -12,36 +11,31 @@ use pumpkin_macros::pumpkin_block; pub struct EndPortalBlock; impl BlockBehaviour for EndPortalBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let target_world = - if args.world.dimension.minecraft_name == Dimension::THE_END.minecraft_name { - args.server.get_world_from_dimension(&Dimension::OVERWORLD) - } else { - args.server.get_world_from_dimension(&Dimension::THE_END) - }; - if Arc::ptr_eq(&target_world, args.world) { - return; - } - tracing::info!( - "End portal collision at {:?}, targeting world {:?}", - args.position, - target_world.dimension.minecraft_name - ); - args.entity - .get_entity() - .try_use_portal(0, target_world, *args.position) - .await; - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let target_world = + if args.world.dimension.minecraft_name == Dimension::THE_END.minecraft_name { + args.server.get_world_from_dimension(&Dimension::OVERWORLD) + } else { + args.server.get_world_from_dimension(&Dimension::THE_END) + }; + if Arc::ptr_eq(&target_world, args.world) { + return; + } + tracing::info!( + "End portal collision at {:?}, targeting world {:?}", + args.position, + target_world.dimension.minecraft_name + ); + args.entity + .get_entity() + .try_use_portal(0, target_world, *args.position); } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let nbt = EndPortalBlockEntity::create_nbt(*args.position); - args.world.add_block_entity_nbt(*args.position, &nbt); + fn placed(&self, args: PlacedArgs<'_>) { + let nbt = EndPortalBlockEntity::create_nbt(*args.position); + args.world.add_block_entity_nbt(*args.position, &nbt); - args.world - .add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position))); - }) + args.world + .add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position))); } } diff --git a/crates/pumpkin/src/block/blocks/end_portal_frame.rs b/crates/pumpkin/src/block/blocks/end_portal_frame.rs index 6ec161b53..f2d18b7e5 100644 --- a/crates/pumpkin/src/block/blocks/end_portal_frame.rs +++ b/crates/pumpkin/src/block/blocks/end_portal_frame.rs @@ -3,7 +3,7 @@ use pumpkin_data::block_properties::BlockProperties; use pumpkin_macros::pumpkin_block; use crate::{ - block::{BlockBehaviour, BlockFuture, OnPlaceArgs}, + block::{BlockBehaviour, OnPlaceArgs}, entity::EntityBase, }; @@ -13,13 +13,10 @@ type EndPortalFrameProperties = pumpkin_data::block_properties::EndPortalFrameLi pub struct EndPortalFrameBlock; impl BlockBehaviour for EndPortalFrameBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block); - end_portal_frame_props.facing = - args.player.get_entity().get_horizontal_facing().opposite(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block); + end_portal_frame_props.facing = args.player.get_entity().get_horizontal_facing().opposite(); - end_portal_frame_props.to_state_id(args.block) - }) + end_portal_frame_props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/end_rod.rs b/crates/pumpkin/src/block/blocks/end_rod.rs index 05cdb8b37..144be283b 100644 --- a/crates/pumpkin/src/block/blocks/end_rod.rs +++ b/crates/pumpkin/src/block/blocks/end_rod.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::{BlockBehaviour, OnPlaceArgs}; use pumpkin_data::Block; use pumpkin_data::BlockStateId; @@ -10,24 +9,22 @@ use pumpkin_macros::pumpkin_block; pub struct EndRodBlock; impl BlockBehaviour for EndRodBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = EndRodLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = EndRodLikeProperties::default(args.block); - let blockstate = args - .world - .get_block_state_id(&args.position.offset(args.direction.to_offset())); + let blockstate = args + .world + .get_block_state_id(&args.position.offset(args.direction.to_offset())); - if Block::from_state_id(blockstate).eq(args.block) - && EndRodLikeProperties::from_state_id(blockstate, args.block).facing - == args.direction.to_facing().opposite() - { - props.facing = args.direction.to_facing(); - } else { - props.facing = args.direction.to_facing().opposite(); - } + if Block::from_state_id(blockstate).eq(args.block) + && EndRodLikeProperties::from_state_id(blockstate, args.block).facing + == args.direction.to_facing().opposite() + { + props.facing = args.direction.to_facing(); + } else { + props.facing = args.direction.to_facing().opposite(); + } - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/ender_chest.rs b/crates/pumpkin/src/block/blocks/ender_chest.rs index d10e87c6d..613d4e02d 100644 --- a/crates/pumpkin/src/block/blocks/ender_chest.rs +++ b/crates/pumpkin/src/block/blocks/ender_chest.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::block::entities::ender_chest::EnderChestBlockEntity; use crate::block::{ - BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, + BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, registry::BlockActionResult, }; use crate::world::World; @@ -57,77 +57,69 @@ impl ScreenHandlerFactory for EnderChestScreenFactory { pub struct EnderChestBlock; impl BlockBehaviour for EnderChestBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = LadderLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = LadderLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - // On the server, we don't need to do more because the client is responsible for that. - args.r#type == Self::LID_ANIMATION_EVENT_TYPE - }) + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + // On the server, we don't need to do more because the client is responsible for that. + args.r#type == Self::LID_ANIMATION_EVENT_TYPE } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if is_chest_blocked(args.world, args.position) { - return BlockActionResult::Success; - } + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if is_chest_blocked(args.world, args.position) { + return BlockActionResult::Success; + } - let block_entity = if let Some(be) = args.world.get_block_entity(args.position) { - be - } else { - let be = Arc::new(EnderChestBlockEntity::new(*args.position)); - args.world.add_block_entity(be.clone()); - be - }; + let block_entity = if let Some(be) = args.world.get_block_entity(args.position) { + be + } else { + let be = Arc::new(EnderChestBlockEntity::new(*args.position)); + args.world.add_block_entity(be.clone()); + be + }; - if let Some(block_entity) = block_entity - .as_any() - .downcast_ref::() - { - let inventory = args.player.ender_chest_inventory(); - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, - 1, - ) - .await; - args.player + if let Some(block_entity) = block_entity + .as_any() + .downcast_ref::() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + let tracker = block_entity.get_tracker(); + tokio::spawn(async move { + let inventory = player.ender_chest_inventory(); + player .open_handled_screen( &EnderChestScreenFactory { inventory: inventory.clone(), - tracker: Some(block_entity.get_tracker()), + tracker: Some(tracker), }, - Some(*args.position), + Some(pos), ) .await; - // TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true); - } + }); + // TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true); + } - BlockActionResult::Success - }) + BlockActionResult::Success } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_entity = EnderChestBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let block_entity = EnderChestBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(block_entity)); } } diff --git a/crates/pumpkin/src/block/blocks/falling.rs b/crates/pumpkin/src/block/blocks/falling.rs index 1007687df..b5bc05b8f 100644 --- a/crates/pumpkin/src/block/blocks/falling.rs +++ b/crates/pumpkin/src/block/blocks/falling.rs @@ -1,7 +1,7 @@ use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, GetStateForNeighborUpdateArgs, - OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, + PlacedArgs, }, entity::falling::FallingEntity, }; @@ -29,33 +29,29 @@ impl BlockMetadata for FallingBlock { } impl BlockBehaviour for FallingBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { // TODO: make delay configurable args.world .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal); - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - // TODO: make delay configurable - args.world - .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal); - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + // TODO: make delay configurable + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal); + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let (block, state) = args.world.get_block_and_state(&args.position.down()); - if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y { - return; - } - let state = args.world.get_block_state(args.position); - FallingEntity::replace_spawn(args.world, *args.position, state.id).await; - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let (block, state) = args.world.get_block_and_state(&args.position.down()); + if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y { + return; + } + let state = args.world.get_block_state(args.position); + FallingEntity::replace_spawn(args.world, *args.position, state.id); } } diff --git a/crates/pumpkin/src/block/blocks/farmland.rs b/crates/pumpkin/src/block/blocks/farmland.rs index 99ea3cfbc..dc2bd4550 100644 --- a/crates/pumpkin/src/block/blocks/farmland.rs +++ b/crates/pumpkin/src/block/blocks/farmland.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -28,123 +27,72 @@ type FarmlandProperties = FarmlandLikeProperties; pub struct FarmlandBlock; impl BlockBehaviour for FarmlandBlock { - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // TODO: push up entities + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + // TODO: push up entities + args.world.set_block_state( + args.position, + Block::DIRT.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if !can_place_at(args.world, args.position) { + return Block::DIRT.default_state.id; + } + args.block.default_state.id + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position) { args.world - .set_block_state( - args.position, - Block::DIRT.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - }) - } - - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - return Block::DIRT.default_state.id; - } - args.block.default_state.id - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { can_place_at(args.block_accessor, args.position) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // TODO: add rain check. Remember to check which one is most optimized. - if is_water_nearby(args.world, args.position) { - let mut props = FarmlandProperties::default(args.block); - props.moisture = 7; - let mut event = crate::plugin::block::moisture_change::MoistureChangeEvent { - block_pos: *args.position, - world: args.world.clone(), - new_moisture: 7, - cancelled: false, - }; - if let Some(server) = args.world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if !event.cancelled { - props.moisture = (event.new_moisture.clamp(0, 7)) as u8; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + fn random_tick(&self, args: RandomTickArgs<'_>) { + // TODO: add rain check. Remember to check which one is most optimized. + if is_water_nearby(args.world, args.position) { + let mut props = FarmlandProperties::default(args.block); + props.moisture = 7; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_NEIGHBORS, + ); + } else { + let state_id = args.world.get_block_state_id(args.position); + let mut props = FarmlandProperties::from_state_id(state_id, args.block); + if props.moisture == 0 { + if !args + .world + .get_block(&args.position.up()) + .has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND) + { + //TODO push entities up + args.world.set_block_state( + args.position, + Block::DIRT.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); } } else { - let state_id = args.world.get_block_state_id(args.position); - let mut props = FarmlandProperties::from_state_id(state_id, args.block); - if props.moisture == 0 { - if !args - .world - .get_block(&args.position.up()) - .has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND) - { - let mut event = - crate::plugin::api::events::block::block_fade::BlockFadeEvent::new( - *args.position, - &Block::DIRT, - ); - if let Some(server) = args.world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return; - } - - //TODO push entities up - args.world - .set_block_state( - args.position, - Block::DIRT.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } - } else { - let mut event = crate::plugin::block::moisture_change::MoistureChangeEvent { - block_pos: *args.position, - world: args.world.clone(), - new_moisture: (props.moisture as i32) - 1, - cancelled: false, - }; - if let Some(server) = args.world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if !event.cancelled { - props.moisture = (event.new_moisture.clamp(0, 7)) as u8; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } - } + props.moisture = (props.moisture as i32 - 1).clamp(0, 7) as u8; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_NEIGHBORS, + ); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/fence_gates.rs b/crates/pumpkin/src/block/blocks/fence_gates.rs index 87101a6b2..ead97d7ab 100644 --- a/crates/pumpkin/src/block/blocks/fence_gates.rs +++ b/crates/pumpkin/src/block/blocks/fence_gates.rs @@ -3,8 +3,7 @@ use std::sync::Arc; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, - OnNeighborUpdateArgs, OnPlaceArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, }; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -38,7 +37,7 @@ fn get_sound(block: &Block, open: bool) -> Sound { } } -pub async fn toggle_fence_gate( +pub fn toggle_fence_gate( world: &Arc, block_pos: &BlockPos, player: &Player, @@ -68,13 +67,11 @@ pub async fn toggle_fence_gate( *block_pos, ); - world - .set_block_state( - block_pos, - fence_gate_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + block_pos, + fence_gate_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); fence_gate_props.to_state_id(block) } @@ -82,43 +79,39 @@ pub async fn toggle_fence_gate( pub struct FenceGateBlock; impl BlockBehaviour for FenceGateBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut fence_gate_props = FenceGateProperties::default(args.block); - fence_gate_props.facing = args.player.get_entity().get_horizontal_facing(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut fence_gate_props = FenceGateProperties::default(args.block); + fence_gate_props.facing = args.player.get_entity().get_horizontal_facing(); - let powered = block_receives_redstone_power(args.world, args.position).await; - fence_gate_props.powered = powered; - fence_gate_props.open = powered; + let powered = block_receives_redstone_power(args.world, args.position); + fence_gate_props.powered = powered; + fence_gate_props.open = powered; - fence_gate_props.to_state_id(args.block) - }) + fence_gate_props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let fence_props = is_in_wall(&args); - fence_props.to_state_id(args.block) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let fence_props = is_in_wall(&args); + fence_props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - toggle_fence_gate(args.world, args.position, args.player).await; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { + toggle_fence_gate(args.world, args.position, args.player); BlockActionResult::Success - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let block_state = args.world.get_block_state(args.position); let mut fence_gate_props = FenceGateProperties::from_state_id(block_state.id, args.block); - let powered = block_receives_redstone_power(args.world, args.position).await; + let powered = block_receives_redstone_power(args.world, args.position); if powered == fence_gate_props.powered { return; @@ -136,14 +129,12 @@ impl BlockBehaviour for FenceGateBlock { ); } - args.world - .set_block_state( - args.position, - fence_gate_props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - }) + args.world.set_block_state( + args.position, + fence_gate_props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/fences.rs b/crates/pumpkin/src/block/blocks/fences.rs index 2fda0dbbd..ae6bcbec2 100644 --- a/crates/pumpkin/src/block/blocks/fences.rs +++ b/crates/pumpkin/src/block/blocks/fences.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; @@ -22,23 +21,19 @@ use crate::world::World; pub struct FenceBlock; impl BlockBehaviour for FenceBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut fence_props = FenceProperties::default(args.block); - fence_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut fence_props = FenceProperties::default(args.block); + fence_props.waterlogged = args.replacing.water_source(); - compute_fence_state(fence_props, args.world, args.block, args.position) - }) + compute_fence_state(fence_props, args.world, args.block, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let fence_props = FenceProperties::from_state_id(args.state_id, args.block); - compute_fence_state(fence_props, args.world, args.block, args.position) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let fence_props = FenceProperties::from_state_id(args.state_id, args.block); + compute_fence_state(fence_props, args.world, args.block, args.position) } } diff --git a/crates/pumpkin/src/block/blocks/fire/fire.rs b/crates/pumpkin/src/block/blocks/fire/fire.rs index d64408c03..a2fd74d90 100644 --- a/crates/pumpkin/src/block/blocks/fire/fire.rs +++ b/crates/pumpkin/src/block/blocks/fire/fire.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use crate::block::blocks::tnt::TNTBlock; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::world::World; @@ -148,7 +148,7 @@ impl FireBlock { ) } - async fn try_spreading_fire(&self, world: &Arc, pos: &BlockPos, chance: i32, age: u8) { + fn try_spreading_fire(&self, world: &Arc, pos: &BlockPos, chance: i32, age: u8) { let block = world.get_block(pos); let odds = Self::get_burn_odds(block); if rand::rng().random_range(0..chance) < odds { @@ -161,93 +161,70 @@ impl FireBlock { let mut fire_props = FireProperties::from_state_id(state_id, &Block::FIRE); fire_props.age = new_age; let new_state_id = fire_props.to_state_id(&Block::FIRE); - world - .set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + world.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS); } else { - let mut burn_event = crate::plugin::block::block_burn::BlockBurnEvent { - igniting_block: &Block::FIRE, - block: old_block, - cancelled: false, - }; - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut burn_event).await; - } - if burn_event.cancelled { - return; - } - world - .set_block_state( - pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); } if old_block == &Block::TNT { - TNTBlock::prime(world, pos).await; + TNTBlock::prime(world, pos); } } } } impl BlockBehaviour for FireBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.old_state_id == args.state_id { - // Already a fire - return; - } + fn placed(&self, args: PlacedArgs<'_>) { + if args.old_state_id == args.state_id { + // Already a fire + return; + } - let dimension = &args.world.dimension; - // First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla - if (dimension == &Dimension::OVERWORLD || dimension == &Dimension::THE_NETHER) - && let Some(portal) = - NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X) - { - portal.create(args.world).await; - return; - } + let dimension = &args.world.dimension; + // First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla + if (dimension == &Dimension::OVERWORLD || dimension == &Dimension::THE_NETHER) + && let Some(portal) = + NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X) + { + portal.create(args.world); + return; + } - args.world.schedule_block_tick( - args.block, - *args.position, - Self::get_fire_tick_delay() as u8, - TickPriority::Normal, - ); - }) + args.world.schedule_block_tick( + args.block, + *args.position, + Self::get_fire_tick_delay() as u8, + TickPriority::Normal, + ); } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - FireBlockBase::apply_fire_collision(args, false) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + FireBlockBase::apply_fire_collision(&args, false); } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if self.can_place_at(CanPlaceAtArgs { - server: None, - world: Some(args.world), - block_accessor: args.world, - block: &Block::FIRE, - state: Block::FIRE.default_state, - position: args.position, - direction: None, - player: None, - use_item_on: None, - }) { - let old_fire_props = FireProperties::from_state_id(args.state_id, &Block::FIRE); - let fire_state_id = - self.get_state_for_position(args.world, &Block::FIRE, args.position); - let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE); - fire_props.age = old_fire_props.age; - return fire_props.to_state_id(&Block::FIRE); - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if self.can_place_at(CanPlaceAtArgs { + server: None, + world: Some(args.world), + block_accessor: args.world, + block: &Block::FIRE, + state: Block::FIRE.default_state, + position: args.position, + direction: None, + player: None, + use_item_on: None, + }) { + self.get_state_for_position(args.world, args.block, args.position) + } else { Block::AIR.default_state.id - }) + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -259,247 +236,223 @@ impl BlockBehaviour for FireBlock { } #[expect(clippy::too_many_lines)] - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let (world, block, pos) = (args.world, args.block, args.position); + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let (world, block, pos) = (args.world, args.block, args.position); - // Schedule next tick first - world.schedule_block_tick( - block, - *pos, - Self::get_fire_tick_delay() as u8, - TickPriority::Normal, + // Schedule next tick first + world.schedule_block_tick( + block, + *pos, + Self::get_fire_tick_delay() as u8, + TickPriority::Normal, + ); + + // Check if fire can survive + if !self.can_place_at(CanPlaceAtArgs { + server: None, + world: Some(world), + block_accessor: world.as_ref(), + block, + state: block.default_state, + position: pos, + direction: None, + player: None, + use_item_on: None, + }) { + world.set_block_state( + pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, ); + return; + } - // Check if fire can survive - if !self.can_place_at(CanPlaceAtArgs { - server: None, - world: Some(world), - block_accessor: world.as_ref(), - block, - state: block.default_state, - position: pos, - direction: None, - player: None, - use_item_on: None, - }) { - world - .set_block_state( + let block_state = world.get_block_state(pos); + let block_below = world.get_block(&pos.down()); + + // Check for infiniburn blocks (depending on dimension) + let infiniburn = match world.dimension.id { + id if id == Dimension::OVERWORLD.id => { + block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_OVERWORLD) + } + id if id == Dimension::THE_NETHER.id => { + block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_NETHER) + } + id if id == Dimension::THE_END.id => { + block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_END) + } + _ => false, + }; + + let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE); + let age = fire_props.age; + + // Check if rain should extinguish the fire + if !infiniburn && Self::is_near_rain(world.as_ref(), pos) { + let rain_chance = 0.2 + (age as f32) * 0.03; + if rand::random::() < rain_chance { + world.set_block_state( + pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); + return; + } + } + + // Increment age + let random = (rand::rng().random_range(0..3) / 2) as u8; + let new_age = (age + random).min(15); + if new_age != age { + fire_props.age = new_age; + let new_state_id = fire_props.to_state_id(&Block::FIRE); + world.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS); + } + + if !infiniburn { + // Check if fire should extinguish due to lack of fuel + if !Self::are_blocks_around_flammable(world.as_ref(), pos) { + let block_below_state = world.get_block_state(&pos.down()); + if !block_below_state.is_side_solid(BlockDirection::Up) || new_age > 3 { + world.set_block_state( pos, Block::AIR.default_state.id, BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + ); + return; + } + } + + // At max age, fire has a chance to extinguish if not on flammable block + if new_age == 15 + && rand::rng().random_range(0..4) == 0 + && !Self::is_flammable(world.get_block_state(&pos.down())) + { + world.set_block_state( + pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); return; } + } - let block_state = world.get_block_state(pos); - let block_below = world.get_block(&pos.down()); + // Burn adjacent blocks + let extra = if Self::is_increased_burnout_biome(world, pos) { + -50 // Increases chance of block being destroyed + } else { + 0 + }; - // Check for infiniburn blocks (depending on dimension) - let infiniburn = match world.dimension.id { - id if id == Dimension::OVERWORLD.id => { - block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_OVERWORLD) - } - id if id == Dimension::THE_NETHER.id => { - block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_NETHER) - } - id if id == Dimension::THE_END.id => { - block_below.has_tag(&tag::Block::MINECRAFT_INFINIBURN_END) - } - _ => false, - }; + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::East.to_offset()), + 300 + extra, + new_age, + ); + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::West.to_offset()), + 300 + extra, + new_age, + ); + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::Down.to_offset()), + 250 + extra, + new_age, + ); + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::Up.to_offset()), + 250 + extra, + new_age, + ); + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::North.to_offset()), + 300 + extra, + new_age, + ); + self.try_spreading_fire( + world, + &pos.offset(BlockDirection::South.to_offset()), + 300 + extra, + new_age, + ); - let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE); - let age = fire_props.age; + // Respect the `fire_spread_radius_around_player` gamerule. + // -1 = disabled (allow unlimited spread), 0 = disabled (no spread), >0 = radius in blocks + let spread_radius = world + .level_info + .load() + .game_rules + .fire_spread_radius_around_player; - // Check if rain should extinguish the fire - if !infiniburn && Self::is_near_rain(world.as_ref(), pos) { - let rain_chance = 0.2 + (age as f32) * 0.03; - if rand::random::() < rain_chance { - world - .set_block_state( - pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - return; - } - } + // Try to spread fire to nearby air blocks + let difficulty = world.level_info.load().difficulty as i32; + for xx in -1..=1 { + for zz in -1..=1 { + for yy in -1..=4 { + if xx != 0 || yy != 0 || zz != 0 { + let offset_pos = pos.offset(Vector3::new(xx, yy, zz)); + let ignite_odds = self.get_burn_chance(world, &offset_pos); - // Increment age - let random = (rand::rng().random_range(0..3) / 2) as u8; - let new_age = (age + random).min(15); - if new_age != age { - fire_props.age = new_age; - let new_state_id = fire_props.to_state_id(&Block::FIRE); - world - .set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; - } - - if !infiniburn { - // Check if fire should extinguish due to lack of fuel - if !Self::are_blocks_around_flammable(world.as_ref(), pos) { - let block_below_state = world.get_block_state(&pos.down()); - if !block_below_state.is_side_solid(BlockDirection::Up) || new_age > 3 { - world - .set_block_state( - pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - return; - } - } - - // At max age, fire has a chance to extinguish if not on flammable block - if new_age == 15 - && rand::rng().random_range(0..4) == 0 - && !Self::is_flammable(world.get_block_state(&pos.down())) - { - world - .set_block_state( - pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - return; - } - } - - // Burn adjacent blocks - let extra = if Self::is_increased_burnout_biome(world, pos) { - -50 // Increases chance of block being destroyed - } else { - 0 - }; - - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::East.to_offset()), - 300 + extra, - new_age, - ) - .await; - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::West.to_offset()), - 300 + extra, - new_age, - ) - .await; - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::Down.to_offset()), - 250 + extra, - new_age, - ) - .await; - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::Up.to_offset()), - 250 + extra, - new_age, - ) - .await; - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::North.to_offset()), - 300 + extra, - new_age, - ) - .await; - self.try_spreading_fire( - world, - &pos.offset(BlockDirection::South.to_offset()), - 300 + extra, - new_age, - ) - .await; - - // Respect the `fire_spread_radius_around_player` gamerule. - // -1 = disabled (allow unlimited spread), 0 = disabled (no spread), >0 = radius in blocks - let spread_radius = world - .level_info - .load() - .game_rules - .fire_spread_radius_around_player; - - // Try to spread fire to nearby air blocks - let difficulty = world.level_info.load().difficulty as i32; - for xx in -1..=1 { - for zz in -1..=1 { - for yy in -1..=4 { - if xx != 0 || yy != 0 || zz != 0 { - let offset_pos = pos.offset(Vector3::new(xx, yy, zz)); - let ignite_odds = self.get_burn_chance(world, &offset_pos); - - if ignite_odds > 0 { - // Skip if spreding is disabled or if there are no players nearby - if spread_radius == 0 { + if ignite_odds > 0 { + // Skip if spreding is disabled or if there are no players nearby + if spread_radius == 0 { + continue; + } + if spread_radius != -1 { + let center = offset_pos.to_centered_f64(); + if world + .get_closest_player(center, spread_radius as f64) + .is_none() + { continue; } - if spread_radius != -1 { - let center = offset_pos.to_centered_f64(); - if world - .get_closest_player(center, spread_radius as f64) - .is_none() - { - continue; - } - } + } - // Calculate spread rate based on height - let rate = if yy > 1 { 100 + (yy - 1) * 100 } else { 100 }; + // Calculate spread rate based on height + let rate = if yy > 1 { 100 + (yy - 1) * 100 } else { 100 }; - // Calculate odds of spreading - let mut odds = - (ignite_odds + 40 + difficulty * 7) / (new_age as i32 + 30); + // Calculate odds of spreading + let mut odds = + (ignite_odds + 40 + difficulty * 7) / (new_age as i32 + 30); - // Reduce spread odds in certain biomes - if Self::is_increased_burnout_biome(world, &offset_pos) { - odds /= 2; // Fire spreads 50% slower - } + // Reduce spread odds in certain biomes + if Self::is_increased_burnout_biome(world, &offset_pos) { + odds /= 2; // Fire spreads 50% slower + } - if odds > 0 - && rand::rng().random_range(0..rate) <= odds - && !Self::is_near_rain(world.as_ref(), &offset_pos) - { - let spread_age = (new_age + rand::rng().random_range(0..5) / 4) - .min(15) - as u8; - let fire_state_id = self.get_state_for_position( - world.as_ref(), - block, - &offset_pos, - ); - let mut new_fire_props = - FireProperties::from_state_id(fire_state_id, &Block::FIRE); - new_fire_props.age = spread_age; + if odds > 0 + && rand::rng().random_range(0..rate) <= odds + && !Self::is_near_rain(world.as_ref(), &offset_pos) + { + let spread_age = + (new_age + rand::rng().random_range(0..5) / 4).min(15) as u8; + let fire_state_id = + self.get_state_for_position(world.as_ref(), block, &offset_pos); + let mut new_fire_props = + FireProperties::from_state_id(fire_state_id, &Block::FIRE); + new_fire_props.age = spread_age; - world - .set_block_state( - &offset_pos, - new_fire_props.to_state_id(&Block::FIRE), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } + world.set_block_state( + &offset_pos, + new_fire_props.to_state_id(&Block::FIRE), + BlockFlags::NOTIFY_NEIGHBORS, + ); } } } } } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { FireBlockBase::broken(args.world, *args.position); - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/fire/mod.rs b/crates/pumpkin/src/block/blocks/fire/mod.rs index 94b9ea509..f37b2ea67 100644 --- a/crates/pumpkin/src/block/blocks/fire/mod.rs +++ b/crates/pumpkin/src/block/blocks/fire/mod.rs @@ -11,7 +11,7 @@ use rand::RngExt; use soul_fire::SoulFireBlock; use crate::block::blocks::fire::fire::FireBlock; -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, OnEntityCollisionArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs}; use crate::entity::EntityBase; use crate::world::World; use crate::world::portal::nether::NetherPortal; @@ -117,46 +117,34 @@ impl FireBlockBase { } /// Shared fire collision behavior used by `fire` and `soul_fire`. - #[must_use] - #[allow(clippy::needless_pass_by_value)] - pub fn apply_fire_collision( - args: OnEntityCollisionArgs<'_>, - extra_damage_for_living: bool, - ) -> BlockFuture<'_, ()> { - Box::pin(async move { - let base_entity = args.entity.get_entity(); - if !base_entity.entity_type.fire_immune - && !base_entity.fire_immune.load(Ordering::Relaxed) - { - let ticks = base_entity.fire_ticks.load(Ordering::Relaxed); + pub fn apply_fire_collision(args: &OnEntityCollisionArgs<'_>, extra_damage_for_living: bool) { + let base_entity = args.entity.get_entity(); + if !base_entity.entity_type.fire_immune && !base_entity.fire_immune.load(Ordering::Relaxed) + { + let ticks = base_entity.fire_ticks.load(Ordering::Relaxed); - // Timer logic - if ticks < 0 { - base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed); - } else if base_entity.entity_type == &EntityType::PLAYER { - let rnd_ticks = rand::rng().random_range(1..3); - base_entity - .fire_ticks - .store(ticks + rnd_ticks, Ordering::Relaxed); - } - - // Apply fire ticks - if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 { - args.entity.set_on_fire_for(8.0); - } - - // Regular fire vs soul fire damage - if extra_damage_for_living { - base_entity - .damage(args.entity, 2.0, DamageType::IN_FIRE) - .await; - } else { - base_entity - .damage(args.entity, 1.0, DamageType::IN_FIRE) - .await; - } + // Timer logic + if ticks < 0 { + base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed); + } else if base_entity.entity_type == &EntityType::PLAYER { + let rnd_ticks = rand::rng().random_range(1..3); + base_entity + .fire_ticks + .store(ticks + rnd_ticks, Ordering::Relaxed); } - }) + + // Apply fire ticks + if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 { + args.entity.set_on_fire_for(8.0); + } + + // Regular fire vs soul fire damage + if extra_damage_for_living { + base_entity.damage(args.entity, 2.0, DamageType::IN_FIRE); + } else { + base_entity.damage(args.entity, 1.0, DamageType::IN_FIRE); + } + } } fn broken(world: &World, block_pos: BlockPos) { diff --git a/crates/pumpkin/src/block/blocks/fire/soul_fire.rs b/crates/pumpkin/src/block/blocks/fire/soul_fire.rs index 0824a569b..eb44007eb 100644 --- a/crates/pumpkin/src/block/blocks/fire/soul_fire.rs +++ b/crates/pumpkin/src/block/blocks/fire/soul_fire.rs @@ -3,9 +3,7 @@ use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_macros::pumpkin_block; -use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, -}; +use crate::block::{BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use super::FireBlockBase; use crate::block::OnEntityCollisionArgs; @@ -21,30 +19,28 @@ impl SoulFireBlock { } impl BlockBehaviour for SoulFireBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - FireBlockBase::apply_fire_collision(args, true) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + FireBlockBase::apply_fire_collision(&args, true); } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !Self::is_soul_base(args.world.get_block(&args.position.down())) { - return Block::AIR.default_state.id; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !Self::is_soul_base(args.world.get_block(&args.position.down())) { + return Block::AIR.default_state.id; + } - args.state_id - }) + args.state_id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { Self::is_soul_base(args.block_accessor.get_block(&args.position.down())) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { FireBlockBase::broken(args.world, *args.position); - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/fletching_table.rs b/crates/pumpkin/src/block/blocks/fletching_table.rs index 07d620713..c019b7ca8 100644 --- a/crates/pumpkin/src/block/blocks/fletching_table.rs +++ b/crates/pumpkin/src/block/blocks/fletching_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_macros::pumpkin_block; @@ -7,7 +7,7 @@ use pumpkin_macros::pumpkin_block; pub struct FletchingTableBlock; impl BlockBehaviour for FletchingTableBlock { - fn normal_use<'a>(&'a self, _args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { BlockActionResult::Pass }) + fn normal_use(&self, _args: NormalUseArgs<'_>) -> BlockActionResult { + BlockActionResult::Pass } } diff --git a/crates/pumpkin/src/block/blocks/flower_pots.rs b/crates/pumpkin/src/block/blocks/flower_pots.rs index e09707f7b..2c8b8027d 100644 --- a/crates/pumpkin/src/block/blocks/flower_pots.rs +++ b/crates/pumpkin/src/block/blocks/flower_pots.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs}; +use crate::block::{BlockBehaviour, RandomTickArgs, UseWithItemArgs}; use pumpkin_data::dimension::Dimension; use pumpkin_data::flower_pot_transformations::get_potted_item; use pumpkin_data::{Block, BlockId}; @@ -10,23 +10,18 @@ use pumpkin_world::world::BlockFlags; pub struct FlowerPotBlock; impl BlockBehaviour for FlowerPotBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { let item = args.item_stack.item; //Place the flower inside the pot let potted_block_id = get_potted_item(item.id); if args.block.eq(&Block::FLOWER_POT) { if potted_block_id != BlockId::AIR { - args.world - .set_block_state( - args.position, - Block::from_id(potted_block_id).default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + Block::from_id(potted_block_id).default_state.id, + BlockFlags::NOTIFY_ALL, + ); } return BlockActionResult::Success; } else if potted_block_id != BlockId::AIR { @@ -35,43 +30,35 @@ impl BlockBehaviour for FlowerPotBlock { } //get the flower + empty the pot - args.world - .set_block_state( - args.position, - Block::FLOWER_POT.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + Block::FLOWER_POT.default_state.id, + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Success - }) + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if (args.world.dimension.eq(&Dimension::OVERWORLD) - || args.world.dimension.eq(&Dimension::OVERWORLD_CAVES)) - && args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM) - && args.world.level_time.lock().await.time_of_day % 24000 > 14500 - { - args.world - .set_block_state( - args.position, - Block::POTTED_OPEN_EYEBLOSSOM.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM) - && args.world.level_time.lock().await.time_of_day % 24000 <= 14500 - { - args.world - .set_block_state( - args.position, - Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if (args.world.dimension.eq(&Dimension::OVERWORLD) + || args.world.dimension.eq(&Dimension::OVERWORLD_CAVES)) + && args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM) + && args.world.get_time_of_day() % 24000 > 14500 + { + args.world.set_block_state( + args.position, + Block::POTTED_OPEN_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM) + && args.world.get_time_of_day() % 24000 <= 14500 + { + args.world.set_block_state( + args.position, + Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/furnace.rs b/crates/pumpkin/src/block/blocks/furnace.rs index 8e235e01f..a484a46d0 100644 --- a/crates/pumpkin/src/block/blocks/furnace.rs +++ b/crates/pumpkin/src/block/blocks/furnace.rs @@ -20,8 +20,8 @@ use tokio::sync::Mutex; use crate::{ block::{ - BlockBehaviour, BlockFuture, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, - OnPlaceArgs, PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -82,79 +82,70 @@ impl ScreenHandlerFactory for FurnaceScreenFactory { pub struct FurnaceBlock; impl BlockBehaviour for FurnaceBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithFurnace as i32, - 1, - ) - .await; - let furnace_screen_factory = - FurnaceScreenFactory::new(inventory, property_delegate, experience_container); - args.player - .open_handled_screen(&furnace_screen_factory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.clone().get_inventory() + && let Some(property_delegate) = block_entity.clone().to_property_delegate() + && let Some(experience_container) = block_entity.to_experience_container() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithFurnace as i32, + 1, + ); + let furnace_screen_factory = + FurnaceScreenFactory::new(inventory, property_delegate, experience_container); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&furnace_screen_factory, Some(pos)) .await; + }); + } + crate::block::registry::BlockActionResult::Consume + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = FurnaceLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + + props.to_state_id(args.block) + } + + fn placed(&self, args: PlacedArgs<'_>) { + let furnace_block_entity = FurnaceBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(furnace_block_entity)); + } + + fn broken(&self, args: BrokenArgs<'_>) { + // Extract and drop accumulated XP as orbs before removing the block entity + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(experience_container) = block_entity.to_experience_container() + { + let xp = experience_container.extract_experience(); + if xp > 0 { + let pos = args.position.to_f64(); + ExperienceOrbEntity::spawn(args.world, pos, xp as u32); } - crate::block::registry::BlockActionResult::Consume - }) + } + args.world.remove_block_entity(args.position); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = FurnaceLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - - props.to_state_id(args.block) - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let furnace_block_entity = FurnaceBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(furnace_block_entity)); - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Extract and drop accumulated XP as orbs before removing the block entity - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(experience_container) = block_entity.to_experience_container() - { - let xp = experience_container.extract_experience(); - if xp > 0 { - let pos = args.position.to_f64(); - ExperienceOrbEntity::spawn(args.world, pos, xp as u32).await; - } - } - args.world.remove_block_entity(args.position); - }) - } - - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/glass_panes.rs b/crates/pumpkin/src/block/blocks/glass_panes.rs index 08c6ffbb9..c73d80ff7 100644 --- a/crates/pumpkin/src/block/blocks/glass_panes.rs +++ b/crates/pumpkin/src/block/blocks/glass_panes.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; @@ -20,23 +19,19 @@ use crate::world::World; pub struct GlassPaneBlock; impl BlockBehaviour for GlassPaneBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut pane_props = GlassPaneProperties::default(args.block); - pane_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut pane_props = GlassPaneProperties::default(args.block); + pane_props.waterlogged = args.replacing.water_source(); - compute_pane_state(pane_props, args.world, args.block, args.position) - }) + compute_pane_state(pane_props, args.world, args.block, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block); - compute_pane_state(pane_props, args.world, args.block, args.position) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block); + compute_pane_state(pane_props, args.world, args.block, args.position) } } diff --git a/crates/pumpkin/src/block/blocks/glazed_terracotta.rs b/crates/pumpkin/src/block/blocks/glazed_terracotta.rs index 80e8604fe..751e2adbc 100644 --- a/crates/pumpkin/src/block/blocks/glazed_terracotta.rs +++ b/crates/pumpkin/src/block/blocks/glazed_terracotta.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs}; +use crate::block::{BlockBehaviour, OnPlaceArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_macros::pumpkin_block_from_tag; @@ -7,16 +7,14 @@ use pumpkin_macros::pumpkin_block_from_tag; pub struct GlazedTerracottaBlock; impl BlockBehaviour for GlazedTerracottaBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut prop = WallTorchLikeProperties::default(args.block); - prop.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - prop.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut prop = WallTorchLikeProperties::default(args.block); + prop.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + prop.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/grass_block.rs b/crates/pumpkin/src/block/blocks/grass_block.rs index b12bdf66a..4d456146d 100644 --- a/crates/pumpkin/src/block/blocks/grass_block.rs +++ b/crates/pumpkin/src/block/blocks/grass_block.rs @@ -17,7 +17,7 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; use rand::RngExt; -use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs}; +use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs}; #[pumpkin_block("minecraft:grass_block")] pub struct GrassBlock; @@ -30,139 +30,116 @@ impl BlockBehaviour for GrassBlock { && args.world.get_block_state(&above).is_air() } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - const SPREAD_ATTEMPTS: i32 = 128; - const ATTEMPTS_PER_STEP: i32 = 16; - const FLOWER_CHANCE: i32 = 8; + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + const SPREAD_ATTEMPTS: i32 = 128; + const ATTEMPTS_PER_STEP: i32 = 16; + const FLOWER_CHANCE: i32 = 8; - let origin = args.position.up(); - for attempt in 0..SPREAD_ATTEMPTS { - let mut target = origin; - let mut valid = true; - for _ in 0..attempt / ATTEMPTS_PER_STEP { - let offset_x = rand::rng().random_range(0..3) - 1; - let offset_y = - ((rand::rng().random_range(0..3) - 1) * rand::rng().random_range(0..3)) / 2; - let offset_z = rand::rng().random_range(0..3) - 1; - target = BlockPos::new( - target.0.x + offset_x, - target.0.y + offset_y, - target.0.z + offset_z, - ); + let origin = args.position.up(); + for attempt in 0..SPREAD_ATTEMPTS { + let mut target = origin; + let mut valid = true; + for _ in 0..attempt / ATTEMPTS_PER_STEP { + let offset_x = rand::rng().random_range(0..3) - 1; + let offset_y = + ((rand::rng().random_range(0..3) - 1) * rand::rng().random_range(0..3)) / 2; + let offset_z = rand::rng().random_range(0..3) - 1; + target = BlockPos::new( + target.0.x + offset_x, + target.0.y + offset_y, + target.0.z + offset_z, + ); - if !args.world.is_loaded(&target) - || args.world.get_block(&target.down()) != args.block - || args.world.get_block_state(&target).is_full_cube() - { - valid = false; - break; - } + if !args.world.is_loaded(&target) + || args.world.get_block(&target.down()) != args.block + || args.world.get_block_state(&target).is_full_cube() + { + valid = false; + break; } + } - if !valid { + if !valid { + continue; + } + let target_state = args.world.get_block_state(&target); + if Block::from_state_id(target_state.id) == &Block::SHORT_GRASS + && rand::rng().random_range(0..10) == 0 + { + let above = target.up(); + if args.world.is_in_height_limit(above.0.y) + && args.world.is_loaded(&above) + && args.world.get_block_state(&above).is_air() + { + place_tall_grass(args.world, target); + } + } else if target_state.is_air() && args.world.is_in_height_limit(target.0.y) { + let selected = if rand::rng().random_range(0..FLOWER_CHANCE) == 0 { + biome_bonemeal_state(args.world, target) + } else { + Some((Block::SHORT_GRASS.default_state, false)) + }; + let Some((state, schedule_tick)) = selected else { + continue; + }; + let placed_block = Block::from_state_id(state.id); + if !args.world.block_registry.can_place_at( + None, + Some(args.world), + args.world.as_ref(), + None, + placed_block, + state, + &target, + None, + None, + ) { continue; } - let target_state = args.world.get_block_state(&target); - if Block::from_state_id(target_state.id) == &Block::SHORT_GRASS - && rand::rng().random_range(0..10) == 0 - { - let above = target.up(); - if args.world.is_in_height_limit(above.0.y) - && args.world.is_loaded(&above) - && args.world.get_block_state(&above).is_air() - { - place_tall_grass(args.world, target).await; - } - } else if target_state.is_air() && args.world.is_in_height_limit(target.0.y) { - let selected = if rand::rng().random_range(0..FLOWER_CHANCE) == 0 { - biome_bonemeal_state(args.world, target) - } else { - Some((Block::SHORT_GRASS.default_state, false)) - }; - let Some((state, schedule_tick)) = selected else { - continue; - }; - let placed_block = Block::from_state_id(state.id); - if !args.world.block_registry.can_place_at( - None, - Some(args.world), - args.world.as_ref(), - None, - placed_block, - state, - &target, - None, - None, - ) { - continue; - } - if placed_block == &Block::TALL_GRASS - && (!args.world.is_loaded(&target.up()) - || !args.world.get_block_state(&target.up()).is_air()) - { - continue; - } + args.world + .set_block_state(&target, state.id, BlockFlags::NOTIFY_LISTENERS); + if schedule_tick { args.world - .set_block_state(&target, state.id, BlockFlags::NOTIFY_LISTENERS) - .await; - if schedule_tick { - args.world.schedule_block_tick( - placed_block, - target, - 1, - TickPriority::Normal, - ); - } - if placed_block == &Block::TALL_GRASS { - place_tall_grass_upper(args.world, target, state.id).await; - } + .schedule_block_tick(placed_block, target, 1, TickPriority::Normal); } } - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let block_above = args.world.get_block(&args.position.up()); - let mut props = - GrassBlockLikeProperties::from_state_id(args.state_id, &Block::GRASS_BLOCK); - let should_be_snowy = block_above.has_tag(&tag::Block::MINECRAFT_SNOW); - if props.snowy == should_be_snowy { - return args.state_id; - } - props.snowy = should_be_snowy; + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let block_above = args.world.get_block(&args.position.up()); + let mut props = GrassBlockLikeProperties::from_state_id(args.state_id, &Block::GRASS_BLOCK); + let should_be_snowy = block_above.has_tag(&tag::Block::MINECRAFT_SNOW); + if props.snowy == should_be_snowy { + return args.state_id; + } + props.snowy = should_be_snowy; - props.to_state_id(&Block::GRASS_BLOCK) - }) + props.to_state_id(&Block::GRASS_BLOCK) } } -async fn place_tall_grass(world: &std::sync::Arc, position: BlockPos) { +fn place_tall_grass(world: &std::sync::Arc, position: BlockPos) { let state = Block::TALL_GRASS.default_state.id; - world - .set_block_state(&position, state, BlockFlags::NOTIFY_LISTENERS) - .await; - place_tall_grass_upper(world, position, state).await; + world.set_block_state(&position, state, BlockFlags::NOTIFY_LISTENERS); + place_tall_grass_upper(world, position, state); } -async fn place_tall_grass_upper( +fn place_tall_grass_upper( world: &std::sync::Arc, position: BlockPos, lower_state: BlockStateId, ) { let mut props = TallSeagrassLikeProperties::from_state_id(lower_state, &Block::TALL_GRASS); props.half = DoubleBlockHalf::Upper; - world - .set_block_state( - &position.up(), - props.to_state_id(&Block::TALL_GRASS), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + &position.up(), + props.to_state_id(&Block::TALL_GRASS), + BlockFlags::NOTIFY_LISTENERS, + ); } fn biome_bonemeal_state( diff --git a/crates/pumpkin/src/block/blocks/grindstone.rs b/crates/pumpkin/src/block/blocks/grindstone.rs index 402382672..1b62320a6 100644 --- a/crates/pumpkin/src/block/blocks/grindstone.rs +++ b/crates/pumpkin/src/block/blocks/grindstone.rs @@ -6,8 +6,8 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; +use crate::block::BlockBehaviour; use crate::block::CanPlaceAtArgs; -use crate::block::{BlockBehaviour, BlockFuture}; use crate::block::{GetStateForNeighborUpdateArgs, OnPlaceArgs}; use super::abstract_wall_mounting::WallMountedBlock; @@ -16,15 +16,13 @@ use super::abstract_wall_mounting::WallMountedBlock; pub struct GrindstoneBlock; impl BlockBehaviour for GrindstoneBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block); - (props.face, props.facing) = - WallMountedBlock::get_placement_face(self, args.player, args.direction); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block); + (props.face, props.facing) = + WallMountedBlock::get_placement_face(self, args.player, args.direction); - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -36,19 +34,19 @@ impl BlockBehaviour for GrindstoneBlock { WallMountedBlock::can_place_at(self, args.block_accessor, args.position, direction) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + WallMountedBlock::get_state_for_neighbor_update(self, args) } } impl WallMountedBlock for GrindstoneBlock { - fn can_place_at<'a>( - &'a self, - _world: &'a dyn BlockAccessor, - _pos: &'a BlockPos, + fn can_place_at( + &self, + _world: &dyn BlockAccessor, + _pos: &BlockPos, _direction: BlockDirection, ) -> bool { true diff --git a/crates/pumpkin/src/block/blocks/hay.rs b/crates/pumpkin/src/block/blocks/hay.rs index 504b19dba..558547b82 100644 --- a/crates/pumpkin/src/block/blocks/hay.rs +++ b/crates/pumpkin/src/block/blocks/hay.rs @@ -1,18 +1,14 @@ use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, BlockFuture, OnLandedUponArgs}; +use crate::block::{BlockBehaviour, OnLandedUponArgs}; #[pumpkin_block("minecraft:hay_block")] pub struct HayBlock; impl BlockBehaviour for HayBlock { - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() { - living - .handle_fall_damage(args.entity, args.fall_distance, 0.2) - .await; - } - }) + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() { + living.handle_fall_damage(args.entity, args.fall_distance, 0.2); + } } } diff --git a/crates/pumpkin/src/block/blocks/hopper.rs b/crates/pumpkin/src/block/blocks/hopper.rs index 40d974458..4d95d5614 100644 --- a/crates/pumpkin/src/block/blocks/hopper.rs +++ b/crates/pumpkin/src/block/blocks/hopper.rs @@ -1,9 +1,7 @@ use std::sync::Arc; use crate::block::blocks::redstone::block_receives_redstone_power; -use crate::block::{ - BlockFuture, GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, -}; +use crate::block::{GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -58,93 +56,76 @@ pub struct HopperBlock; type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties; impl BlockBehaviour for HopperBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InspectHopper as i32, - 1, - ) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InspectHopper as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&HopperBlockScreenFactory(inventory), Some(pos)) .await; - args.player - .open_handled_screen(&HopperBlockScreenFactory(inventory), Some(*args.position)) - .await; - } + }); + } - BlockActionResult::Success - }) + BlockActionResult::Success } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = HopperLikeProperties::default(args.block); - props.facing = match args.direction { - BlockDirection::North => FacingHopper::North, - BlockDirection::East => FacingHopper::East, - BlockDirection::South => FacingHopper::South, - BlockDirection::West => FacingHopper::West, - BlockDirection::Up | BlockDirection::Down => FacingHopper::Down, - }; - props.enabled = true; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = HopperLikeProperties::default(args.block); + props.facing = match args.direction { + BlockDirection::North => FacingHopper::North, + BlockDirection::East => FacingHopper::East, + BlockDirection::South => FacingHopper::South, + BlockDirection::West => FacingHopper::West, + BlockDirection::Up | BlockDirection::Down => FacingHopper::Down, + }; + props.enabled = true; + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let props = HopperLikeProperties::from_state_id(args.state_id, args.block); - let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing); - args.world.add_block_entity(Arc::new(hopper_block_entity)); - if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) { - check_powered_state(args.world, args.position, args.state_id, args.block).await; - } - }) + fn placed(&self, args: PlacedArgs<'_>) { + let props = HopperLikeProperties::from_state_id(args.state_id, args.block); + let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing); + args.world.add_block_entity(Arc::new(hopper_block_entity)); + if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) { + check_powered_state(args.world, args.position, args.state_id, args.block); + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - check_powered_state( - args.world, - args.position, - args.world.get_block_state_id(args.position), - args.block, - ) - .await; - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + check_powered_state( + args.world, + args.position, + args.world.get_block_state_id(args.position), + args.block, + ); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } -async fn check_powered_state( - world: &Arc, - pos: &BlockPos, - state_id: BlockStateId, - block: &Block, -) { - let signal = !block_receives_redstone_power(world, pos).await; +fn check_powered_state(world: &Arc, pos: &BlockPos, state_id: BlockStateId, block: &Block) { + let signal = !block_receives_redstone_power(world, pos); let mut state = HopperLikeProperties::from_state_id(state_id, block); if signal != state.enabled { state.enabled = signal; - world - .set_block_state(pos, state.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(pos, state.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); } } diff --git a/crates/pumpkin/src/block/blocks/ice.rs b/crates/pumpkin/src/block/blocks/ice.rs index 01aa2247a..2e284c6db 100644 --- a/crates/pumpkin/src/block/blocks/ice.rs +++ b/crates/pumpkin/src/block/blocks/ice.rs @@ -9,26 +9,21 @@ use pumpkin_world::world::BlockFlags; use rand::RngExt; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, - OnScheduledTickArgs, PlacedArgs, RandomTickArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, + PlacedArgs, RandomTickArgs, }; use crate::world::World; /// Melts ice at the given position into water (or removes it in ultrawarm dimensions like the Nether). -pub async fn melt(world: &Arc, position: &BlockPos) { +pub fn melt(world: &Arc, position: &BlockPos) { if world.dimension == Dimension::THE_NETHER { - world - .set_block_state(position, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(position, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); } else { - world - .set_block_state( - position, - Block::WATER.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - world.update_neighbors(position, None).await; + world.set_block_state( + position, + Block::WATER.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } } @@ -47,20 +42,14 @@ pub fn fewer_neighbors_than(world: &World, pos: &BlockPos, limit: usize) -> bool true } -/// Slightly melts the frosted ice at `pos`. -/// -/// Increments its age if `age < 3`, or completely melts it if `age >= 3`. -/// Returns `true` if it completely melted, or `false` if it just aged. -async fn slightly_melt(world: &Arc, pos: &BlockPos, block: &Block, age: u8) -> bool { +fn slightly_melt(world: &Arc, pos: &BlockPos, block: &Block, age: u8) -> bool { if age < 3 { let mut new_props = NetherWartLikeProperties::default(block); new_props.r#age = age + 1; - world - .set_block_state(pos, new_props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, new_props.to_state_id(block), BlockFlags::NOTIFY_ALL); false } else { - melt(world, pos).await; + melt(world, pos); true } } @@ -74,15 +63,17 @@ impl BlockMetadata for IceBlock { } impl BlockBehaviour for IceBlock { - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let held_item = args.player.inventory().held_item().await; + fn broken(&self, args: BrokenArgs<'_>) { + { + let held_item = args.player.inventory().held_item(); let has_silk_touch = held_item.get_enchantment_level(&Enchantment::SILK_TOUCH) > 0; if !has_silk_touch { if args.world.dimension == Dimension::THE_NETHER { - args.world - .set_block_state(args.position, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + args.world.set_block_state( + args.position, + BlockStateId::AIR, + BlockFlags::NOTIFY_ALL, + ); return; } @@ -93,26 +84,22 @@ impl BlockBehaviour for IceBlock { || below_state.is_liquid() || below_state.is_solid() { - args.world - .set_block_state( - args.position, - Block::WATER.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + Block::WATER.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } } - }) + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let block_light = args.world.get_block_light_level(args.position).unwrap_or(0); - if block_light > (11u8.saturating_sub(state.opacity)) { - melt(args.world, args.position).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let block_light = args.world.get_block_light_level(args.position).unwrap_or(0); + if block_light > (11u8.saturating_sub(state.opacity)) { + melt(args.world, args.position); + } } } @@ -125,85 +112,75 @@ impl BlockMetadata for FrostedIceBlock { } impl BlockBehaviour for FrostedIceBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let delay = rand::rng().random_range(60..=120); args.world .schedule_block_tick(args.block, *args.position, delay, TickPriority::Normal); - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let should_check_melt = rand::rng().random_range(0..3) == 0 - || fewer_neighbors_than(args.world, args.position, 4); + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let should_check_melt = rand::rng().random_range(0..3) == 0 + || fewer_neighbors_than(args.world, args.position, 4); - if should_check_melt { - let state_id = args.world.get_block_state_id(args.position); - let state = args.world.get_block_state(args.position); - let props = NetherWartLikeProperties::from_state_id(state_id, args.block); - let age = props.r#age; + if should_check_melt { + let state_id = args.world.get_block_state_id(args.position); + let state = args.world.get_block_state(args.position); + let props = NetherWartLikeProperties::from_state_id(state_id, args.block); + let age = props.r#age; - let brightness = if args.world.dimension == Dimension::THE_END { - args.world.get_block_light_level(args.position).unwrap_or(0) - } else { - args.world.get_max_local_raw_brightness(args.position) - }; + let brightness = if args.world.dimension == Dimension::THE_END { + args.world.get_block_light_level(args.position).unwrap_or(0) + } else { + args.world.get_max_local_raw_brightness(args.position) + }; - let threshold = 11u8.saturating_sub(age).saturating_sub(state.opacity); - if brightness > threshold - && slightly_melt(args.world, args.position, args.block, age).await - { - for dir in BlockDirection::all() { - let neighbor_pos = args.position.offset(dir.to_offset()); - let (neighbor_block, neighbor_state_id) = - args.world.get_block_and_state_id(&neighbor_pos); - if neighbor_block == &Block::FROSTED_ICE { - let neighbor_props = NetherWartLikeProperties::from_state_id( - neighbor_state_id, + let threshold = 11u8.saturating_sub(age).saturating_sub(state.opacity); + if brightness > threshold && slightly_melt(args.world, args.position, args.block, age) { + for dir in BlockDirection::all() { + let neighbor_pos = args.position.offset(dir.to_offset()); + let (neighbor_block, neighbor_state_id) = + args.world.get_block_and_state_id(&neighbor_pos); + if neighbor_block == &Block::FROSTED_ICE { + let neighbor_props = NetherWartLikeProperties::from_state_id( + neighbor_state_id, + neighbor_block, + ); + if !slightly_melt( + args.world, + &neighbor_pos, + neighbor_block, + neighbor_props.r#age, + ) { + let delay = rand::rng().random_range(20..=40); + args.world.schedule_block_tick( neighbor_block, + neighbor_pos, + delay, + TickPriority::Normal, ); - if !slightly_melt( - args.world, - &neighbor_pos, - neighbor_block, - neighbor_props.r#age, - ) - .await - { - let delay = rand::rng().random_range(20..=40); - args.world.schedule_block_tick( - neighbor_block, - neighbor_pos, - delay, - TickPriority::Normal, - ); - } } } - return; } + return; } + } - let delay = rand::rng().random_range(20..=40); - args.world - .schedule_block_tick(args.block, *args.position, delay, TickPriority::Normal); - }) + let delay = rand::rng().random_range(20..=40); + args.world + .schedule_block_tick(args.block, *args.position, delay, TickPriority::Normal); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.source_block == &Block::FROSTED_ICE - && fewer_neighbors_than(args.world, args.position, 2) - { - melt(args.world, args.position).await; - } - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if args.source_block == &Block::FROSTED_ICE + && fewer_neighbors_than(args.world, args.position, 2) + { + melt(args.world, args.position); + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - IceBlock.broken(args).await; - }) + fn broken(&self, args: BrokenArgs<'_>) { + IceBlock.broken(args); } } diff --git a/crates/pumpkin/src/block/blocks/infested.rs b/crates/pumpkin/src/block/blocks/infested.rs index 2269db3ef..3d4fc62f3 100644 --- a/crates/pumpkin/src/block/blocks/infested.rs +++ b/crates/pumpkin/src/block/blocks/infested.rs @@ -4,16 +4,16 @@ use pumpkin_data::entity::EntityType; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::GameMode; +use crate::block::BlockBehaviour; use crate::block::BrokenArgs; -use crate::block::{BlockBehaviour, BlockFuture}; use crate::entity::Entity; #[pumpkin_block_from_tag("c:cobblestones/infested")] pub struct InfestedBlock; impl BlockBehaviour for InfestedBlock { - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async { + fn broken(&self, args: BrokenArgs<'_>) { + { // TODO: ugly fix, use onStacksDropped if args.player.gamemode.load() == GameMode::Creative { return; @@ -24,7 +24,7 @@ impl BlockBehaviour for InfestedBlock { &EntityType::SILVERFISH, ); - args.world.spawn_entity(Arc::new(entity)).await; - }) + args.world.spawn_entity(Arc::new(entity)); + } } } diff --git a/crates/pumpkin/src/block/blocks/iron_bars.rs b/crates/pumpkin/src/block/blocks/iron_bars.rs index 8d10552f9..7bc76e1e7 100644 --- a/crates/pumpkin/src/block/blocks/iron_bars.rs +++ b/crates/pumpkin/src/block/blocks/iron_bars.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; @@ -20,23 +19,19 @@ use crate::world::World; pub struct IronBarsBlock; impl BlockBehaviour for IronBarsBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut bars_props = IronBarsProperties::default(args.block); - bars_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut bars_props = IronBarsProperties::default(args.block); + bars_props.waterlogged = args.replacing.water_source(); - compute_bars_state(bars_props, args.world, args.block, args.position) - }) + compute_bars_state(bars_props, args.world, args.block, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block); - compute_bars_state(bars_props, args.world, args.block, args.position) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block); + compute_bars_state(bars_props, args.world, args.block, args.position) } } diff --git a/crates/pumpkin/src/block/blocks/jigsaw.rs b/crates/pumpkin/src/block/blocks/jigsaw.rs index bedcf1b73..1a65aa0a5 100644 --- a/crates/pumpkin/src/block/blocks/jigsaw.rs +++ b/crates/pumpkin/src/block/blocks/jigsaw.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::block::entities::jigsaw_block::JigsawBlockEntity; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs, PlacedArgs}; use crate::entity::EntityBase; use pumpkin_data::block_properties::{ BlockProperties, HorizontalFacing, JigsawLikeProperties, Orientation, @@ -100,24 +100,21 @@ impl JigsawBlock { } impl BlockBehaviour for JigsawBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = JigsawLikeProperties::default(args.block); - let front = args.direction; - let top = if front == BlockDirection::Up || front == BlockDirection::Down { - horizontal_facing_to_dir(args.player.get_entity().get_horizontal_facing()) - .opposite() - } else { - BlockDirection::Up - }; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = JigsawLikeProperties::default(args.block); + let front = args.direction; + let top = if front == BlockDirection::Up || front == BlockDirection::Down { + horizontal_facing_to_dir(args.player.get_entity().get_horizontal_facing()).opposite() + } else { + BlockDirection::Up + }; - props.r#orientation = Self::from_front_top(front, top); - props.to_state_id(args.block) - }) + props.r#orientation = Self::from_front_top(front, top); + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { if args.player.permission_lvl.load() < PermissionLvl::Two { return BlockActionResult::Pass; } @@ -129,14 +126,14 @@ impl BlockBehaviour for JigsawBlock { }; args.world.update_block_entity(&block_entity); BlockActionResult::SuccessServer - }) + } } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = JigsawBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } fn mirror( diff --git a/crates/pumpkin/src/block/blocks/jukebox.rs b/crates/pumpkin/src/block/blocks/jukebox.rs index 937b36f7d..e354228fa 100644 --- a/crates/pumpkin/src/block/blocks/jukebox.rs +++ b/crates/pumpkin/src/block/blocks/jukebox.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::block::entities::jukebox::JukeboxBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, + BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, NormalUseArgs, OnStateReplacedArgs, PlacedArgs, UseWithItemArgs, }; use crate::entity::Entity; @@ -33,29 +33,22 @@ impl JukeboxBlock { JukeboxLikeProperties::from_state_id(state_id, block).has_record } - async fn set_record_state( - has_record: bool, - block: &Block, - position: &BlockPos, - world: &Arc, - ) { + fn set_record_state(has_record: bool, block: &Block, position: &BlockPos, world: &Arc) { let new_state = JukeboxLikeProperties { has_record }; - world - .set_block_state( - position, - new_state.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + position, + new_state.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); } /// Drops the record from the jukebox - matches vanilla's `JukeboxBlockEntity.dropRecord()` /// Spawns item at (pos + 0.5, pos + 1.01, pos + 0.5) with horizontal random offset - async fn drop_record(position: &BlockPos, world: &Arc) { + fn drop_record(position: &BlockPos, world: &Arc) { if let Some(block_entity) = world.get_block_entity(position) && let Some(jukebox_entity) = block_entity.as_any().downcast_ref::() { - let record = jukebox_entity.clear_record().await; + let record = jukebox_entity.clear_record(); if !record.is_empty() { // Vanilla: Vec3d.add(pos, 0.5, 1.01, 0.5).addHorizontalRandom(random, 0.7F) // addHorizontalRandom adds random in range [-0.35, 0.35] to x and z @@ -68,14 +61,14 @@ impl JukeboxBlock { let entity = Entity::new(world.clone(), spawn_pos, &EntityType::ITEM); // Vanilla: setToDefaultPickupDelay() = 10 ticks let item_entity = Arc::new(ItemEntity::new(entity, record)); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); } } } /// Stops the music and updates block state - async fn stop_playing(block: &Block, position: &BlockPos, world: &Arc) { - Self::set_record_state(false, block, position, world).await; + fn stop_playing(block: &Block, position: &BlockPos, world: &Arc) { + Self::set_record_state(false, block, position, world); world.sync_world_event(WorldEvent::SoundStopJukeboxSong, *position, 0); } @@ -87,168 +80,137 @@ impl JukeboxBlock { impl BlockBehaviour for JukeboxBlock { /// Called when the jukebox is placed - creates the block entity - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_entity = JukeboxBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let block_entity = JukeboxBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(block_entity)); } /// Called when player right-clicks with empty hand or non-disc item /// Vanilla: `JukeboxBlock.onUse()` - drops record if present - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state(args.position).id; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.position).id; - // Vanilla: if (state.get(HAS_RECORD) && world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv) - if Self::has_record_state(args.block, state_id) { - // Drop the record - Self::drop_record(args.position, args.world).await; - // Stop the music and update block state - Self::stop_playing(args.block, args.position, args.world).await; - return BlockActionResult::Success; - } + // Vanilla: if (state.get(HAS_RECORD) && world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv) + if Self::has_record_state(args.block, state_id) { + // Drop the record + Self::drop_record(args.position, args.world); + // Stop the music and update block state + Self::stop_playing(args.block, args.position, args.world); + return BlockActionResult::Success; + } - BlockActionResult::Pass - }) + BlockActionResult::Pass } /// Called when player right-clicks with an item /// Vanilla: `JukeboxBlock.onUseWithItem()` -> `JukeboxPlayableComponent.tryPlayStack()` - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let world = args.world; - let state_id = world.get_block_state(args.position).id; + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let world = args.world; + let state_id = world.get_block_state(args.position).id; - // Vanilla: if (state.get(HAS_RECORD)) return PASS_TO_DEFAULT_BLOCK_ACTION - if Self::has_record_state(args.block, state_id) { - return BlockActionResult::PassToDefaultBlockAction; - } + // Vanilla: if (state.get(HAS_RECORD)) return PASS_TO_DEFAULT_BLOCK_ACTION + if Self::has_record_state(args.block, state_id) { + return BlockActionResult::PassToDefaultBlockAction; + } - let item_stack = &mut *args.item_stack; + let item_stack = &mut *args.item_stack; - // Vanilla: JukeboxPlayableComponent lv = stack.get(DataComponentTypes.JUKEBOX_PLAYABLE) - let jukebox_playable = item_stack - .get_data_component::() - .map(|i| i.song); + // Vanilla: JukeboxPlayableComponent lv = stack.get(DataComponentTypes.JUKEBOX_PLAYABLE) + let jukebox_playable = item_stack + .get_data_component::() + .map(|i| i.song); - // Vanilla: if (lv == null) return PASS_TO_DEFAULT_BLOCK_ACTION - let Some(jukebox_playable) = jukebox_playable else { - return BlockActionResult::PassToDefaultBlockAction; - }; + // Vanilla: if (lv == null) return PASS_TO_DEFAULT_BLOCK_ACTION + let Some(jukebox_playable) = jukebox_playable else { + return BlockActionResult::PassToDefaultBlockAction; + }; - let Some(song_name) = jukebox_playable.split(':').nth(1) else { - return BlockActionResult::PassToDefaultBlockAction; - }; + let Some(song_name) = jukebox_playable.split(':').nth(1) else { + return BlockActionResult::PassToDefaultBlockAction; + }; - let Some(jukebox_song) = JukeboxSong::from_name(song_name) else { - error!("Jukebox playable song not registered: {song_name}"); - return BlockActionResult::PassToDefaultBlockAction; - }; + let Some(jukebox_song) = JukeboxSong::from_name(song_name) else { + error!("Jukebox playable song not registered: {song_name}"); + return BlockActionResult::PassToDefaultBlockAction; + }; - // Vanilla: ItemStack lv3 = stack.splitUnlessCreative(1, player) - let record = item_stack.split_unless_creative(args.player.gamemode.load(), 1); + // Vanilla: ItemStack lv3 = stack.splitUnlessCreative(1, player) + let record = item_stack.split_unless_creative(args.player.gamemode.load(), 1); - // Vanilla: lv4.setStack(lv3) - if let Some(block_entity) = world.get_block_entity(args.position) - && let Some(jukebox_entity) = - block_entity.as_any().downcast_ref::() - { - jukebox_entity.set_record(record).await; - // Start tracking playback with song duration - jukebox_entity.start_playing(jukebox_song.length_in_ticks()); - } + // Vanilla: lv4.setStack(lv3) + if let Some(block_entity) = world.get_block_entity(args.position) + && let Some(jukebox_entity) = block_entity.as_any().downcast_ref::() + { + jukebox_entity.set_record(record); + // Start tracking playback with song duration + jukebox_entity.start_playing(jukebox_song.length_in_ticks()); + } - // Update block state to has_record = true - Self::set_record_state(true, args.block, args.position, world).await; + // Update block state to has_record = true + Self::set_record_state(true, args.block, args.position, world); - // Start playing the music (client-side audio) - Self::start_playing(args.position, world, jukebox_song.get_id()); + // Start playing the music (client-side audio) + Self::start_playing(args.position, world, jukebox_song.get_id()); - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::PlayRecord as i32, - 1, - ) - .await; + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::PlayRecord as i32, + 1, + ); - // TODO: world.emitGameEvent(GameEvent.BLOCK_CHANGE, pos, ...) + // TODO: world.emitGameEvent(GameEvent.BLOCK_CHANGE, pos, ...) - BlockActionResult::Success - }) + BlockActionResult::Success } /// Called when the jukebox is broken - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Drop the record if there is one - Self::drop_record(args.position, args.world).await; - // Stop the music - args.world - .sync_world_event(WorldEvent::SoundStopJukeboxSong, *args.position, 0); - }) + fn broken(&self, args: BrokenArgs<'_>) { + // Drop the record if there is one + Self::drop_record(args.position, args.world); + // Stop the music + args.world + .sync_world_event(WorldEvent::SoundStopJukeboxSong, *args.position, 0); } /// Vanilla: `JukeboxBlock.onStateReplaced()` -> `ItemScatterer.onStateReplaced()` - fn on_state_replaced<'a>(&'a self, _args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Vanilla calls ItemScatterer.onStateReplaced which updates comparators - // TODO: world.updateComparators(pos, block) when implemented - }) + fn on_state_replaced(&self, _args: OnStateReplacedArgs<'_>) { + // Vanilla calls ItemScatterer.onStateReplaced which updates comparators + // TODO: world.updateComparators(pos, block) when implemented } /// Vanilla: `JukeboxBlock.emitsRedstonePower()` returns true - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } /// Vanilla: Returns 15 if playing, 0 otherwise - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - // Vanilla: return world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv && lv.getManager().isPlaying() ? 15 : 0 - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(jukebox_entity) = - block_entity.as_any().downcast_ref::() - && jukebox_entity.is_playing() - { - 15 - } else { - 0 - } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + // Vanilla: return world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv && lv.getManager().isPlaying() ? 15 : 0 + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(jukebox_entity) = block_entity.as_any().downcast_ref::() + && jukebox_entity.is_playing() + { + 15 + } else { + 0 + } } /// Vanilla: Returns the song's comparator output (0-15) - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - // Vanilla: return world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv ? lv.getComparatorOutput() : 0 - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(jukebox_entity) = - block_entity.as_any().downcast_ref::() + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + // Vanilla: return world.getBlockEntity(pos) instanceof JukeboxBlockEntity lv ? lv.getComparatorOutput() : 0 + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(jukebox_entity) = block_entity.as_any().downcast_ref::() + { + let record = jukebox_entity.get_record(); + // Get the song from the record's jukebox_playable component + if let Some(playable) = record.get_data_component::() + && let Some(song_name) = playable.song.split(':').nth(1) + && let Some(song) = JukeboxSong::from_name(song_name) { - let record = jukebox_entity.get_record().await; - // Get the song from the record's jukebox_playable component - if let Some(playable) = record.get_data_component::() - && let Some(song_name) = playable.song.split(':').nth(1) - && let Some(song) = JukeboxSong::from_name(song_name) - { - return Some(song.comparator_output()); - } + return Some(song.comparator_output()); } - Some(0) - }) + } + Some(0) } } diff --git a/crates/pumpkin/src/block/blocks/ladder.rs b/crates/pumpkin/src/block/blocks/ladder.rs index 39d2101bb..bf3ab429e 100644 --- a/crates/pumpkin/src/block/blocks/ladder.rs +++ b/crates/pumpkin/src/block/blocks/ladder.rs @@ -1,6 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, }; use crate::entity::EntityBase; use crate::world::World; @@ -16,40 +15,37 @@ use pumpkin_world::tick::TickPriority; pub struct LadderBlock; impl BlockBehaviour for LadderBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let clicked_pos = args.use_item_on.position; - let (clicked_block, clicked_block_state_id) = - args.world.get_block_and_state_id(&clicked_pos); - if clicked_block == &Block::LADDER { - //you can't click on a ladder and place a ladder - let props = - LadderLikeProperties::from_state_id(clicked_block_state_id, clicked_block); - let sub = args.position.0.sub(&clicked_pos.0); - if let Some(dir) = horizontal_facing_from_offset(sub) - && let Some(horizontal_facing) = dir.to_horizontal_facing() - && props.facing == horizontal_facing - { - return Block::AIR.default_state.id; - } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let clicked_pos = args.use_item_on.position; + let (clicked_block, clicked_block_state_id) = + args.world.get_block_and_state_id(&clicked_pos); + if clicked_block == &Block::LADDER { + //you can't click on a ladder and place a ladder + let props = LadderLikeProperties::from_state_id(clicked_block_state_id, clicked_block); + let sub = args.position.0.sub(&clicked_pos.0); + if let Some(dir) = horizontal_facing_from_offset(sub) + && let Some(horizontal_facing) = dir.to_horizontal_facing() + && props.facing == horizontal_facing + { + return Block::AIR.default_state.id; } - let mut props = LadderLikeProperties::default(args.block); + } + let mut props = LadderLikeProperties::default(args.block); - let directions = args.player.get_entity().get_entity_facing_order(); - for dir in directions { - if dir == Facing::Up || dir == Facing::Down { - continue; - } - if !can_place_ladder_at(args.world, args.position, dir.to_block_direction()) { - continue; - } - if let Some(facing) = dir.opposite().to_horizontal_facing() { - props.facing = facing; - return props.to_state_id(args.block); - } + let directions = args.player.get_entity().get_entity_facing_order(); + for dir in directions { + if dir == Facing::Up || dir == Facing::Down { + continue; } - Block::AIR.default_state.id - }) + if !can_place_ladder_at(args.world, args.position, dir.to_block_direction()) { + continue; + } + if let Some(facing) = dir.opposite().to_horizontal_facing() { + props.facing = facing; + return props.to_state_id(args.block); + } + } + Block::AIR.default_state.id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { for dir in BlockDirection::horizontal() { @@ -63,41 +59,37 @@ impl BlockBehaviour for LadderBlock { } false } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = LadderLikeProperties::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_ladder_at( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) - { - return BlockStateId::AIR; - } - args.state_id - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - if Block::from_state_id(state_id) != &Block::LADDER { - return; - } - let props = LadderLikeProperties::from_state_id(state_id, args.block); - if !can_place_ladder_at( + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = LadderLikeProperties::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_ladder_at( args.world, args.position, props.facing.to_block_direction().opposite(), - ) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - }) + ) + { + return BlockStateId::AIR; + } + args.state_id + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + if Block::from_state_id(state_id) != &Block::LADDER { + return; + } + let props = LadderLikeProperties::from_state_id(state_id, args.block); + if !can_place_ladder_at( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } } } #[must_use] diff --git a/crates/pumpkin/src/block/blocks/lanterns.rs b/crates/pumpkin/src/block/blocks/lanterns.rs index dcd164aa3..70c298d9b 100644 --- a/crates/pumpkin/src/block/blocks/lanterns.rs +++ b/crates/pumpkin/src/block/blocks/lanterns.rs @@ -1,6 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, }; use crate::world::World; use pumpkin_data::BlockStateId; @@ -16,19 +15,16 @@ use pumpkin_world::world::BlockFlags; pub struct LanternBlock; impl BlockBehaviour for LanternBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - pumpkin_data::block_properties::LanternLikeProperties::default(args.block); - props.r#waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = pumpkin_data::block_properties::LanternLikeProperties::default(args.block); + props.r#waterlogged = args.replacing.water_source(); - let block_up_state = args.world.get_block_state(&args.position.up()); - if block_up_state.is_center_solid(BlockDirection::Down) { - props.r#hanging = true; - } + let block_up_state = args.world.get_block_state(&args.position.up()); + if block_up_state.is_center_solid(BlockDirection::Down) { + props.r#hanging = true; + } - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -36,27 +32,22 @@ impl BlockBehaviour for LanternBlock { .is_some_and(|world| can_place_at(world, args.position)) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world, args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } diff --git a/crates/pumpkin/src/block/blocks/leaves.rs b/crates/pumpkin/src/block/blocks/leaves.rs index c0323facf..cbedce1e1 100644 --- a/crates/pumpkin/src/block/blocks/leaves.rs +++ b/crates/pumpkin/src/block/blocks/leaves.rs @@ -12,8 +12,7 @@ use pumpkin_world::{ }; use crate::block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, - RandomTickArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs, }; pub const DECAY_DISTANCE: u8 = 7; @@ -53,59 +52,49 @@ pub fn update_distance( } impl BlockBehaviour for LeavesBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - OakLeavesLikeProperties::from_state_id(args.block.default_state.id, args.block); - props.persistent = true; - props.waterlogged = args.replacing.water_source(); - props = update_distance(args.world, args.position, props); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + OakLeavesLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.persistent = true; + props.waterlogged = args.replacing.water_source(); + props = update_distance(args.world, args.position, props); + props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let neighbor_block = args.world.get_block(args.neighbor_position); - let distance_from_neighbor = - get_distance_at(neighbor_block, args.neighbor_state_id).saturating_add(1); - let current_props = OakLeavesLikeProperties::from_state_id(args.state_id, args.block); + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let neighbor_block = args.world.get_block(args.neighbor_position); + let distance_from_neighbor = + get_distance_at(neighbor_block, args.neighbor_state_id).saturating_add(1); + let current_props = OakLeavesLikeProperties::from_state_id(args.state_id, args.block); - if distance_from_neighbor != 1 || current_props.distance != distance_from_neighbor { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } + if distance_from_neighbor != 1 || current_props.distance != distance_from_neighbor { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } - args.state_id - }) + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let props = OakLeavesLikeProperties::from_state_id(state_id, args.block); - let updated_props = update_distance(&**args.world, args.position, props); - let new_state_id = updated_props.to_state_id(args.block); - if new_state_id != state_id { - args.world - .set_block_state(args.position, new_state_id, BlockFlags::NOTIFY_ALL) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let props = OakLeavesLikeProperties::from_state_id(state_id, args.block); + let updated_props = update_distance(&**args.world, args.position, props); + let new_state_id = updated_props.to_state_id(args.block); + if new_state_id != state_id { + args.world + .set_block_state(args.position, new_state_id, BlockFlags::NOTIFY_ALL); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let props = OakLeavesLikeProperties::from_state_id(state_id, args.block); - if !props.persistent && props.distance == DECAY_DISTANCE { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let props = OakLeavesLikeProperties::from_state_id(state_id, args.block); + if !props.persistent && props.distance == DECAY_DISTANCE { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } diff --git a/crates/pumpkin/src/block/blocks/lectern.rs b/crates/pumpkin/src/block/blocks/lectern.rs index 9927df99c..219ea693d 100644 --- a/crates/pumpkin/src/block/blocks/lectern.rs +++ b/crates/pumpkin/src/block/blocks/lectern.rs @@ -4,7 +4,7 @@ use std::sync::atomic::Ordering; use crate::block::entities::lectern::LecternBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, + BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, UseWithItemArgs, }; @@ -63,7 +63,7 @@ impl LecternController for LecternPageController { } entity.page.store(page as usize, Ordering::Relaxed); entity.mark_dirty(); - LecternBlock::pulse(&self.world, &self.position).await; + LecternBlock::pulse(&self.world, &self.position); }) } @@ -72,7 +72,7 @@ impl LecternController for LecternPageController { if let Some(entity) = self.entity() { entity.page.store(0, Ordering::Relaxed); } - LecternBlock::set_has_book(&self.world, &self.position, false).await; + LecternBlock::set_has_book(&self.world, &self.position, false); }) } } @@ -113,22 +113,20 @@ impl LecternBlock { /// The lectern strongly powers the block below it, so its neighbors need /// updating whenever the power or book state changes. - async fn update_neighbors_below(world: &Arc, position: &BlockPos) { - world.update_neighbors(&position.down(), None).await; + fn update_neighbors_below(world: &Arc, position: &BlockPos) { + world.update_neighbors(&position.down(), None); } /// Emits the vanilla page-turn redstone pulse: powered for two game ticks. - pub(crate) async fn pulse(world: &Arc, position: &BlockPos) { + pub(crate) fn pulse(world: &Arc, position: &BlockPos) { let (block, state_id) = world.get_block_and_state_id(position); if block != &Block::LECTERN { return; } let mut props = LecternLikeProperties::from_state_id(state_id, block); props.powered = true; - world - .set_block_state(position, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; - Self::update_neighbors_below(world, position).await; + world.set_block_state(position, props.to_state_id(block), BlockFlags::NOTIFY_ALL); + Self::update_neighbors_below(world, position); world.schedule_block_tick( block, *position, @@ -139,7 +137,7 @@ impl LecternBlock { } /// Sets `has_book`, dropping any pending pulse like vanilla `setHasBook`. - pub(crate) async fn set_has_book(world: &Arc, position: &BlockPos, has_book: bool) { + pub(crate) fn set_has_book(world: &Arc, position: &BlockPos, has_book: bool) { let (block, state_id) = world.get_block_and_state_id(position); if block != &Block::LECTERN { return; @@ -147,214 +145,175 @@ impl LecternBlock { let mut props = LecternLikeProperties::from_state_id(state_id, block); props.powered = false; props.has_book = has_book; - world - .set_block_state(position, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; - Self::update_neighbors_below(world, position).await; + world.set_block_state(position, props.to_state_id(block), BlockFlags::NOTIFY_ALL); + Self::update_neighbors_below(world, position); } } impl BlockBehaviour for LecternBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_entity = LecternBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let block_entity = LecternBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(block_entity)); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = LecternLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = LecternLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let props = LecternLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); - if !props.has_book { - return BlockActionResult::Pass; - } + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let props = LecternLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); + if !props.has_book { + return BlockActionResult::Pass; + } - let Some(block_entity) = args.world.get_block_entity(args.position) else { - return BlockActionResult::Pass; - }; - let Some(inventory) = block_entity.get_inventory() else { - return BlockActionResult::Pass; - }; + let Some(block_entity) = args.world.get_block_entity(args.position) else { + return BlockActionResult::Pass; + }; + let Some(inventory) = block_entity.get_inventory() else { + return BlockActionResult::Pass; + }; - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithLectern as i32, - 1, - ) - .await; + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithLectern as i32, + 1, + ); - let controller = Arc::new(LecternPageController { - world: args.world.clone(), - position: *args.position, - inventory: inventory.clone(), - }); - args.player + let controller = Arc::new(LecternPageController { + world: args.world.clone(), + position: *args.position, + inventory: inventory.clone(), + }); + let player = args.player.clone(); + let pos = *args.position; + tokio::spawn(async move { + player .open_handled_screen( &LecternScreenFactory { inventory, controller, }, - Some(*args.position), + Some(pos), ) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let item_stack = &mut *args.item_stack; - if !item_stack.item.has_tag(&tag::Item::MINECRAFT_LECTERN_BOOKS) { - return BlockActionResult::PassToDefaultBlockAction; + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let item_stack = &mut *args.item_stack; + if !item_stack.item.has_tag(&tag::Item::MINECRAFT_LECTERN_BOOKS) { + return BlockActionResult::PassToDefaultBlockAction; + } + + let props = LecternLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); + if props.has_book { + // Fall through so `normal_use` opens the reading screen. + return BlockActionResult::PassToDefaultBlockAction; + } + + let Some(lectern) = args.world.get_block_entity(args.position) else { + return BlockActionResult::PassToDefaultBlockAction; + }; + let Some(lectern) = lectern.as_any().downcast_ref::() else { + return BlockActionResult::PassToDefaultBlockAction; + }; + + let book = item_stack.split_unless_creative(args.player.gamemode.load(), 1); + futures::executor::block_on(lectern.set_stack(0, book)); + + Self::set_has_book(args.world, args.position, true); + args.world + .play_block_sound(Sound::ItemBookPut, SoundCategory::Blocks, *args.position); + + BlockActionResult::Success + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let mut props = LecternLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); + props.powered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true + } + + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LecternLikeProperties::from_state_id(args.state.id, args.block); + if props.powered { 15 } else { 0 } + } + + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LecternLikeProperties::from_state_id(args.state.id, args.block); + if props.powered && args.direction == BlockDirection::Up { + 15 + } else { + 0 + } + } + + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if !args.moved { + let props = LecternLikeProperties::from_state_id(args.old_state_id, args.block); + if props.powered { + Self::update_neighbors_below(args.world, args.position); } + } + } - let props = LecternLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); - if props.has_book { - // Fall through so `normal_use` opens the reading screen. - return BlockActionResult::PassToDefaultBlockAction; + fn broken(&self, args: BrokenArgs<'_>) { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(lectern_entity) = block_entity.as_any().downcast_ref::() + { + let book = futures::executor::block_on(lectern_entity.remove_stack(0)); + if !book.is_empty() { + // Drop the book item + let entity = Entity::new( + args.world.clone(), + Vector3::new( + f64::from(args.position.0.x) + 0.5, + f64::from(args.position.0.y) + 0.5, + f64::from(args.position.0.z) + 0.5, + ), + &EntityType::ITEM, + ); + let item_entity = ItemEntity::new(entity, book); + args.world.spawn_entity(Arc::new(item_entity)); } - - let Some(lectern) = args.world.get_block_entity(args.position) else { - return BlockActionResult::PassToDefaultBlockAction; - }; - let Some(lectern) = lectern.as_any().downcast_ref::() else { - return BlockActionResult::PassToDefaultBlockAction; - }; - - let book = item_stack.split_unless_creative(args.player.gamemode.load(), 1); - let _ = item_stack; - lectern.set_stack(0, book).await; - - Self::set_has_book(args.world, args.position, true).await; - args.world - .play_block_sound(Sound::ItemBookPut, SoundCategory::Blocks, *args.position); - - BlockActionResult::Success - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let mut props = LecternLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - Self::update_neighbors_below(args.world, args.position).await; - }) - } - - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) - } - - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = LecternLikeProperties::from_state_id(args.state.id, args.block); - if props.powered { 15 } else { 0 } - }) - } - - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = LecternLikeProperties::from_state_id(args.state.id, args.block); - if props.powered && args.direction == BlockDirection::Up { - 15 - } else { - 0 - } - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !args.moved { - let props = LecternLikeProperties::from_state_id(args.old_state_id, args.block); - if props.powered { - Self::update_neighbors_below(args.world, args.position).await; - } - } - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(lectern_entity) = - block_entity.as_any().downcast_ref::() - { - let book = lectern_entity.remove_stack(0).await; - if !book.is_empty() { - // Drop the book item - let entity = Entity::new( - args.world.clone(), - Vector3::new( - f64::from(args.position.0.x) + 0.5, - f64::from(args.position.0.y) + 0.5, - f64::from(args.position.0.z) + 0.5, - ), - &EntityType::ITEM, - ); - let item_entity = ItemEntity::new(entity, book); - args.world.spawn_entity(Arc::new(item_entity)).await; - } - } - }) - } - - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(lectern_entity) = - block_entity.as_any().downcast_ref::() - { - Some(lectern_entity.comparator_output().await) - } else { - Some(0) - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(lectern_entity) = block_entity.as_any().downcast_ref::() + { + Some(futures::executor::block_on( + lectern_entity.comparator_output(), + )) + } else { + Some(0) + } } } diff --git a/crates/pumpkin/src/block/blocks/logs.rs b/crates/pumpkin/src/block/blocks/logs.rs index e749f2f67..8a4ca4f2a 100644 --- a/crates/pumpkin/src/block/blocks/logs.rs +++ b/crates/pumpkin/src/block/blocks/logs.rs @@ -2,8 +2,8 @@ use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::BlockProperties; use pumpkin_macros::pumpkin_block_from_tag; +use crate::block::BlockBehaviour; use crate::block::OnPlaceArgs; -use crate::block::{BlockBehaviour, BlockFuture}; type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties; @@ -11,12 +11,10 @@ type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties; pub struct LogBlock; impl BlockBehaviour for LogBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut log_props = LogProperties::default(args.block); - log_props.axis = args.direction.to_axis(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut log_props = LogProperties::default(args.block); + log_props.axis = args.direction.to_axis(); - log_props.to_state_id(args.block) - }) + log_props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/loom.rs b/crates/pumpkin/src/block/blocks/loom.rs index 3ab041f42..931344582 100644 --- a/crates/pumpkin/src/block/blocks/loom.rs +++ b/crates/pumpkin/src/block/blocks/loom.rs @@ -1,10 +1,10 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs}; use crate::entity::EntityBase; -use pumpkin_data::FacingExt; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_data::translation; +use pumpkin_data::{BlockStateId, FacingExt}; use pumpkin_inventory::loom_screen_handler::LoomScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ @@ -19,40 +19,35 @@ use tokio::sync::Mutex; pub struct LoomBlock; impl BlockBehaviour for LoomBlock { - fn on_place<'a>( - &'a self, - args: OnPlaceArgs<'a>, - ) -> BlockFuture<'a, pumpkin_data::BlockStateId> { - Box::pin(async move { - let mut props = WallTorchLikeProperties::default(args.block); - if let Some(facing) = args - .player - .get_entity() - .get_facing() - .opposite() - .to_horizontal_facing() - { - props.facing = facing; - } - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = WallTorchLikeProperties::default(args.block); + if let Some(facing) = args + .player + .get_entity() + .get_facing() + .opposite() + .to_horizontal_facing() + { + props.facing = facing; + } + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithLoom as i32, - 1, - ) - .await; - args.player - .open_handled_screen(&LoomScreenFactory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithLoom as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&LoomScreenFactory, Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/magma.rs b/crates/pumpkin/src/block/blocks/magma.rs index 0f2e32e5e..2862ea14c 100644 --- a/crates/pumpkin/src/block/blocks/magma.rs +++ b/crates/pumpkin/src/block/blocks/magma.rs @@ -5,14 +5,14 @@ use pumpkin_data::{ }; use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, BlockFuture, OnEntityStepArgs}; +use crate::block::{BlockBehaviour, OnEntityStepArgs}; #[pumpkin_block("minecraft:magma_block")] pub struct MagmaBlock; impl BlockBehaviour for MagmaBlock { - fn on_entity_step<'a>(&'a self, args: OnEntityStepArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_step(&self, args: OnEntityStepArgs<'_>) { + { // Only living entities take damage let Some(living_entity) = args.entity.get_living_entity() else { return; @@ -31,7 +31,10 @@ impl BlockBehaviour for MagmaBlock { } let has_frost_walker = { - let equipment = living_entity.entity_equipment.lock().await; + let equipment = living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment .equipment .get(&EquipmentSlot::FEET) @@ -45,16 +48,13 @@ impl BlockBehaviour for MagmaBlock { if living_entity .get_effect(&StatusEffect::FIRE_RESISTANCE) - .await .is_some() { return; } // Apply damage - args.entity - .damage(args.entity, 1.0, DamageType::HOT_FLOOR) - .await; - }) + args.entity.damage(args.entity, 1.0, DamageType::HOT_FLOOR); + } } } diff --git a/crates/pumpkin/src/block/blocks/mangrove_roots.rs b/crates/pumpkin/src/block/blocks/mangrove_roots.rs index f3e8a90c9..af3b77a99 100644 --- a/crates/pumpkin/src/block/blocks/mangrove_roots.rs +++ b/crates/pumpkin/src/block/blocks/mangrove_roots.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties}; use pumpkin_data::fluid::Fluid; @@ -9,29 +9,25 @@ use pumpkin_world::tick::TickPriority; pub struct MangroveRootsBlock; impl BlockBehaviour for MangroveRootsBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = MangroveRootsLikeProperties::default(args.block); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = MangroveRootsLikeProperties::default(args.block); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block); - if props.waterlogged { - args.world.schedule_fluid_tick( - &Fluid::WATER, - *args.position, - Fluid::WATER.flow_speed as u8, - TickPriority::Normal, - ); - } - props.to_state_id(args.block) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block); + if props.waterlogged { + args.world.schedule_fluid_tick( + &Fluid::WATER, + *args.position, + Fluid::WATER.flow_speed as u8, + TickPriority::Normal, + ); + } + props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/nether_portal.rs b/crates/pumpkin/src/block/blocks/nether_portal.rs index 199004bdc..c76d1f2b9 100644 --- a/crates/pumpkin/src/block/blocks/nether_portal.rs +++ b/crates/pumpkin/src/block/blocks/nether_portal.rs @@ -13,8 +13,8 @@ use uuid::Uuid; use crate::{ block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, - OnStateReplacedArgs, RandomTickArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnStateReplacedArgs, + RandomTickArgs, }, entity::{EntityBase, r#type::from_type}, world::{World, portal::nether::NetherPortal}, @@ -44,123 +44,116 @@ impl NetherPortalBlock { } impl BlockBehaviour for NetherPortalBlock { - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let direction_axis = args.direction.to_axis(); - let state_axis = - NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL) - .axis; - // Convert HorizontalAxis to Axis for comparison - let state_axis_full: Axis = match state_axis { - HorizontalAxis::X => Axis::X, - HorizontalAxis::Z => Axis::Z, - }; - // Vanilla logic: keep portal if direction is horizontal AND different from portal axis - let is_horizontal_and_different = - args.direction.is_horizontal() && direction_axis != state_axis_full; - if is_horizontal_and_different - || args.neighbor_state_id == args.state_id - || NetherPortal::get_on_axis(args.world, args.position, state_axis) - .is_some_and(|e| e.was_already_valid()) - { - return args.state_id; - } - Block::AIR.default_state.id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let direction_axis = args.direction.to_axis(); + let state_axis = + NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL).axis; + // Convert HorizontalAxis to Axis for comparison + let state_axis_full: Axis = match state_axis { + HorizontalAxis::X => Axis::X, + HorizontalAxis::Z => Axis::Z, + }; + // Vanilla logic: keep portal if direction is horizontal AND different from portal axis + let is_horizontal_and_different = + args.direction.is_horizontal() && direction_axis != state_axis_full; + if is_horizontal_and_different + || args.neighbor_state_id == args.state_id + || NetherPortal::get_on_axis(args.world, args.position, state_axis) + .is_some_and(|e| e.was_already_valid()) + { + return args.state_id; + } + Block::AIR.default_state.id } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let level_info = args.world.level_info.load(); - let difficulty = level_info.difficulty; - if !level_info.game_rules.spawn_monsters - || !level_info.game_rules.spawn_mobs - || difficulty == Difficulty::Peaceful - || (args.world.dimension != Dimension::OVERWORLD - && args.world.dimension != Dimension::OVERWORLD_CAVES) - { - return; - } + fn random_tick(&self, args: RandomTickArgs<'_>) { + let level_info = args.world.level_info.load(); + let difficulty = level_info.difficulty; + if !level_info.game_rules.spawn_mobs + || difficulty == Difficulty::Peaceful + || (args.world.dimension != Dimension::OVERWORLD + && args.world.dimension != Dimension::OVERWORLD_CAVES) + { + return; + } - let difficulty_id = difficulty as u32; - let roll = rand::rng().random_range(0..2000); - if roll >= difficulty_id { - return; - } + let difficulty_id = difficulty as u32; + let roll = rand::rng().random_range(0..2000); + if roll >= difficulty_id { + return; + } - let player_close = args - .world - .get_closest_player(args.position.to_centered_f64(), 128.0) - .is_some(); - if !player_close { - return; - } + let player_close = args + .world + .get_closest_player(args.position.to_centered_f64(), 128.0) + .is_some(); + if !player_close { + return; + } - let mut bottom_pos = *args.position; - while args.world.get_block(&bottom_pos) == &Block::NETHER_PORTAL { - bottom_pos = bottom_pos.down(); - } + let mut bottom_pos = *args.position; + while args.world.get_block(&bottom_pos) == &Block::NETHER_PORTAL { + bottom_pos = bottom_pos.down(); + } - if args - .world - .get_block_state(&bottom_pos) - .is_side_solid(BlockDirection::Up) - { - let spawn_pos = Vector3::new( - bottom_pos.0.x as f64 + 0.5, - (bottom_pos.0.y + 1) as f64, - bottom_pos.0.z as f64 + 0.5, - ); - let mob = from_type( - &EntityType::ZOMBIFIED_PIGLIN, - spawn_pos, - args.world, - Uuid::new_v4(), - ); - mob.get_entity() - .portal_cooldown - .store(300, Ordering::Relaxed); - args.world.spawn_entity(mob).await; - } - }) - } - - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let target_world = - if args.world.dimension.minecraft_name == Dimension::THE_NETHER.minecraft_name { - args.server.get_world_from_dimension(&Dimension::OVERWORLD) - } else { - args.server.get_world_from_dimension(&Dimension::THE_NETHER) - }; - - if Arc::ptr_eq(&target_world, args.world) { - return; - } - - tracing::debug!( - "Nether portal collision at {:?}, targeting world {:?}", - args.position, - target_world.dimension.minecraft_name + if args + .world + .get_block_state(&bottom_pos) + .is_side_solid(BlockDirection::Up) + { + let spawn_pos = Vector3::new( + bottom_pos.0.x as f64 + 0.5, + (bottom_pos.0.y + 1) as f64, + bottom_pos.0.z as f64 + 0.5, ); - let portal_delay = Self::get_portal_time(args.world, args.entity); - - args.entity - .get_entity() - .try_use_portal(portal_delay, target_world, *args.position) - .await; - }) + let mob = from_type( + &EntityType::ZOMBIFIED_PIGLIN, + spawn_pos, + args.world, + Uuid::new_v4(), + ); + mob.get_entity() + .portal_cooldown + .store(300, Ordering::Relaxed); + args.world.spawn_entity_non_save(mob); + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Remove from POI storage when portal block is replaced - let mut poi_storage = args.world.portal_poi.lock().await; - poi_storage.remove(args.position); - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let target_world = + if args.world.dimension.minecraft_name == Dimension::THE_NETHER.minecraft_name { + args.server.get_world_from_dimension(&Dimension::OVERWORLD) + } else { + args.server.get_world_from_dimension(&Dimension::THE_NETHER) + }; + + if Arc::ptr_eq(&target_world, args.world) { + return; + } + + tracing::debug!( + "Nether portal collision at {:?}, targeting world {:?}", + args.position, + target_world.dimension.minecraft_name + ); + let portal_delay = Self::get_portal_time(args.world, args.entity); + + args.entity + .get_entity() + .try_use_portal(portal_delay, target_world, *args.position); + } + + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + // Remove from POI storage when portal block is replaced + let mut poi_storage = args + .world + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + poi_storage.remove(args.position); } fn rotate( diff --git a/crates/pumpkin/src/block/blocks/note.rs b/crates/pumpkin/src/block/blocks/note.rs index 2e483a8ec..27711b6a2 100644 --- a/crates/pumpkin/src/block/blocks/note.rs +++ b/crates/pumpkin/src/block/blocks/note.rs @@ -1,6 +1,6 @@ use crate::block::registry::BlockActionResult; use crate::block::{ - BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, UseWithItemArgs, }; use pumpkin_data::BlockStateId; @@ -25,7 +25,7 @@ use super::redstone::block_receives_redstone_power; pub struct NoteBlock; impl NoteBlock { - pub async fn play_note(props: &NoteBlockLikeProperties, world: &World, pos: &BlockPos) { + pub fn play_note(props: &NoteBlockLikeProperties, world: &World, pos: &BlockPos) { if !is_base_block(props.instrument) || world.get_block_state(&pos.up()).is_air() { let mut event = crate::plugin::api::events::block::note_play::NotePlayEvent::new( *pos, @@ -33,12 +33,12 @@ impl NoteBlock { props.note, ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; } - world.add_synced_block_event(*pos, 0, 0).await; + world.add_synced_block_event(*pos, 0, 0); } } fn get_note_pitch(note: u16) -> f32 { @@ -70,116 +70,92 @@ impl NoteBlock { } impl BlockBehaviour for NoteBlock { - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_state = args.world.get_block_state(args.position); - let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); - let powered = block_receives_redstone_power(args.world, args.position).await; - // check if powered state changed - if note_props.powered != powered { - if powered { - Self::play_note(¬e_props, args.world, args.position).await; - } - note_props.powered = powered; - args.world - .set_block_state( - args.position, - note_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let block_state = args.world.get_block_state(args.position); + let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); + let powered = block_receives_redstone_power(args.world, args.position); + // check if powered state changed + if note_props.powered != powered { + if powered { + Self::play_note(¬e_props, args.world, args.position); } - }) - } - - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let block_state = args.world.get_block_state(args.position); - let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); - note_props.note = (note_props.note + 1) % 25; - args.world - .set_block_state( - args.position, - note_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - Self::play_note(¬e_props, args.world, args.position).await; - - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::TuneNoteblock as i32, - 1, - ) - .await; - - BlockActionResult::Success - }) - } - - fn use_with_item<'a>( - &'a self, - _args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - // TODO - BlockActionResult::PassToDefaultBlockAction - }) - } - - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - let block_state = args.world.get_block_state(args.position); - let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); - let instrument = note_props.instrument; - let pitch = if is_base_block(instrument) { - // checks if can be pitched - Self::get_note_pitch(u16::from(note_props.note)) - } else { - 1.0 // default pitch - }; - // check hasCustomSound - args.world.play_sound_raw( - convert_instrument_to_sound(instrument) as u16, - SoundCategory::Records, - &args.position.to_f64(), - 3.0, - pitch, + note_props.powered = powered; + args.world.set_block_state( + args.position, + note_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, ); - true - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - Self::get_state_with_instrument( + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let block_state = args.world.get_block_state(args.position); + let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); + note_props.note = (note_props.note + 1) % 25; + args.world.set_block_state( + args.position, + note_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + Self::play_note(¬e_props, args.world, args.position); + + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::TuneNoteblock as i32, + 1, + ); + + BlockActionResult::Success + } + + fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { + // TODO + BlockActionResult::PassToDefaultBlockAction + } + + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + let block_state = args.world.get_block_state(args.position); + let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); + let instrument = note_props.instrument; + let pitch = if is_base_block(instrument) { + // checks if can be pitched + Self::get_note_pitch(u16::from(note_props.note)) + } else { + 1.0 // default pitch + }; + // check hasCustomSound + args.world.play_sound_raw( + convert_instrument_to_sound(instrument) as u16, + SoundCategory::Records, + &args.position.to_f64(), + 3.0, + pitch, + ); + true + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + Self::get_state_with_instrument( + args.world, + args.position, + Block::NOTE_BLOCK.default_state.id, + args.block, + ) + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction.to_axis() == Axis::Y { + return Self::get_state_with_instrument( args.world, args.position, - Block::NOTE_BLOCK.default_state.id, + args.state_id, args.block, - ) - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction.to_axis() == Axis::Y { - return Self::get_state_with_instrument( - args.world, - args.position, - args.state_id, - args.block, - ); - } - args.state_id - }) + ); + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/nylium.rs b/crates/pumpkin/src/block/blocks/nylium.rs index 2ea1f9210..adba52e0d 100644 --- a/crates/pumpkin/src/block/blocks/nylium.rs +++ b/crates/pumpkin/src/block/blocks/nylium.rs @@ -7,7 +7,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use rand::RngExt; -use crate::block::{BlockBehaviour, BlockFuture, BonemealArgs, RandomTickArgs}; +use crate::block::{BlockBehaviour, BonemealArgs, RandomTickArgs}; use crate::world::World; #[pumpkin_block_from_tag("minecraft:nylium")] @@ -25,18 +25,14 @@ impl NyliumBlock { } impl BlockBehaviour for NyliumBlock { - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !Self::can_be_nylium(args.world, args.position) { - args.world - .set_block_state( - args.position, - Block::NETHERRACK.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if !Self::can_be_nylium(args.world, args.position) { + args.world.set_block_state( + args.position, + Block::NETHERRACK.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } } fn is_valid_bonemeal_target(&self, args: BonemealArgs<'_>) -> bool { @@ -50,26 +46,24 @@ impl BlockBehaviour for NyliumBlock { true } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let world = args.world; - let block = args.block; - let above_pos = args.position.up(); + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + let world = args.world; + let block = args.block; + let above_pos = args.position.up(); - if block == &Block::CRIMSON_NYLIUM { - place_crimson_vegetation(world, &above_pos).await; - } else if block == &Block::WARPED_NYLIUM { - place_warped_vegetation(world, &above_pos).await; - place_nether_sprouts(world, &above_pos).await; - if rand::rng().random_range(0..8) == 0 { - place_twisting_vines(world, &above_pos).await; - } + if block == &Block::CRIMSON_NYLIUM { + place_crimson_vegetation(world, &above_pos); + } else if block == &Block::WARPED_NYLIUM { + place_warped_vegetation(world, &above_pos); + place_nether_sprouts(world, &above_pos); + if rand::rng().random_range(0..8) == 0 { + place_twisting_vines(world, &above_pos); } - }) + } } } -async fn place_crimson_vegetation(world: &Arc, origin: &BlockPos) { +fn place_crimson_vegetation(world: &Arc, origin: &BlockPos) { for _ in 0..9 { let dx = rand::rng().random_range(0..3) - rand::rng().random_range(0..3); let dy = rand::rng().random_range(0..1) - rand::rng().random_range(0..1); @@ -116,13 +110,11 @@ async fn place_crimson_vegetation(world: &Arc, origin: &BlockPos) { continue; } - world - .set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL); } } -async fn place_warped_vegetation(world: &Arc, origin: &BlockPos) { +fn place_warped_vegetation(world: &Arc, origin: &BlockPos) { for _ in 0..9 { let dx = rand::rng().random_range(0..3) - rand::rng().random_range(0..3); let dy = rand::rng().random_range(0..1) - rand::rng().random_range(0..1); @@ -171,13 +163,11 @@ async fn place_warped_vegetation(world: &Arc, origin: &BlockPos) { continue; } - world - .set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL); } } -async fn place_nether_sprouts(world: &Arc, origin: &BlockPos) { +fn place_nether_sprouts(world: &Arc, origin: &BlockPos) { for _ in 0..9 { let dx = rand::rng().random_range(0..3) - rand::rng().random_range(0..3); let dy = rand::rng().random_range(0..1) - rand::rng().random_range(0..1); @@ -216,13 +206,11 @@ async fn place_nether_sprouts(world: &Arc, origin: &BlockPos) { continue; } - world - .set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&target_pos, state.id, BlockFlags::NOTIFY_ALL); } } -async fn place_twisting_vines(world: &Arc, origin: &BlockPos) { +fn place_twisting_vines(world: &Arc, origin: &BlockPos) { for _ in 0..9 { let dx = rand::rng().random_range(0..3) - rand::rng().random_range(0..3); let dy = rand::rng().random_range(0..1) - rand::rng().random_range(0..1); @@ -256,22 +244,18 @@ async fn place_twisting_vines(world: &Arc, origin: &BlockPos) { || !world.get_block_state(¤t_pos.up()).is_air(); if is_top { - world - .set_block_state( - ¤t_pos, - Block::TWISTING_VINES.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + ¤t_pos, + Block::TWISTING_VINES.default_state.id, + BlockFlags::NOTIFY_ALL, + ); break; } - world - .set_block_state( - ¤t_pos, - Block::TWISTING_VINES_PLANT.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + ¤t_pos, + Block::TWISTING_VINES_PLANT.default_state.id, + BlockFlags::NOTIFY_ALL, + ); current_pos = current_pos.up(); } } diff --git a/crates/pumpkin/src/block/blocks/piston/mod.rs b/crates/pumpkin/src/block/blocks/piston/mod.rs index 88ca567a2..96e508fb6 100644 --- a/crates/pumpkin/src/block/blocks/piston/mod.rs +++ b/crates/pumpkin/src/block/blocks/piston/mod.rs @@ -46,7 +46,7 @@ impl<'a> PistonHandler<'a> { } } - pub async fn calculate_push(&mut self) -> bool { + pub fn calculate_push(&mut self) -> bool { self.moved_blocks.clear(); self.broken_blocks.clear(); let (block, block_state) = self.world.get_block_and_state(&self.pos_to); @@ -64,15 +64,13 @@ impl<'a> PistonHandler<'a> { } return false; } - if !self.try_move(self.pos_to, self.motion_direction).await { + if !self.try_move(self.pos_to, self.motion_direction) { return false; } for i in 0..self.moved_blocks.len() { let block_pos = self.moved_blocks[i]; let block = self.world.get_block(&block_pos); - if Self::is_block_sticky(block) - && !self.try_move_adjacent_block(block, &block_pos).await - { + if Self::is_block_sticky(block) && !self.try_move_adjacent_block(block, &block_pos) { return false; } } @@ -98,7 +96,7 @@ impl<'a> PistonHandler<'a> { || (!self.retracted && pos == self.pos_from.offset(self.piston_direction.to_offset())) } - async fn try_move(&mut self, pos: BlockPos, dir: BlockDirection) -> bool { + fn try_move(&mut self, pos: BlockPos, dir: BlockDirection) -> bool { let (mut block, block_state) = self.world.get_block_and_state(&pos); if block_state.is_air() { return true; @@ -154,7 +152,7 @@ impl<'a> PistonHandler<'a> { let block_pos3 = self.moved_blocks[m]; let block = self.world.get_block(&block_pos3); if Self::is_block_sticky(block) - && !Box::pin(self.try_move_adjacent_block(block, &block_pos3)).await + && !self.try_move_adjacent_block(block, &block_pos3) { return false; } @@ -204,7 +202,7 @@ impl<'a> PistonHandler<'a> { self.moved_blocks.extend(list3); } - async fn try_move_adjacent_block(&mut self, block: &Block, pos: &BlockPos) -> bool { + fn try_move_adjacent_block(&mut self, block: &Block, pos: &BlockPos) -> bool { for direction in BlockDirection::all() { if direction.to_axis() == self.motion_direction.to_axis() { continue; @@ -212,7 +210,7 @@ impl<'a> PistonHandler<'a> { let block_pos = pos.offset(direction.to_offset()); let block_state2 = self.world.get_block(&block_pos); if Self::is_adjacent_block_stuck(block_state2, block) - && !self.try_move(block_pos, direction).await + && !self.try_move(block_pos, direction) { return false; } diff --git a/crates/pumpkin/src/block/blocks/piston/piston.rs b/crates/pumpkin/src/block/blocks/piston/piston.rs index 3a10db337..ef9d13b4d 100644 --- a/crates/pumpkin/src/block/blocks/piston/piston.rs +++ b/crates/pumpkin/src/block/blocks/piston/piston.rs @@ -18,7 +18,7 @@ use rustc_hash::FxHashMap; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, blocks::{piston::piston_head::PistonHeadProperties, redstone::is_emitting_redstone_power}, }, @@ -78,249 +78,229 @@ impl PistonBlock { } impl BlockBehaviour for PistonBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = PistonProps::default(args.block); - props.extended = false; - props.facing = args.player.get_entity().get_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = PistonProps::default(args.block); + props.extended = false; + props.facing = args.player.get_entity().get_facing().opposite(); + props.to_state_id(args.block) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let props = PistonProps::from_state_id(args.state.id, args.block); - let pos = args - .position - .offset(props.facing.to_block_direction().to_offset()); - let (block_to_check, block_to_check_state_id) = args.world.get_block_and_state_id(&pos); - if &Block::PISTON_HEAD == block_to_check { - let head_props = - PistonHeadProperties::from_state_id(block_to_check_state_id, block_to_check); + fn broken(&self, args: BrokenArgs<'_>) { + let props = PistonProps::from_state_id(args.state.id, args.block); + let pos = args + .position + .offset(props.facing.to_block_direction().to_offset()); + let (block_to_check, block_to_check_state_id) = args.world.get_block_and_state_id(&pos); + if &Block::PISTON_HEAD == block_to_check { + let head_props = + PistonHeadProperties::from_state_id(block_to_check_state_id, block_to_check); - if (head_props.facing.to_block_direction() != props.facing.to_block_direction()) - && &Block::PISTON_HEAD == block_to_check - { - //Then this is a head of some other piston. - return; - } - - args.world - .break_block(&pos, None, BlockFlags::SKIP_DROPS) - .await; - } else if &Block::MOVING_PISTON == block_to_check { - args.world - .break_block(&pos, None, BlockFlags::SKIP_DROPS) - .await; - } - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.old_state_id == args.state_id { + if (head_props.facing.to_block_direction() != props.facing.to_block_direction()) + && &Block::PISTON_HEAD == block_to_check + { + //Then this is a head of some other piston. return; } - try_move(args.world, args.block, args.position).await; - }) + + args.world.break_block(&pos, None, BlockFlags::SKIP_DROPS); + } else if &Block::MOVING_PISTON == block_to_check { + args.world.break_block(&pos, None, BlockFlags::SKIP_DROPS); + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - try_move(args.world, args.block, args.position).await; - }) + fn placed(&self, args: PlacedArgs<'_>) { + if args.old_state_id == args.state_id { + return; + } + try_move(args.world, args.block, args.position); } + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + try_move(args.world, args.block, args.position); + } + + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + let block_id = args.block.id; + let block = Block::from_id(block_id); + Self::handle_synced_block_event(block, args.world, args.position, args.r#type, args.data) + } +} + +impl PistonBlock { #[expect(clippy::too_many_lines)] - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - let (block, world, pos, r#type, data) = ( - args.block, - args.world, - args.position, - args.r#type, - args.data, - ); + fn handle_synced_block_event( + block: &Block, + world: &Arc, + pos: &BlockPos, + r#type: u8, + data: u8, + ) -> bool { + let state = world.get_block_state(pos); + let mut props = PistonProps::from_state_id(state.id, block); + let dir = props.facing.to_block_direction(); - let state = world.get_block_state(pos); - let mut props = PistonProps::from_state_id(state.id, block); - let dir = props.facing.to_block_direction(); + // I don't think this is optimal ? + let sticky = block == &Block::STICKY_PISTON; - // I don't think this is optimal ? - let sticky = block == &Block::STICKY_PISTON; + let should_extend = should_extend(world, pos, dir); + if should_extend && (r#type == 1 || r#type == 2) { + props.extended = true; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); + return false; + } - let should_extend = should_extend(world, pos, dir).await; - if should_extend && (r#type == 1 || r#type == 2) { - props.extended = true; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) - .await; - return false; - } - - // This may prevents when something happens in the one tick before this function got called - if !should_extend && r#type == 0 { - return false; - } - - // Extend Piston - if r#type == 0 { - let mut event = - crate::plugin::api::events::block::block_piston::BlockPistonExtendEvent::new( - *pos, - format!("{dir:?}"), - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return false; - } - - if !move_piston(world, dir, pos, true, sticky).await { - return false; - } - props.extended = true; - world - .set_block_state( - pos, - props.to_state_id(block), - BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, - ) - .await; - // Play piston extend sound - let pitch = rand::rng().random_range(0.6f32..0.85); - world.play_sound_fine( - Sound::BlockPistonExtend, - SoundCategory::Blocks, - &pos.to_centered_f64(), - 0.5, - pitch, - ); - return true; - } - // Reduce Piston + // This may prevents when something happens in the one tick before this function got called + if !should_extend && r#type == 0 { + return false; + } + // Extend Piston + if r#type == 0 { let mut event = - crate::plugin::api::events::block::block_piston::BlockPistonRetractEvent::new( + crate::plugin::api::events::block::block_piston::BlockPistonExtendEvent::new( *pos, format!("{dir:?}"), ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return false; } - let extended_pos = pos.offset(dir.to_offset()); - - if let Some(block_entity) = world.get_block_entity(&extended_pos) - && let Some(piston) = block_entity.as_any().downcast_ref::() - { - piston.finish(world.clone()).await; + if !move_piston(world, dir, pos, true, sticky) { + return false; } - - let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON); - props.facing = dir.to_facing(); - props.r#type = if sticky { - PistonType::Sticky - } else { - PistonType::Normal - }; - - world - .set_block_state( - pos, - props.to_state_id(&Block::MOVING_PISTON), - BlockFlags::FORCE_STATE, - ) - .await; - - let mut props = PistonProps::default(block); - props.facing = BlockDirection::by_index((data & 7) as usize) - .unwrap_or(BlockDirection::North) - .to_facing(); - - world.add_block_entity(Arc::new(PistonBlockEntity { - position: *pos, - facing: dir, - pushed_block_state: BlockState::from_id(props.to_state_id(block)), - current_progress: 0.0.into(), - last_progress: 0.0.into(), - extending: false, - source: true, - })); - - world.update_neighbors(pos, None).await; - if sticky { - let pull_pos = pos.offset_dir(dir.to_offset(), 2); - let (block, state) = world.get_block_and_state(&pull_pos); - let piston_piece = if block == &Block::MOVING_PISTON - && let Some(entity) = world.get_block_entity(&pull_pos) - && let Some(piston) = entity.as_any().downcast_ref::() - && piston.facing == dir - && piston.extending - { - piston.finish(world.clone()).await; - true - } else { - false - }; - if !piston_piece { - if r#type == 1 - && !state.is_air() - && Self::is_movable(block, state, dir, false, dir) - && (state.piston_behavior == PistonBehavior::Normal - || block == &Block::PISTON - || block == &Block::STICKY_PISTON) - { - move_piston(world, dir, pos, false, sticky).await; - } else { - // remove - world - .set_block_state( - &extended_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - } - } else { - // remove - world - .set_block_state( - &extended_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - // Play piston contract sound - let pitch = rand::rng().random_range(0.6f32..0.75); + props.extended = true; + world.set_block_state( + pos, + props.to_state_id(block), + BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, + ); + // Play piston extend sound + let pitch = rand::rng().random_range(0.6f32..0.85); world.play_sound_fine( - Sound::BlockPistonContract, + Sound::BlockPistonExtend, SoundCategory::Blocks, &pos.to_centered_f64(), 0.5, pitch, ); - true - }) + return true; + } + // Reduce Piston + + let mut event = + crate::plugin::api::events::block::block_piston::BlockPistonRetractEvent::new( + *pos, + format!("{dir:?}"), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + if event.cancelled { + return false; + } + + let extended_pos = pos.offset(dir.to_offset()); + + if let Some(block_entity) = world.get_block_entity(&extended_pos) + && let Some(piston) = block_entity.as_any().downcast_ref::() + { + piston.finish(world); + } + + let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON); + props.facing = dir.to_facing(); + props.r#type = if sticky { + PistonType::Sticky + } else { + PistonType::Normal + }; + + world.set_block_state( + pos, + props.to_state_id(&Block::MOVING_PISTON), + BlockFlags::FORCE_STATE, + ); + + let mut props = PistonProps::default(block); + props.facing = BlockDirection::by_index((data & 7) as usize) + .unwrap_or(BlockDirection::North) + .to_facing(); + + world.add_block_entity(Arc::new(PistonBlockEntity { + position: *pos, + facing: dir, + pushed_block_state: BlockState::from_id(props.to_state_id(block)), + current_progress: 0.0.into(), + last_progress: 0.0.into(), + extending: false, + source: true, + })); + + world.set_block_state( + &extended_pos, + Block::AIR.default_state.id, + BlockFlags::FORCE_STATE, + ); + + world.update_neighbors(pos, None); + if sticky { + let pull_pos = pos.offset_dir(dir.to_offset(), 2); + let (block, state) = world.get_block_and_state(&pull_pos); + if data == 2 { + world.set_block_state( + &extended_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } else { + let is_air = state.is_air(); + if !is_air + && (Self::is_movable(block, state, dir, false, dir.opposite()) + || Self::is_movable(block, state, dir, false, dir)) + && (state.piston_behavior == PistonBehavior::Normal + || block == &Block::PISTON + || block == &Block::STICKY_PISTON) + { + move_piston(world, dir, pos, false, sticky); + } else { + // remove + world.set_block_state( + &extended_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + } + } else { + // remove + world.set_block_state( + &extended_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + // Play piston contract sound + let pitch = rand::rng().random_range(0.6f32..0.75); + world.play_sound_fine( + Sound::BlockPistonContract, + SoundCategory::Blocks, + &pos.to_centered_f64(), + 0.5, + pitch, + ); + true } } -async fn should_extend(world: &World, block_pos: &BlockPos, piston_dir: BlockDirection) -> bool { +fn should_extend(world: &World, block_pos: &BlockPos, piston_dir: BlockDirection) -> bool { for dir in BlockDirection::all() { let neighbor_pos = block_pos.offset(dir.to_offset()); let (block, state) = world.get_block_and_state(&neighbor_pos); // Pistons can't be powered from the same direction as they are facing - if dir == piston_dir - || !is_emitting_redstone_power(block, state, world, &neighbor_pos, dir).await + if dir == piston_dir || !is_emitting_redstone_power(block, state, world, &neighbor_pos, dir) { continue; } @@ -328,14 +308,14 @@ async fn should_extend(world: &World, block_pos: &BlockPos, piston_dir: BlockDir } let neighbor_pos = block_pos.offset(BlockDirection::Down.to_offset()); let (block, state) = world.get_block_and_state(&neighbor_pos); - if is_emitting_redstone_power(block, state, world, block_pos, BlockDirection::Down).await { + if is_emitting_redstone_power(block, state, world, block_pos, BlockDirection::Down) { return true; } for dir in BlockDirection::all() { let neighbor_pos = block_pos.up().offset(dir.to_offset()); let (block, state) = world.get_block_and_state(&neighbor_pos); if dir == BlockDirection::Down - || !is_emitting_redstone_power(block, state, world, &neighbor_pos, dir).await + || !is_emitting_redstone_power(block, state, world, &neighbor_pos, dir) { continue; } @@ -344,20 +324,15 @@ async fn should_extend(world: &World, block_pos: &BlockPos, piston_dir: BlockDir false } -pub async fn try_move(world: &Arc, block: &Block, block_pos: &BlockPos) { +pub fn try_move(world: &Arc, block: &Block, block_pos: &BlockPos) { let state = world.get_block_state(block_pos); let props = PistonProps::from_state_id(state.id, block); let dir = props.facing.to_block_direction(); - let should_extent = should_extend(world, block_pos, dir).await; + let should_extent = should_extend(world, block_pos, dir); if should_extent && !props.extended { - if PistonHandler::new(world, *block_pos, dir, true) - .calculate_push() - .await - { - world - .add_synced_block_event(*block_pos, 0, dir.to_index()) - .await; + if PistonHandler::new(world, *block_pos, dir, true).calculate_push() { + world.add_synced_block_event(*block_pos, 0, dir.to_index()); } } else if !should_extent && props.extended { let new_pos = block_pos.offset_dir(dir.to_offset(), 2); @@ -380,14 +355,12 @@ pub async fn try_move(world: &Arc, block: &Block, block_pos: &BlockPos) { } } } - world - .add_synced_block_event(*block_pos, r#type, dir.to_index()) - .await; + world.add_synced_block_event(*block_pos, r#type, dir.to_index()); } } #[expect(clippy::too_many_lines)] -async fn move_piston( +fn move_piston( world: &Arc, dir: BlockDirection, block_pos: &BlockPos, @@ -396,16 +369,14 @@ async fn move_piston( ) -> bool { let extended_pos = block_pos.offset(dir.to_offset()); if !extend && world.get_block(&extended_pos) == &Block::PISTON_HEAD { - world - .set_block_state( - &extended_pos, - Block::AIR.default_state.id, - BlockFlags::FORCE_STATE, - ) - .await; + world.set_block_state( + &extended_pos, + Block::AIR.default_state.id, + BlockFlags::FORCE_STATE, + ); } let mut handler = PistonHandler::new(world, *block_pos, dir, extend); - if !handler.calculate_push().await { + if !handler.calculate_push() { return false; } @@ -427,13 +398,11 @@ async fn move_piston( for &broken_block_pos in broken_blocks.iter().rev() { let block_state = world.get_block_state(&broken_block_pos); - world - .break_block( - &broken_block_pos, - None, - BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, - ) - .await; + world.break_block( + &broken_block_pos, + None, + BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, + ); affected_block_states.push(block_state); } @@ -446,9 +415,7 @@ async fn move_piston( props.facing = dir.to_facing(); let state = props.to_state_id(&Block::MOVING_PISTON); - world - .set_block_state(&target_pos, state, BlockFlags::MOVED) - .await; + world.set_block_state(&target_pos, state, BlockFlags::MOVED); if let Some(moved_state) = moved_block_states.get(moved_blocks.len() - 1 - index) { world.add_block_entity(Arc::new(PistonBlockEntity { @@ -474,13 +441,11 @@ async fn move_piston( props.facing = dir.to_facing(); props.r#type = pistion_type; moved_blocks_map.remove(&extended_pos); - world - .set_block_state( - &extended_pos, - props.to_state_id(&Block::MOVING_PISTON), - BlockFlags::MOVED, - ) - .await; + world.set_block_state( + &extended_pos, + props.to_state_id(&Block::MOVING_PISTON), + BlockFlags::MOVED, + ); let mut props = PistonHeadLikeProperties::default(&Block::PISTON_HEAD); props.facing = dir.to_facing(); props.r#type = pistion_type; @@ -497,70 +462,56 @@ async fn move_piston( let air_state = Block::AIR.default_state.id; for &pos in moved_blocks_map.keys() { - world - .set_block_state( - &pos, - air_state, - BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE | BlockFlags::MOVED, - ) - .await; + world.set_block_state( + &pos, + air_state, + BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE | BlockFlags::MOVED, + ); } for (pos, state) in &moved_blocks_map { - world - .block_registry - .prepare( - world, - pos, - Block::from_state_id(state.id), - state.id, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - world.update_neighbors(pos, None).await; - world - .block_registry - .prepare( - world, - pos, - &Block::AIR, - air_state, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.block_registry.prepare( + world, + pos, + Block::from_state_id(state.id), + state.id, + BlockFlags::NOTIFY_LISTENERS, + ); + world.update_neighbors(pos, None); + world.block_registry.prepare( + world, + pos, + &Block::AIR, + air_state, + BlockFlags::NOTIFY_LISTENERS, + ); } for (i, &broken_block_pos) in broken_blocks.iter().rev().enumerate() { if let Some(block_state) = affected_block_states.get(i) { - world - .block_registry - .on_state_replaced( - world, - Block::from_state_id(block_state.id), - &broken_block_pos, - block_state.id, // ? - false, - ) - .await; - world - .block_registry - .prepare( - world, - &broken_block_pos, - Block::from_state_id(block_state.id), - block_state.id, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - world.update_neighbors(&broken_block_pos, None).await; + world.block_registry.on_state_replaced( + world, + Block::from_state_id(block_state.id), + &broken_block_pos, + block_state.id, // ? + false, + ); + world.block_registry.prepare( + world, + &broken_block_pos, + Block::from_state_id(block_state.id), + block_state.id, + BlockFlags::NOTIFY_LISTENERS, + ); + world.update_neighbors(&broken_block_pos, None); } } for &moved_block_pos in moved_blocks.iter().rev() { - world.update_neighbors(&moved_block_pos, None).await; + world.update_neighbors(&moved_block_pos, None); } if extend { - world.update_neighbors(&extended_pos, None).await; + world.update_neighbors(&extended_pos, None); } true diff --git a/crates/pumpkin/src/block/blocks/piston/piston_extension.rs b/crates/pumpkin/src/block/blocks/piston/piston_extension.rs index 09d1fb635..6f729abe2 100644 --- a/crates/pumpkin/src/block/blocks/piston/piston_extension.rs +++ b/crates/pumpkin/src/block/blocks/piston/piston_extension.rs @@ -3,8 +3,8 @@ use pumpkin_data::{Block, FacingExt}; use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; +use crate::block::BlockBehaviour; use crate::block::BrokenArgs; -use crate::block::{BlockBehaviour, BlockFuture}; use super::piston::PistonProps; @@ -14,8 +14,8 @@ pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPiston pub struct PistonExtensionBlock; impl BlockBehaviour for PistonExtensionBlock { - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON); let pos = args .position @@ -25,11 +25,9 @@ impl BlockBehaviour for PistonExtensionBlock { let props = PistonProps::from_state_id(new_state, new_block); if props.extended { // TODO: use player - args.world - .break_block(&pos, None, BlockFlags::SKIP_DROPS) - .await; + args.world.break_block(&pos, None, BlockFlags::SKIP_DROPS); } } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/piston/piston_head.rs b/crates/pumpkin/src/block/blocks/piston/piston_head.rs index 9b4f5ff93..8f0e0becf 100644 --- a/crates/pumpkin/src/block/blocks/piston/piston_head.rs +++ b/crates/pumpkin/src/block/blocks/piston/piston_head.rs @@ -3,8 +3,8 @@ use pumpkin_data::{Block, FacingExt}; use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; +use crate::block::BlockBehaviour; use crate::block::blocks::piston::piston::try_move; -use crate::block::{BlockBehaviour, BlockFuture}; use crate::block::{BrokenArgs, OnNeighborUpdateArgs}; use super::piston::PistonProps; @@ -15,50 +15,43 @@ pub(crate) type PistonHeadProperties = pumpkin_data::block_properties::PistonHea pub struct PistonHeadBlock; impl BlockBehaviour for PistonHeadBlock { - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD); - let pos = args - .position - .offset(props.facing.opposite().to_block_direction().to_offset()); - let (new_block, new_state) = args.world.get_block_and_state_id(&pos); - if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block { - let props = PistonProps::from_state_id(new_state, new_block); - if props.extended { - // TODO: use player - args.world - .break_block(&pos, None, BlockFlags::SKIP_DROPS) - .await; - } + fn broken(&self, args: BrokenArgs<'_>) { + let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD); + let pos = args + .position + .offset(props.facing.opposite().to_block_direction().to_offset()); + let (new_block, new_state) = args.world.get_block_and_state_id(&pos); + if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block { + let props = PistonProps::from_state_id(new_state, new_block); + if props.extended { + // TODO: use player + args.world.break_block(&pos, None, BlockFlags::SKIP_DROPS); } - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let head_state_id = args.world.get_block_state_id(args.position); - let head_props = - PistonHeadProperties::from_state_id(head_state_id, &Block::PISTON_HEAD); - if head_props.facing != Facing::Up { - return; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let head_state_id = args.world.get_block_state_id(args.position); + let head_props = PistonHeadProperties::from_state_id(head_state_id, &Block::PISTON_HEAD); + if head_props.facing != Facing::Up { + return; + } + let piston_pos = args.position.offset( + head_props + .facing + .opposite() + .to_block_direction() + .to_offset(), + ); + let piston_block = args.world.get_block(&piston_pos); + if &Block::PISTON == piston_block || &Block::STICKY_PISTON == piston_block { + let up_pos = args + .position + .offset(head_props.facing.to_block_direction().to_offset()); + let upper_block = args.world.get_block(&up_pos); + if upper_block != &Block::REDSTONE_BLOCK { + //Then somebody probably broke the redstone block, try to check if piston should still be extended. + try_move(args.world, piston_block, &piston_pos); } - let piston_pos = args.position.offset( - head_props - .facing - .opposite() - .to_block_direction() - .to_offset(), - ); - let piston_block = args.world.get_block(&piston_pos); - if &Block::PISTON == piston_block || &Block::STICKY_PISTON == piston_block { - let up_pos = args - .position - .offset(head_props.facing.to_block_direction().to_offset()); - let upper_block = args.world.get_block(&up_pos); - if upper_block != &Block::REDSTONE_BLOCK { - //Then somebody probably broke the redstone block, try to check if piston should still be extended. - try_move(args.world, piston_block, &piston_pos).await; - } - } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/bamboo.rs b/crates/pumpkin/src/block/blocks/plant/bamboo.rs index 6523b7278..33e0a8f51 100644 --- a/crates/pumpkin/src/block/blocks/plant/bamboo.rs +++ b/crates/pumpkin/src/block/blocks/plant/bamboo.rs @@ -12,7 +12,7 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::RngExt; -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, blocks::plant::PlantBlockBase}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, blocks::plant::PlantBlockBase}; use crate::block::{ GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs, }; @@ -37,105 +37,89 @@ impl BlockBehaviour for BambooBlock { && args.world.get_block_state(&top.up()).is_air() } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - bone_meal(Arc::clone(args.world), args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + bone_meal(args.world, args.position); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let (block_below, state_id_below) = - args.world.get_block_and_state_id(&args.position.down()); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let (block_below, state_id_below) = + args.world.get_block_and_state_id(&args.position.down()); - if block_below.has_tag(&MINECRAFT_SUPPORTS_BAMBOO) { - let mut props = BambooLikeProperties::from_state_id( - Block::BAMBOO.default_state.id, - &Block::BAMBOO, - ); - if block_below == &Block::BAMBOO_SAPLING { - return Block::BAMBOO.default_state.id; - } else if block_below == &Block::BAMBOO { - let props_below = - BambooLikeProperties::from_state_id(state_id_below, block_below); - if props_below.age > 0 { - props.age = 1; - } + if block_below.has_tag(&MINECRAFT_SUPPORTS_BAMBOO) { + let mut props = + BambooLikeProperties::from_state_id(Block::BAMBOO.default_state.id, &Block::BAMBOO); + if block_below == &Block::BAMBOO_SAPLING { + return Block::BAMBOO.default_state.id; + } else if block_below == &Block::BAMBOO { + let props_below = BambooLikeProperties::from_state_id(state_id_below, block_below); + if props_below.age > 0 { + props.age = 1; + } + } else { + let (block_above, state_id_above) = + args.world.get_block_and_state_id(&args.position.up()); + if block_above == &Block::BAMBOO { + let props_above = + BambooLikeProperties::from_state_id(state_id_above, block_above); + props.age = props_above.age; } else { - let (block_above, state_id_above) = - args.world.get_block_and_state_id(&args.position.up()); - if block_above == &Block::BAMBOO { - let props_above = - BambooLikeProperties::from_state_id(state_id_above, block_above); - props.age = props_above.age; - } else { - return Block::BAMBOO_SAPLING.default_state.id; - } + return Block::BAMBOO_SAPLING.default_state.id; } - return props.to_state_id(&Block::BAMBOO); } - Block::AIR.default_state.id - }) + return props.to_state_id(&Block::BAMBOO); + } + Block::AIR.default_state.id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !::can_place_at(self, args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } else if args.world.get_block(&args.position.down()) == &Block::BAMBOO_SAPLING { - args.world - .set_block_state( - &args.position.down(), - Block::BAMBOO.default_state.id, - BlockFlags::empty(), - ) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !::can_place_at(self, args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } else if args.world.get_block(&args.position.down()) == &Block::BAMBOO_SAPLING { + args.world.set_block_state( + &args.position.down(), + Block::BAMBOO.default_state.id, + BlockFlags::empty(), + ); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !::can_place_at(self, args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !::can_place_at(self, args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + let neighbor_block = args.world.get_block(args.neighbor_position); + if args.direction == BlockDirection::Up && neighbor_block == &Block::BAMBOO { + let neighbor_props = + BambooLikeProperties::from_state_id(args.neighbor_state_id, neighbor_block); + let mut props = BambooLikeProperties::from_state_id(args.state_id, args.block); + if neighbor_props.age > props.age { + props.age = match props.age { + 0 => 1, + _ => 0, + }; + return props.to_state_id(args.block); } - let neighbor_block = args.world.get_block(args.neighbor_position); - if args.direction == BlockDirection::Up && neighbor_block == &Block::BAMBOO { - let neighbor_props = - BambooLikeProperties::from_state_id(args.neighbor_state_id, neighbor_block); - let mut props = BambooLikeProperties::from_state_id(args.state_id, args.block); - if neighbor_props.age > props.age { - props.age = match props.age { - 0 => 1, - _ => 0, - }; - return props.to_state_id(args.block); - } - } - args.state_id - }) + } + args.state_id } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..=3) == 0 { - update_leaves_and_grow(args.world.clone(), args.position).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..=3) == 0 { + update_leaves_and_grow(args.world, args.position); + } } } -async fn update_leaves_and_grow(world: Arc, position: &BlockPos) { +fn update_leaves_and_grow(world: &Arc, position: &BlockPos) { let above_pos = position.up(); let below_pos = position.down(); let two_below_pos = position.down_height(2); @@ -152,7 +136,7 @@ async fn update_leaves_and_grow(world: Arc, position: &BlockPos) { return; } - let bamboo_count = count_bamboo_below(&world, position); + let bamboo_count = count_bamboo_below(world, position); if bamboo_count >= 16 { return; } @@ -178,20 +162,16 @@ async fn update_leaves_and_grow(world: Arc, position: &BlockPos) { BambooLikeProperties::from_state_id(state_id_two_below, block_two_below); props_two_below.leaves = BambooLeaves::None; - world - .set_block_state( - &below_pos, - props_below.to_state_id(block_below), - BlockFlags::NOTIFY_ALL, - ) - .await; - world - .set_block_state( - &two_below_pos, - props_two_below.to_state_id(block_two_below), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &below_pos, + props_below.to_state_id(block_below), + BlockFlags::NOTIFY_ALL, + ); + world.set_block_state( + &two_below_pos, + props_two_below.to_state_id(block_two_below), + BlockFlags::NOTIFY_ALL, + ); } } @@ -201,9 +181,7 @@ async fn update_leaves_and_grow(world: Arc, position: &BlockPos) { !((bamboo_count < 11 || rand::rng().random::() >= 0.25) && bamboo_count != 15), ); - world - .set_block_state(&above_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&above_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); } fn count_bamboo_below(world: &World, pos: &BlockPos) -> usize { @@ -236,12 +214,12 @@ fn count_bamboo_above(world: &World, pos: &BlockPos) -> usize { bamboo_count } -async fn bone_meal(world: Arc, position: &BlockPos) { - let bamboo_below = count_bamboo_below(&world, position); +fn bone_meal(world: &Arc, position: &BlockPos) { + let bamboo_below = count_bamboo_below(world, position); let growth_amount = rand::rng().random_range(1..=2); - for (bamboo_above, _) in (count_bamboo_above(&world, position)..).zip(0..growth_amount) { + for (bamboo_above, _) in (count_bamboo_above(world, position)..).zip(0..growth_amount) { let current_total_height = bamboo_above + bamboo_below + 1; // `next_pos` is the topmost bamboo of the stalk, so the free space we grow into is the @@ -261,7 +239,7 @@ async fn bone_meal(world: Arc, position: &BlockPos) { return; } - update_leaves_and_grow(Arc::clone(&world), &next_pos).await; + update_leaves_and_grow(world, &next_pos); } } diff --git a/crates/pumpkin/src/block/blocks/plant/bamboo_sapling.rs b/crates/pumpkin/src/block/blocks/plant/bamboo_sapling.rs index 38accd834..36eb20690 100644 --- a/crates/pumpkin/src/block/blocks/plant/bamboo_sapling.rs +++ b/crates/pumpkin/src/block/blocks/plant/bamboo_sapling.rs @@ -9,8 +9,8 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::RngExt; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnNeighborUpdateArgs, blocks::plant::PlantBlockBase, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnNeighborUpdateArgs, + blocks::plant::PlantBlockBase, }; #[pumpkin_block("minecraft:bamboo_sapling")] @@ -24,71 +24,63 @@ impl BlockBehaviour for BambooSaplingBlock { && args.world.get_block_state(&above).is_air() } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - grow_bamboo(args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + { + grow_bamboo(args.world, args.position); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { if args.block == &Block::BAMBOO_SAPLING && args.world.get_block(&args.position.up()) == &Block::BAMBOO { - args.world - .set_block_state( - args.position, - Block::BAMBOO.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + args.world.set_block_state( + args.position, + Block::BAMBOO.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); } - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !::can_place_at(self, args.world, args.position) { - return Block::AIR.default_state.id; - } - if args.direction == BlockDirection::Up - && args.world.get_block(args.neighbor_position) == &Block::BAMBOO - { - return Block::BAMBOO.default_state.id; - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !::can_place_at(self, args.world, args.position) { + return Block::AIR.default_state.id; + } + if args.direction == BlockDirection::Up + && args.world.get_block(args.neighbor_position) == &Block::BAMBOO + { + return Block::BAMBOO.default_state.id; + } + args.state_id } - fn random_tick<'a>(&'a self, args: crate::block::RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_above = args.world.get_block_state(&args.position.up()); - if !state_above.is_air() || rand::rng().random_range(0..3) > 0 { - return; - } - grow_bamboo(args.world, args.position).await; - }) + fn random_tick(&self, args: crate::block::RandomTickArgs<'_>) { + let state_above = args.world.get_block_state(&args.position.up()); + if !state_above.is_air() || rand::rng().random_range(0..3) > 0 { + return; + } + grow_bamboo(args.world, args.position); } } -async fn grow_bamboo(world: &std::sync::Arc, position: &BlockPos) { +fn grow_bamboo(world: &std::sync::Arc, position: &BlockPos) { let mut props = BambooLikeProperties::from_state_id(Block::BAMBOO.default_state.id, &Block::BAMBOO); props.leaves = BambooLeaves::Small; - world - .set_block_state( - &position.up(), - props.to_state_id(&Block::BAMBOO), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &position.up(), + props.to_state_id(&Block::BAMBOO), + BlockFlags::NOTIFY_ALL, + ); } impl PlantBlockBase for BambooSaplingBlock { diff --git a/crates/pumpkin/src/block/blocks/plant/big_dripleaf.rs b/crates/pumpkin/src/block/blocks/plant/big_dripleaf.rs index a89e04703..555e8f335 100644 --- a/crates/pumpkin/src/block/blocks/plant/big_dripleaf.rs +++ b/crates/pumpkin/src/block/blocks/plant/big_dripleaf.rs @@ -7,8 +7,8 @@ use crate::block::blocks::plant::big_dripleaf_stem::{ }; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnEntityStepArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityStepArgs, + OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::entity::EntityBase; use crate::entity::ai::pathfinder::node::Coordinate; @@ -30,12 +30,12 @@ use rand::RngExt; pub struct BigDripleafBlock; impl BlockBehaviour for BigDripleafBlock { - fn on_entity_step<'a>(&'a self, args: OnEntityStepArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_step(&self, args: OnEntityStepArgs<'_>) { + { let props = BigDripleafLikeProperties::from_state_id(args.state.id, args.block); if props.tilt == Tilt::None && can_entity_tilt(args.position, args.entity) - && !block_receives_redstone_power(args.world, args.position).await + && !block_receives_redstone_power(args.world, args.position) { set_tilt_and_schedule_tick( args.state.id, @@ -43,96 +43,81 @@ impl BlockBehaviour for BigDripleafBlock { args.position, Tilt::Unstable, None, - ) - .await; + ); } - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - if block_receives_redstone_power(args.world, args.position).await { - reset_tilt(state.id, args.world, args.position).await; - } else { - let props = - BigDripleafLikeProperties::from_state_id(state.id, &Block::BIG_DRIPLEAF); + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let props = BigDripleafLikeProperties::from_state_id(state.id, &Block::BIG_DRIPLEAF); - if props.tilt == Tilt::Unstable { - set_tilt_and_schedule_tick( - state.id, - args.world, - args.position, - Tilt::Partial, - Some(Sound::BlockBigDripleafTiltDown), - ) - .await; - } else if props.tilt == Tilt::Partial { - set_tilt_and_schedule_tick( - state.id, - args.world, - args.position, - Tilt::Full, - Some(Sound::BlockBigDripleafTiltDown), - ) - .await; - } else if props.tilt == Tilt::Full { - reset_tilt(state.id, args.world, args.position).await; - } - } - }) + if props.tilt == Tilt::Unstable { + set_tilt_and_schedule_tick( + state.id, + args.world, + args.position, + Tilt::Partial, + Some(Sound::BlockBigDripleafTiltDown), + ); + } else if props.tilt == Tilt::Partial { + set_tilt_and_schedule_tick( + state.id, + args.world, + args.position, + Tilt::Full, + Some(Sound::BlockBigDripleafTiltDown), + ); + } else if props.tilt == Tilt::Full { + reset_tilt(state.id, args.world, args.position); + } } //TODO: onProjectileHit fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let (support_block, support_block_state_id) = - args.world.get_block_and_state_id(&args.position.down()); - let facing = if support_block == &Block::BIG_DRIPLEAF { - get_dripleaf_facing_dir(support_block_state_id) - } else { - args.player - .living_entity - .entity - .get_horizontal_facing() - .opposite() - }; - let mut dripleaf_props = BigDripleafLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let (support_block, support_block_state_id) = + args.world.get_block_and_state_id(&args.position.down()); + let facing = if support_block == &Block::BIG_DRIPLEAF { + get_dripleaf_facing_dir(support_block_state_id) + } else { + args.player + .living_entity + .entity + .get_horizontal_facing() + .opposite() + }; + let mut dripleaf_props = BigDripleafLikeProperties::default(args.block); - dripleaf_props.facing = facing; - dripleaf_props.waterlogged = args.replacing.water_source(); + dripleaf_props.facing = facing; + dripleaf_props.waterlogged = args.replacing.water_source(); - dripleaf_props.to_state_id(args.block) - }) + dripleaf_props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if block_receives_redstone_power(args.world, args.position).await { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { + if block_receives_redstone_power(args.world, args.position) { let state_id = args.world.get_block_state_id(args.position); - reset_tilt(state_id, args.world, args.position).await; + reset_tilt(state_id, args.world, args.position); } - }) + } } /// if leaf is placed on top of another leaf, turn the lower one into a stem. - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let support_pos = args.position.down(); let (support_block, support_state_id) = args.world.get_block_and_state_id(&support_pos); if support_block == &Block::BIG_DRIPLEAF { @@ -145,30 +130,27 @@ impl BlockBehaviour for BigDripleafBlock { dripleaf_stem_props.facing = old_dripleaf_props.facing; dripleaf_stem_props.waterlogged = old_dripleaf_props.waterlogged; - args.world - .set_block_state( - &support_pos, - dripleaf_stem_props.to_state_id(&Block::BIG_DRIPLEAF_STEM), - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + dripleaf_stem_props.to_state_id(&Block::BIG_DRIPLEAF_STEM), + BlockFlags::empty(), + ); } - }) + } } - /// if the leaf is broken, turn the stem below into a leaf. - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { handle_big_dripleaf_breaking(args.world, args.position).await }) + fn broken(&self, args: BrokenArgs<'_>) { + handle_big_dripleaf_breaking(args.world, args.position); } } -async fn set_tilt_and_schedule_tick( +fn set_tilt_and_schedule_tick( state_id: BlockStateId, world: &Arc, pos: &BlockPos, tilt: Tilt, sound_wrapper: Option, ) { - set_tilt(state_id, world, pos, tilt).await; + set_tilt(state_id, world, pos, tilt); if let Some(tilt_sound) = sound_wrapper { play_tilt_sound(world, pos, tilt_sound); } @@ -186,30 +168,30 @@ async fn set_tilt_and_schedule_tick( ); } } -fn play_tilt_sound(world: &Arc, pos: &BlockPos, tilt_sound: Sound) { - let pitch = rand::rng().random_range(0.8f32..1.2f32); - let v = pos.as_vector3(); - let position = Vector3::new(v.x as f64, v.y as f64, v.z as f64); - world.play_sound_fine(tilt_sound, SoundCategory::Blocks, &position, 1f32, pitch); -} -async fn reset_tilt(state_id: BlockStateId, world: &Arc, pos: &BlockPos) { - set_tilt(state_id, world, pos, Tilt::None).await; + +fn reset_tilt(state_id: BlockStateId, world: &Arc, pos: &BlockPos) { + set_tilt(state_id, world, pos, Tilt::None); let props = BigDripleafLikeProperties::from_state_id(state_id, &Block::BIG_DRIPLEAF); if props.tilt != Tilt::None { play_tilt_sound(world, pos, Sound::BlockBigDripleafTiltUp); } } -async fn set_tilt(state_id: BlockStateId, world: &Arc, pos: &BlockPos, new_tilt: Tilt) { + +fn set_tilt(state_id: BlockStateId, world: &Arc, pos: &BlockPos, new_tilt: Tilt) { let mut props = BigDripleafLikeProperties::from_state_id(state_id, &Block::BIG_DRIPLEAF); props.tilt = new_tilt; - world - .set_block_state( - pos, - props.to_state_id(&Block::BIG_DRIPLEAF), - BlockFlags::NOTIFY_ALL, - ) - .await; - //todo GameEvents? + world.set_block_state( + pos, + props.to_state_id(&Block::BIG_DRIPLEAF), + BlockFlags::NOTIFY_ALL, + ); +} + +fn play_tilt_sound(world: &Arc, pos: &BlockPos, tilt_sound: Sound) { + let pitch = rand::rng().random_range(0.8f32..1.2f32); + let v = pos.as_vector3(); + let position = Vector3::new(v.x as f64, v.y as f64, v.z as f64); + world.play_sound_fine(tilt_sound, SoundCategory::Blocks, &position, 1f32, pitch); } fn can_entity_tilt(pos: &BlockPos, entity: &T) -> bool { entity.get_entity().on_ground.load(Ordering::Relaxed) @@ -229,8 +211,7 @@ impl PlantBlockBase for BigDripleafBlock { let support_block = block_accessor.get_block(pos); can_plant_dripleaf_on_top(support_block) } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/big_dripleaf_stem.rs b/crates/pumpkin/src/block/blocks/plant/big_dripleaf_stem.rs index b4dd25a24..088d73e55 100644 --- a/crates/pumpkin/src/block/blocks/plant/big_dripleaf_stem.rs +++ b/crates/pumpkin/src/block/blocks/plant/big_dripleaf_stem.rs @@ -2,9 +2,7 @@ use std::sync::Arc; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::big_dripleaf::can_plant_dripleaf_on_top; -use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, -}; +use crate::block::{BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use crate::world::World; use pumpkin_data::Block; use pumpkin_data::BlockStateId; @@ -25,22 +23,19 @@ impl BlockBehaviour for BigDripleafStemBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { handle_big_dripleaf_breaking(args.world, args.position).await }) + fn broken(&self, args: BrokenArgs<'_>) { + handle_big_dripleaf_breaking(args.world, args.position); } } impl PlantBlockBase for BigDripleafStemBlock { @@ -49,8 +44,7 @@ impl PlantBlockBase for BigDripleafStemBlock { can_plant_dripleaf_on_top(support_block) } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, @@ -69,7 +63,7 @@ impl PlantBlockBase for BigDripleafStemBlock { block_state } } -pub async fn handle_big_dripleaf_breaking(world: &Arc, position: &BlockPos) { +pub fn handle_big_dripleaf_breaking(world: &Arc, position: &BlockPos) { let support_pos = position.down(); let (support_block, support_state_id) = world.get_block_and_state_id(&support_pos); if support_block == &Block::BIG_DRIPLEAF_STEM { @@ -79,12 +73,10 @@ pub async fn handle_big_dripleaf_breaking(world: &Arc, position: &BlockPo let mut dripleaf_props = BigDripleafLikeProperties::default(&Block::BIG_DRIPLEAF); dripleaf_props.facing = dripleaf_stem_props.facing; dripleaf_props.waterlogged = dripleaf_stem_props.waterlogged; - world - .set_block_state( - &support_pos, - dripleaf_props.to_state_id(&Block::BIG_DRIPLEAF), - BlockFlags::empty(), - ) - .await; + world.set_block_state( + &support_pos, + dripleaf_props.to_state_id(&Block::BIG_DRIPLEAF), + BlockFlags::empty(), + ); } } diff --git a/crates/pumpkin/src/block/blocks/plant/bush.rs b/crates/pumpkin/src/block/blocks/plant/bush.rs index 2061642bc..c74ac4913 100644 --- a/crates/pumpkin/src/block/blocks/plant/bush.rs +++ b/crates/pumpkin/src/block/blocks/plant/bush.rs @@ -2,7 +2,7 @@ use pumpkin_data::BlockId; use pumpkin_data::BlockStateId; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; @@ -19,19 +19,16 @@ impl BlockBehaviour for BushBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/cactus.rs b/crates/pumpkin/src/block/blocks/plant/cactus.rs index 4f04d6e31..cd9b34fb2 100644 --- a/crates/pumpkin/src/block/blocks/plant/cactus.rs +++ b/crates/pumpkin/src/block/blocks/plant/cactus.rs @@ -10,105 +10,85 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::RngExt; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnEntityCollisionArgs, OnScheduledTickArgs, RandomTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, + OnScheduledTickArgs, RandomTickArgs, }; #[pumpkin_block("minecraft:cactus")] pub struct CactusBlock; impl BlockBehaviour for CactusBlock { - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let block_up = args.position.up(); - if args.world.get_block_state(&block_up).is_air() { - let state = args.world.get_block_state(args.position); - let mut props = CactusLikeProperties::from_state_id(state.id, args.block); - let age = props.age; - let mut i = 1; - while args.world.get_block(&args.position.down_height(i)) == &Block::CACTUS { - i += 1; - if 1 == 3 && age == 15 { - return; - } - } - - if age == 8 && can_place_at(args.world.as_ref(), &block_up) { - let d = if i >= 3 { 0.25 } else { 0.1 }; - if rand::rng().random_range(0.0..1.0) <= d { - args.world - .set_block_state( - &block_up, - Block::CACTUS_FLOWER.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - } else if age == 15 && i < 3 { - args.world - .set_block_state( - &block_up, - Block::CACTUS.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - let mut new_props = CactusLikeProperties::default(&Block::CACTUS); - new_props.age = 0; - args.world - .set_block_state( - args.position, - new_props.to_state_id(&Block::CACTUS), - BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK, - ) - .await; - args.world - .update_neighbor(args.position, &Block::CACTUS) - .await; - } - if age < 15 { - props.age = age + 1; - args.world - .set_block_state( - args.position, - props.to_state_id(&Block::CACTUS), - BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK, - ) - .await; + fn random_tick(&self, args: RandomTickArgs<'_>) { + let block_up = args.position.up(); + if args.world.get_block_state(&block_up).is_air() { + let state = args.world.get_block_state(args.position); + let mut props = CactusLikeProperties::from_state_id(state.id, args.block); + let age = props.age; + let mut i = 1; + while args.world.get_block(&args.position.down_height(i)) == &Block::CACTUS { + i += 1; + if 1 == 3 && age == 15 { + return; } } - }) - } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.entity - .damage(args.entity, 1.0, DamageType::CACTUS) - .await; - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + if age == 8 && can_place_at(args.world.as_ref(), &block_up) { + let d = if i >= 3 { 0.25 } else { 0.1 }; + if rand::rng().random_range(0.0..1.0) <= d { + args.world.set_block_state( + &block_up, + Block::CACTUS_FLOWER.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + } else if age == 15 && i < 3 { + args.world.set_block_state( + &block_up, + Block::CACTUS.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + let mut new_props = CactusLikeProperties::default(&Block::CACTUS); + new_props.age = 0; + args.world.set_block_state( + args.position, + new_props.to_state_id(&Block::CACTUS), + BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK, + ); } + if age < 15 { + props.age = age + 1; + args.world.set_block_state( + args.position, + props.to_state_id(&Block::CACTUS), + BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK, + ); + } + } + } - args.state_id - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + { + args.entity.damage(args.entity, 1.0, DamageType::CACTUS); + } + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + + args.state_id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/plant/cactus_flower.rs b/crates/pumpkin/src/block/blocks/plant/cactus_flower.rs index 58235f456..ee69cfe42 100644 --- a/crates/pumpkin/src/block/blocks/plant/cactus_flower.rs +++ b/crates/pumpkin/src/block/blocks/plant/cactus_flower.rs @@ -1,5 +1,5 @@ use crate::block::blocks::plant::PlantBlockBase; -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::{Block, BlockDirection, BlockState}; @@ -14,19 +14,16 @@ impl BlockBehaviour for CactusFlowerBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } @@ -43,8 +40,7 @@ impl PlantBlockBase for CactusFlowerBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/chorus_flower.rs b/crates/pumpkin/src/block/blocks/plant/chorus_flower.rs index 6f3044422..d776b3712 100644 --- a/crates/pumpkin/src/block/blocks/plant/chorus_flower.rs +++ b/crates/pumpkin/src/block/blocks/plant/chorus_flower.rs @@ -17,8 +17,8 @@ use rand::RngExt; use super::chorus_plant; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnScheduledTickArgs, RandomTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, + RandomTickArgs, }, world::World, }; @@ -40,156 +40,144 @@ impl BlockBehaviour for ChorusFlowerBlock { can_survive(args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction != BlockDirection::Up && !can_survive(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction != BlockDirection::Up && !can_survive(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_survive(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_survive(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let above = args.position.up(); - let max_y = args.world.dimension.min_y + args.world.dimension.height - 1; - if args.world.get_block(&above).default_state.is_air() && above.0.y <= max_y { - let state_id = args.world.get_block_state_id(args.position); - let state_props = ChorusFlowerLikeProperties::from_state_id(state_id, args.block); - let current_age = state_props.age; - if current_age < DEAD_AGE { - let mut grow_upwards = false; - let mut pillar_on_support_block = false; - let below_pos = args.position.down(); - let (below_block, _) = args.world.get_block_and_state(&below_pos); + fn random_tick(&self, args: RandomTickArgs<'_>) { + let above = args.position.up(); + let max_y = args.world.dimension.min_y + args.world.dimension.height - 1; + if args.world.get_block(&above).default_state.is_air() && above.0.y <= max_y { + let state_id = args.world.get_block_state_id(args.position); + let state_props = ChorusFlowerLikeProperties::from_state_id(state_id, args.block); + let current_age = state_props.age; + if current_age < DEAD_AGE { + let mut grow_upwards = false; + let mut pillar_on_support_block = false; + let below_pos = args.position.down(); + let (below_block, _) = args.world.get_block_and_state(&below_pos); - if below_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_FLOWER) { - grow_upwards = true; - } else if below_block == &Block::CHORUS_PLANT { - let mut height = 1; - for _ in 0..4 { - let test_pos = args.position.offset(Vector3::new(0, -(height + 1), 0)); - let (test_block, _) = args.world.get_block_and_state(&test_pos); - if test_block != &Block::CHORUS_PLANT { - if test_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_FLOWER) - { - pillar_on_support_block = true; - } - break; + if below_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_FLOWER) { + grow_upwards = true; + } else if below_block == &Block::CHORUS_PLANT { + let mut height = 1; + for _ in 0..4 { + let test_pos = args.position.offset(Vector3::new(0, -(height + 1), 0)); + let (test_block, _) = args.world.get_block_and_state(&test_pos); + if test_block != &Block::CHORUS_PLANT { + if test_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_FLOWER) { + pillar_on_support_block = true; } - height += 1; + break; } - - let max_chance = if pillar_on_support_block { 5 } else { 4 }; - if height < 2 || height <= rand::rng().random_range(0..max_chance) { - grow_upwards = true; - } - } else if below_block.default_state.is_air() { - grow_upwards = true; + height += 1; } - let above_2 = args.position.offset(Vector3::new(0, 2, 0)); - if grow_upwards - && all_neighbors_empty(args.world.as_ref(), &above, None) - && args.world.get_block(&above_2).default_state.is_air() - { + let max_chance = if pillar_on_support_block { 5 } else { 4 }; + if height < 2 || height <= rand::rng().random_range(0..max_chance) { + grow_upwards = true; + } + } else if below_block.default_state.is_air() { + grow_upwards = true; + } + + let above_2 = args.position.offset(Vector3::new(0, 2, 0)); + if grow_upwards + && all_neighbors_empty(args.world.as_ref(), &above, None) + && args.world.get_block(&above_2).default_state.is_air() + { + let plant_state_id = chorus_plant::get_state_with_connections( + args.world.as_ref(), + &Block::CHORUS_PLANT, + args.position, + ); + args.world.set_block_state( + args.position, + plant_state_id, + BlockFlags::NOTIFY_ALL, + ); + place_grown_flower(args.world, &above, current_age); + } else if current_age < 4 { + let mut num_branch_attempts = rand::rng().random_range(0..4); + if pillar_on_support_block { + num_branch_attempts += 1; + } + + let mut created_branch = false; + + for _ in 0..num_branch_attempts { + let direction = HORIZONTAL_DIRECTIONS[rand::rng().random_range(0..4)]; + let target = args.position.offset(direction.to_offset()); + let target_below = target.down(); + + if args.world.get_block(&target).default_state.is_air() + && args.world.get_block(&target_below).default_state.is_air() + && all_neighbors_empty( + args.world.as_ref(), + &target, + Some(direction.opposite()), + ) + { + place_grown_flower(args.world, &target, current_age + 1); + created_branch = true; + } + } + + if created_branch { let plant_state_id = chorus_plant::get_state_with_connections( args.world.as_ref(), &Block::CHORUS_PLANT, args.position, ); - args.world - .set_block_state(args.position, plant_state_id, BlockFlags::NOTIFY_ALL) - .await; - place_grown_flower(args.world, &above, current_age).await; - } else if current_age < 4 { - let mut num_branch_attempts = rand::rng().random_range(0..4); - if pillar_on_support_block { - num_branch_attempts += 1; - } - - let mut created_branch = false; - - for _ in 0..num_branch_attempts { - let direction = HORIZONTAL_DIRECTIONS[rand::rng().random_range(0..4)]; - let target = args.position.offset(direction.to_offset()); - let target_below = target.down(); - - if args.world.get_block(&target).default_state.is_air() - && args.world.get_block(&target_below).default_state.is_air() - && all_neighbors_empty( - args.world.as_ref(), - &target, - Some(direction.opposite()), - ) - { - place_grown_flower(args.world, &target, current_age + 1).await; - created_branch = true; - } - } - - if created_branch { - let plant_state_id = chorus_plant::get_state_with_connections( - args.world.as_ref(), - &Block::CHORUS_PLANT, - args.position, - ); - args.world - .set_block_state( - args.position, - plant_state_id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } else { - place_dead_flower(args.world, args.position).await; - } + args.world.set_block_state( + args.position, + plant_state_id, + BlockFlags::NOTIFY_ALL, + ); } else { - place_dead_flower(args.world, args.position).await; + place_dead_flower(args.world, args.position); } + } else { + place_dead_flower(args.world, args.position); } } - }) + } } } -pub async fn place_grown_flower(world: &Arc, pos: &BlockPos, age: u8) { +pub fn place_grown_flower(world: &Arc, pos: &BlockPos, age: u8) { let mut props = ChorusFlowerLikeProperties::default(&Block::CHORUS_FLOWER); props.age = age; - world - .set_block_state( - pos, - props.to_state_id(&Block::CHORUS_FLOWER), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + pos, + props.to_state_id(&Block::CHORUS_FLOWER), + BlockFlags::NOTIFY_ALL, + ); world.sync_world_event(WorldEvent::SoundChorusGrow, *pos, 0); } -pub async fn place_dead_flower(world: &Arc, pos: &BlockPos) { +pub fn place_dead_flower(world: &Arc, pos: &BlockPos) { let mut props = ChorusFlowerLikeProperties::default(&Block::CHORUS_FLOWER); props.age = DEAD_AGE; - world - .set_block_state( - pos, - props.to_state_id(&Block::CHORUS_FLOWER), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + pos, + props.to_state_id(&Block::CHORUS_FLOWER), + BlockFlags::NOTIFY_ALL, + ); world.sync_world_event(WorldEvent::SoundChorusDeath, *pos, 0); } diff --git a/crates/pumpkin/src/block/blocks/plant/chorus_plant.rs b/crates/pumpkin/src/block/blocks/plant/chorus_plant.rs index 43b684757..ed59b1829 100644 --- a/crates/pumpkin/src/block/blocks/plant/chorus_plant.rs +++ b/crates/pumpkin/src/block/blocks/plant/chorus_plant.rs @@ -11,68 +11,59 @@ use pumpkin_world::{ }; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, }; #[pumpkin_block("minecraft:chorus_plant")] pub struct ChorusPlantBlock; impl BlockBehaviour for ChorusPlantBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - // Compute all 6 face connections immediately so the placed block visually connects to its neighbors. - get_state_with_connections(args.world, args.block, args.position) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + // Compute all 6 face connections immediately so the placed block visually connects to its neighbors. + get_state_with_connections(args.world, args.block, args.position) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { can_survive(args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_survive(args.world, args.position) { - // Schedule delayed destruction so the whole plant cascades down. - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - return args.state_id; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_survive(args.world, args.position) { + // Schedule delayed destruction so the whole plant cascades down. + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + return args.state_id; + } - // Update the single face connection for the direction that changed. - let neighbor_block = args.world.get_block(args.neighbor_position); - let connect = neighbor_block == &Block::CHORUS_PLANT - || neighbor_block == &Block::CHORUS_FLOWER - || (args.direction == BlockDirection::Down - && neighbor_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT)); + // Update the single face connection for the direction that changed. + let neighbor_block = args.world.get_block(args.neighbor_position); + let connect = neighbor_block == &Block::CHORUS_PLANT + || neighbor_block == &Block::CHORUS_FLOWER + || (args.direction == BlockDirection::Down + && neighbor_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT)); - let mut props = - BrownMushroomBlockLikeProperties::from_state_id(args.state_id, args.block); - match args.direction { - BlockDirection::Down => props.down = connect, - BlockDirection::Up => props.up = connect, - BlockDirection::North => props.north = connect, - BlockDirection::South => props.south = connect, - BlockDirection::East => props.east = connect, - BlockDirection::West => props.west = connect, - } - props.to_state_id(args.block) - }) + let mut props = BrownMushroomBlockLikeProperties::from_state_id(args.state_id, args.block); + match args.direction { + BlockDirection::Down => props.down = connect, + BlockDirection::Up => props.up = connect, + BlockDirection::North => props.north = connect, + BlockDirection::South => props.south = connect, + BlockDirection::East => props.east = connect, + BlockDirection::West => props.west = connect, + } + props.to_state_id(args.block) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Destroy if unsupported; breaking propagates neighbor-update callbacks - // to connected chorus blocks, which schedule their own ticks. - if !can_survive(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + // Destroy if unsupported; breaking propagates neighbor-update callbacks + // to connected chorus blocks, which schedule their own ticks. + if !can_survive(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/cocoa.rs b/crates/pumpkin/src/block/blocks/plant/cocoa.rs index 01fd741cf..bc5c5e05f 100644 --- a/crates/pumpkin/src/block/blocks/plant/cocoa.rs +++ b/crates/pumpkin/src/block/blocks/plant/cocoa.rs @@ -8,8 +8,8 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::{ - BlockBehaviour, BlockFuture, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, RandomTickArgs, + BlockBehaviour, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + RandomTickArgs, }; use crate::entity::EntityBase; @@ -53,57 +53,49 @@ impl BlockBehaviour for CocoaBlock { false } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CocoaProperties::default(args.block); - props.age = 0; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = CocoaProperties::default(args.block); + props.age = 0; - let directions = args.player.get_entity().get_entity_facing_order(); - for dir in directions { - if let Some(facing) = dir.to_horizontal_facing() - && Self::can_survive(args.world, args.position, facing) - { - props.facing = facing; - return props.to_state_id(args.block); - } - } - - Block::AIR.default_state.id - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = CocoaProperties::from_state_id(args.state_id, args.block); - if args.direction == props.facing.to_block_direction() - && !Self::can_survive(args.world, args.position, props.facing) + let directions = args.player.get_entity().get_entity_facing_order(); + for dir in directions { + if let Some(facing) = dir.to_horizontal_facing() + && Self::can_survive(args.world, args.position, facing) { - return Block::AIR.default_state.id; + props.facing = facing; + return props.to_state_id(args.block); } - args.state_id - }) + } + + Block::AIR.default_state.id } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::random::().is_multiple_of(5) { - let state_id = args.world.get_block_state_id(args.position); - let mut props = CocoaProperties::from_state_id(state_id, args.block); - if props.age < MAX_AGE { - props.age += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = CocoaProperties::from_state_id(args.state_id, args.block); + if args.direction == props.facing.to_block_direction() + && !Self::can_survive(args.world, args.position, props.facing) + { + return Block::AIR.default_state.id; + } + args.state_id + } + + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::random::().is_multiple_of(5) { + let state_id = args.world.get_block_state_id(args.position); + let mut props = CocoaProperties::from_state_id(state_id, args.block); + if props.age < MAX_AGE { + props.age += 1; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } fn is_valid_bonemeal_target(&self, args: BonemealArgs<'_>) -> bool { @@ -115,20 +107,18 @@ impl BlockBehaviour for CocoaBlock { true } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + { let mut props = CocoaProperties::from_state_id(args.state_id, args.block); if props.age < MAX_AGE { props.age += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } fn rotate( diff --git a/crates/pumpkin/src/block/blocks/plant/crop/beetroot.rs b/crates/pumpkin/src/block/blocks/plant/crop/beetroot.rs index cd28544aa..05936946e 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/beetroot.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/beetroot.rs @@ -6,9 +6,7 @@ use rand::RngExt; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::crop::CropBlockBase; -use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, -}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; type BeetrootProperties = NetherWartLikeProperties; @@ -20,37 +18,30 @@ impl BlockBehaviour for BeetrootBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_plant_on_top(self, args.block_accessor, &args.position.down()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..3) == 0 { - ::random_tick(self, args.world, args.position).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..3) == 0 { + ::random_tick(self, args.world, args.position); + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/carrot.rs b/crates/pumpkin/src/block/blocks/plant/crop/carrot.rs index 78d8e0e35..4f44a6358 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/carrot.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/carrot.rs @@ -3,9 +3,7 @@ use pumpkin_macros::pumpkin_block; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::crop::CropBlockBase; -use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, -}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; #[pumpkin_block("minecraft:carrots")] pub struct CarrotBlock; @@ -15,35 +13,28 @@ impl BlockBehaviour for CarrotBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_plant_on_top(self, args.block_accessor, &args.position.down()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::random_tick(self, args.world, args.position).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + ::random_tick(self, args.world, args.position); } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs b/crates/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs index a4c344f1d..191394db5 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs @@ -7,7 +7,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; @@ -45,27 +45,24 @@ impl BlockBehaviour for AttachedStemBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = AttachedStemProperties::from_state_id(args.state_id, args.block); - if args.direction.to_horizontal_facing() == Some(props.facing) - && args.neighbor_state_id != Self::get_gourd(args.block).default_state.id - { - let mut props = StemProperties::default(Self::get_stem(args.block)); - props.age = 7; - return props.to_state_id(Self::get_stem(args.block)); - } - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = AttachedStemProperties::from_state_id(args.state_id, args.block); + if args.direction.to_horizontal_facing() == Some(props.facing) + && args.neighbor_state_id != Self::get_gourd(args.block).default_state.id + { + let mut props = StemProperties::default(Self::get_stem(args.block)); + props.age = 7; + return props.to_state_id(Self::get_stem(args.block)); + } + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs b/crates/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs index b090cd82f..afe54863f 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs @@ -1,6 +1,5 @@ use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - RandomTickArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, blocks::plant::{ PlantBlockBase, crop::{CropBlockBase, get_available_moisture}, @@ -63,90 +62,76 @@ impl BlockBehaviour for StemBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - let (_, state) = args.world.get_block_and_state_id(args.position); - if StemProperties::from_state_id(state, args.block).age == 7 { - BlockBehaviour::random_tick( - self, - RandomTickArgs { - world: args.world, - block: args.block, - position: args.position, - }, - ) - .await; - } - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); + let (_, state) = args.world.get_block_and_state_id(args.position); + if StemProperties::from_state_id(state, args.block).age == 7 { + BlockBehaviour::random_tick( + self, + RandomTickArgs { + world: args.world, + block: args.block, + position: args.position, + }, + ); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // TODO add light level check - let f: f32 = get_available_moisture(args.world, args.position, args.block).await; - if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 { - let (block, state) = args.world.get_block_and_state_id(args.position); - let props = StemProperties::from_state_id(state, block); - let age = i32::from(props.age); - if age < 7 { - args.world - .set_block_state( - args.position, - Self::state_with_age(block, state, age + 1), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } else { - let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro( - Xoroshiro::from_seed(rand::rng().random()), - )); - let plant_block_pos = args.position.offset(dir.to_offset()); - let plant_block_state = args.world.get_block_state(&plant_block_pos); - let under_block: &Block = args.world.get_block(&plant_block_pos.down()); - if plant_block_state.is_air() - && (under_block == &Block::FARMLAND - || under_block.has_tag(&tag::Block::MINECRAFT_DIRT)) - { - let attached_stem = Self::get_attached_stem(dir, block); - let gourd = Self::get_gourd(block); - args.world - .set_block_state( - &plant_block_pos, - gourd.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - args.world - .set_block_state( - args.position, - attached_stem, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } + fn random_tick(&self, args: RandomTickArgs<'_>) { + // TODO add light level check + let f: f32 = get_available_moisture(args.world, args.position, args.block); + if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 { + let (block, state) = args.world.get_block_and_state_id(args.position); + let props = StemProperties::from_state_id(state, block); + let age = i32::from(props.age); + if age < 7 { + args.world.set_block_state( + args.position, + Self::state_with_age(block, state, age + 1), + BlockFlags::NOTIFY_NEIGHBORS, + ); + } else { + let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro( + Xoroshiro::from_seed(rand::rng().random()), + )); + let plant_block_pos = args.position.offset(dir.to_offset()); + let plant_block_state = args.world.get_block_state(&plant_block_pos); + let under_block: &Block = args.world.get_block(&plant_block_pos.down()); + if plant_block_state.is_air() + && (under_block == &Block::FARMLAND + || under_block.has_tag(&tag::Block::MINECRAFT_DIRT)) + { + let attached_stem = Self::get_attached_stem(dir, block); + let gourd = Self::get_gourd(block); + args.world.set_block_state( + &plant_block_pos, + gourd.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); + args.world.set_block_state( + args.position, + attached_stem, + BlockFlags::NOTIFY_NEIGHBORS, + ); } } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/mod.rs b/crates/pumpkin/src/block/blocks/plant/crop/mod.rs index 6871d3f60..a79ec4d44 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/mod.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/mod.rs @@ -10,10 +10,7 @@ use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::RngExt; -use crate::{ - block::blocks::plant::PlantBlockBase, plugin::api::events::block::block_grow::BlockGrowEvent, - world::World, -}; +use crate::{block::blocks::plant::PlantBlockBase, world::World}; type CropProperties = WheatLikeProperties; type FarmlandProperties = FarmlandLikeProperties; @@ -57,44 +54,25 @@ trait CropBlockBase: PlantBlockBase { self.get_age(state, block) < self.max_age() } - async fn perform_bonemeal(&self, world: &Arc, pos: &BlockPos) { + fn perform_bonemeal(&self, world: &Arc, pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(pos); let age = self.get_age(state, block); let new_age = (age + self.bonemeal_age_increase()).min(self.max_age()); - world - .set_block_state( - pos, - self.state_with_age(block, state, new_age), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + pos, + self.state_with_age(block, state, new_age), + BlockFlags::NOTIFY_LISTENERS, + ); } - async fn random_tick(&self, world: &Arc, pos: &BlockPos) { + fn random_tick(&self, world: &Arc, pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(pos); let age = self.get_age(state, block); if age < self.max_age() { - let f = get_available_moisture(world, pos, block).await; + let f = get_available_moisture(world, pos, block); if rand::rng().random_range(0..=(25.0 / f).floor() as i64) == 0 { - let mut new_state_id = self.state_with_age(block, state, age + 1); - if let Some(server) = world.server.upgrade() { - let mut event = BlockGrowEvent::new( - world.clone(), - block, - state, - Block::from_state_id(new_state_id), - new_state_id, - *pos, - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - return; - } - new_state_id = event.new_state_id; - } - world - .set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + let new_state_id = self.state_with_age(block, state, age + 1); + world.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS); } } } @@ -102,7 +80,7 @@ trait CropBlockBase: PlantBlockBase { //TODO add impl for light level } -pub async fn get_available_moisture(world: &Arc, pos: &BlockPos, block: &Block) -> f32 { +pub fn get_available_moisture(world: &World, pos: &BlockPos, block: &Block) -> f32 { let mut moisture = 1.0; let down_pos = pos.down(); diff --git a/crates/pumpkin/src/block/blocks/plant/crop/nether_wart.rs b/crates/pumpkin/src/block/blocks/plant/crop/nether_wart.rs index 4f5974272..f10ccb6ce 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/nether_wart.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/nether_wart.rs @@ -12,7 +12,7 @@ use rand::RngExt; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, blocks::plant::{PlantBlockBase, crop::CropBlockBase}, }, world::World, @@ -26,25 +26,20 @@ impl BlockBehaviour for NetherWartBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::random_tick(self, args.world, args.position).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + ::random_tick(self, args.world, args.position); } } @@ -79,17 +74,15 @@ impl CropBlockBase for NetherWartBlock { props.to_state_id(block) } - async fn random_tick(&self, world: &Arc, pos: &BlockPos) { + fn random_tick(&self, world: &Arc, pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(pos); let age = self.get_age(state, block); if age < self.max_age() && rand::rng().random_range(0..10) == 0 { - world - .set_block_state( - pos, - self.state_with_age(block, state, age + 1), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + pos, + self.state_with_age(block, state, age + 1), + BlockFlags::NOTIFY_NEIGHBORS, + ); } } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/potatoes.rs b/crates/pumpkin/src/block/blocks/plant/crop/potatoes.rs index 8875ba0d8..ce5d53230 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/potatoes.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/potatoes.rs @@ -3,9 +3,7 @@ use pumpkin_macros::pumpkin_block; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::crop::CropBlockBase; -use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, -}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; #[pumpkin_block("minecraft:potatoes")] pub struct PotatoBlock; @@ -15,35 +13,28 @@ impl BlockBehaviour for PotatoBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_plant_on_top(self, args.block_accessor, &args.position.down()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::random_tick(self, args.world, args.position).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + ::random_tick(self, args.world, args.position); } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/sweet_berry_bush.rs b/crates/pumpkin/src/block/blocks/plant/crop/sweet_berry_bush.rs index bfd02fcc3..d6d91f71b 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/sweet_berry_bush.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/sweet_berry_bush.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnEntityCollisionArgs, RandomTickArgs, UseWithItemArgs, blocks::plant::{PlantBlockBase, crop::CropBlockBase}, registry::BlockActionResult, @@ -31,127 +31,101 @@ impl BlockBehaviour for SweetBerryBushBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let mut props = NetherWartLikeProperties::from_state_id(state_id, args.block); - match props.age { - 2 | 3 => { - let index = props.age; - props.age = 1; - let count: u8 = rand::rng().random_range((index - 1)..=(index)); - for _ in 0..count { - args.world - .drop_stack( - args.position, - ItemStack::new(1, &Item::SWEET_BERRIES), // - ) - .await; - } - args.world - .set_block_state( - args.position, - props.to_state_id(&Block::SWEET_BERRY_BUSH), - BlockFlags::NOTIFY_ALL, - ) - .await; - BlockActionResult::SuccessServer + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + let mut props = NetherWartLikeProperties::from_state_id(state_id, args.block); + match props.age { + 2 | 3 => { + let index = props.age; + props.age = 1; + let count: u8 = rand::rng().random_range((index - 1)..=(index)); + for _ in 0..count { + args.world.drop_stack( + args.position, + ItemStack::new(1, &Item::SWEET_BERRIES), // + ); } - _ => BlockActionResult::Pass, + args.world.set_block_state( + args.position, + props.to_state_id(&Block::SWEET_BERRY_BUSH), + BlockFlags::NOTIFY_ALL, + ); + BlockActionResult::SuccessServer } - }) + _ => BlockActionResult::Pass, + } } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let props = NetherWartLikeProperties::from_state_id(state_id, &Block::SWEET_BERRY_BUSH); - if props.age != 3 && args.item_stack.get_item() == &Item::BONE_MEAL { - BlockActionResult::Pass - } else { - BlockActionResult::PassToDefaultBlockAction - } - }) + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + let props = NetherWartLikeProperties::from_state_id(state_id, &Block::SWEET_BERRY_BUSH); + if props.age != 3 && args.item_stack.get_item() == &Item::BONE_MEAL { + BlockActionResult::Pass + } else { + BlockActionResult::PassToDefaultBlockAction + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = args.entity.get_entity(); + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let entity = args.entity.get_entity(); - let living_entity_opt = args.entity.get_living_entity(); - let Some(living_entity) = living_entity_opt else { - return; - }; - if entity.entity_type == &EntityType::FOX || entity.entity_type == &EntityType::BEE { - return; - } - entity - .slow_movement(args.state, Vector3::new(0.8, 0.75, 0.8)) - .await; - let mov = if living_entity.is_player() { - living_entity.get_movement() - } else { - entity.last_pos.load() - entity.pos.load() - }; + let living_entity_opt = args.entity.get_living_entity(); + let Some(living_entity) = living_entity_opt else { + return; + }; + if entity.entity_type == &EntityType::FOX || entity.entity_type == &EntityType::BEE { + return; + } + entity.slow_movement(args.state, Vector3::new(0.8, 0.75, 0.8)); + let mov = if living_entity.is_player() { + living_entity.get_movement() + } else { + entity.last_pos.load() - entity.pos.load() + }; - let state_id = args.world.get_block_state_id(args.position); - let props = NetherWartLikeProperties::from_state_id(state_id, args.block); - if props.age == 0 { - return; - } + let state_id = args.world.get_block_state_id(args.position); + let props = NetherWartLikeProperties::from_state_id(state_id, args.block); + if props.age == 0 { + return; + } - if mov.horizontal_length_squared() <= 0.0 - || (mov.x.abs() < 0.003 && mov.z.abs() < 0.003) - { - return; - } + if mov.horizontal_length_squared() <= 0.0 || (mov.x.abs() < 0.003 && mov.z.abs() < 0.003) { + return; + } - args.entity - .damage(args.entity, 1.0, DamageType::SWEET_BERRY_BUSH) - .await; - }) + args.entity + .damage(args.entity, 1.0, DamageType::SWEET_BERRY_BUSH); } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..5) == 0 { - ::random_tick(self, args.world, args.position).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..5) == 0 { + ::random_tick(self, args.world, args.position); + } } } impl PlantBlockBase for SweetBerryBushBlock { - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, @@ -192,7 +166,7 @@ impl CropBlockBase for SweetBerryBushBlock { props.to_state_id(block) } - async fn random_tick(&self, world: &Arc, pos: &BlockPos) { + fn random_tick(&self, world: &Arc, pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(pos); let age = self.get_age(state, block); if age < self.max_age() { @@ -201,13 +175,11 @@ impl CropBlockBase for SweetBerryBushBlock { if state_above.is_full_cube() || state_above.is_solid() { return; } - world - .set_block_state( - pos, - self.state_with_age(block, state, age + 1), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + pos, + self.state_with_age(block, state, age + 1), + BlockFlags::NOTIFY_NEIGHBORS, + ); } } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/torch_flower.rs b/crates/pumpkin/src/block/blocks/plant/crop/torch_flower.rs index 0f6eebc05..8d44e15e8 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/torch_flower.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/torch_flower.rs @@ -6,9 +6,7 @@ use rand::RngExt; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::crop::CropBlockBase; -use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, -}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; type TorchFlowerProperties = TorchflowerCropLikeProperties; @@ -20,37 +18,30 @@ impl BlockBehaviour for TorchFlowerBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_plant_on_top(self, args.block_accessor, &args.position.down()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..2) != 0 { - ::random_tick(self, args.world, args.position).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..2) != 0 { + ::random_tick(self, args.world, args.position); + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/crop/wheat.rs b/crates/pumpkin/src/block/blocks/plant/crop/wheat.rs index 07f0e6039..13d6dfc6f 100644 --- a/crates/pumpkin/src/block/blocks/plant/crop/wheat.rs +++ b/crates/pumpkin/src/block/blocks/plant/crop/wheat.rs @@ -3,9 +3,7 @@ use pumpkin_macros::pumpkin_block; use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::crop::CropBlockBase; -use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, -}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; #[pumpkin_block("minecraft:wheat")] pub struct WheatBlock; @@ -15,35 +13,28 @@ impl BlockBehaviour for WheatBlock { ::is_valid_bonemeal_target(self, args.world, args.position) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::perform_bonemeal(self, args.world, args.position).await; - }) + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + ::perform_bonemeal(self, args.world, args.position); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_plant_on_top(self, args.block_accessor, &args.position.down()) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - ::random_tick(self, args.world, args.position).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + ::random_tick(self, args.world, args.position); } } diff --git a/crates/pumpkin/src/block/blocks/plant/dry_vegetation.rs b/crates/pumpkin/src/block/blocks/plant/dry_vegetation.rs index 33cde42dd..4a7cc03ed 100644 --- a/crates/pumpkin/src/block/blocks/plant/dry_vegetation.rs +++ b/crates/pumpkin/src/block/blocks/plant/dry_vegetation.rs @@ -5,7 +5,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::seq::SliceRandom; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; @@ -33,16 +33,14 @@ impl BlockBehaviour for DryVegetationBlock { }) } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + { if args.block == &Block::SHORT_DRY_GRASS { - args.world - .set_block_state( - args.position, - Block::TALL_DRY_GRASS.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + Block::TALL_DRY_GRASS.default_state.id, + BlockFlags::NOTIFY_ALL, + ); return; } @@ -52,34 +50,29 @@ impl BlockBehaviour for DryVegetationBlock { let position = args.position.offset(direction.to_offset()); can_spread_to(args.world, position).then_some(position) }) { - args.world - .set_block_state( - &position, - Block::SHORT_DRY_GRASS.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + &position, + Block::SHORT_DRY_GRASS.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } - }) + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/eyeblossom.rs b/crates/pumpkin/src/block/blocks/plant/eyeblossom.rs index d8c0234bd..51241f9ac 100644 --- a/crates/pumpkin/src/block/blocks/plant/eyeblossom.rs +++ b/crates/pumpkin/src/block/blocks/plant/eyeblossom.rs @@ -17,7 +17,7 @@ use rand::RngExt; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnScheduledTickArgs, RandomTickArgs, blocks::plant::PlantBlockBase, }, world::World, @@ -39,66 +39,58 @@ impl BlockBehaviour for EyeblossomBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !::can_place_at(self, args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - return; - } + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !::can_place_at(self, args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + return; + } - let was_open = args.block == &Block::OPEN_EYEBLOSSOM; - if try_changing_state(args.world, args.block, args.position).await { - let sound = if was_open { - Sound::BlockEyeblossomClose - } else { - Sound::BlockEyeblossomOpen - }; - args.world.play_sound( - sound, - SoundCategory::Blocks, - &args.position.to_centered_f64(), - ); - } - }) + let was_open = args.block == &Block::OPEN_EYEBLOSSOM; + if try_changing_state(args.world, args.block, args.position) { + let sound = if was_open { + Sound::BlockEyeblossomClose + } else { + Sound::BlockEyeblossomOpen + }; + args.world.play_sound( + sound, + SoundCategory::Blocks, + &args.position.to_centered_f64(), + ); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let was_open = args.block == &Block::OPEN_EYEBLOSSOM; - if try_changing_state(args.world, args.block, args.position).await { - let sound = if was_open { - Sound::BlockEyeblossomCloseLong - } else { - Sound::BlockEyeblossomOpenLong - }; - args.world.play_sound( - sound, - SoundCategory::Blocks, - &args.position.to_centered_f64(), - ); - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + let was_open = args.block == &Block::OPEN_EYEBLOSSOM; + if try_changing_state(args.world, args.block, args.position) { + let sound = if was_open { + Sound::BlockEyeblossomCloseLong + } else { + Sound::BlockEyeblossomOpenLong + }; + args.world.play_sound( + sound, + SoundCategory::Blocks, + &args.position.to_centered_f64(), + ); + } } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + { if args.world.level_info.load().difficulty == Difficulty::Peaceful { return; } @@ -115,20 +107,24 @@ impl BlockBehaviour for EyeblossomBlock { show_icon: true, blend: true, }; - living_entity.add_effect(effect).await; + living_entity.add_effect(effect); } - }) + } } } impl PlantBlockBase for EyeblossomBlock {} -pub async fn try_changing_state(world: &Arc, current_block: &Block, pos: &BlockPos) -> bool { +pub fn try_changing_state(world: &Arc, current_block: &Block, pos: &BlockPos) -> bool { let is_open = current_block == &Block::OPEN_EYEBLOSSOM; let should_be_open = if world.dimension == Dimension::OVERWORLD || world.dimension == Dimension::OVERWORLD_CAVES { - world.level_time.lock().await.is_night() + world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_night() } else { is_open }; @@ -143,9 +139,7 @@ pub async fn try_changing_state(world: &Arc, current_block: &Block, pos: &Block::OPEN_EYEBLOSSOM }; - world - .set_block_state(pos, new_block.default_state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, new_block.default_state.id, BlockFlags::NOTIFY_ALL); world.spawn_particle( pos.to_centered_f64(), diff --git a/crates/pumpkin/src/block/blocks/plant/flower.rs b/crates/pumpkin/src/block/blocks/plant/flower.rs index 41882f752..bd20ea591 100644 --- a/crates/pumpkin/src/block/blocks/plant/flower.rs +++ b/crates/pumpkin/src/block/blocks/plant/flower.rs @@ -2,7 +2,7 @@ use pumpkin_data::BlockStateId; use pumpkin_macros::pumpkin_block_from_tag; use crate::block::blocks::plant::PlantBlockBase; -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; #[pumpkin_block_from_tag("minecraft:small_flowers")] pub struct FlowerBlock; @@ -12,19 +12,16 @@ impl BlockBehaviour for FlowerBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/flowerbed.rs b/crates/pumpkin/src/block/blocks/plant/flowerbed.rs index b2fd010c4..fd5f7c651 100644 --- a/crates/pumpkin/src/block/blocks/plant/flowerbed.rs +++ b/crates/pumpkin/src/block/blocks/plant/flowerbed.rs @@ -5,8 +5,8 @@ use pumpkin_data::{Block, BlockId, tag}; use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, - GetStateForNeighborUpdateArgs, OnPlaceArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, + OnPlaceArgs, }; use super::segmented::Segmented; @@ -31,23 +31,20 @@ impl BlockBehaviour for FlowerbedBlock { Segmented::can_update_at(self, args) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { Segmented::on_place(self, args) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/fungus.rs b/crates/pumpkin/src/block/blocks/plant/fungus.rs index c12a2c036..5a8c0bb82 100644 --- a/crates/pumpkin/src/block/blocks/plant/fungus.rs +++ b/crates/pumpkin/src/block/blocks/plant/fungus.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs}; +use crate::block::{BlockBehaviour, BlockMetadata, CanPlaceAtArgs}; use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; use pumpkin_data::BlockStateId; use pumpkin_data::tag::Taggable; @@ -17,19 +17,16 @@ impl BlockBehaviour for FungusBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } impl PlantBlockBase for FungusBlock { diff --git a/crates/pumpkin/src/block/blocks/plant/kelp.rs b/crates/pumpkin/src/block/blocks/plant/kelp.rs index 35972cec1..55861ba28 100644 --- a/crates/pumpkin/src/block/blocks/plant/kelp.rs +++ b/crates/pumpkin/src/block/blocks/plant/kelp.rs @@ -1,7 +1,7 @@ use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, CanPlaceAtArgs, - GetStateForNeighborUpdateArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + PlacedArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, WaterLikeProperties}; @@ -21,56 +21,47 @@ impl BlockBehaviour for KelpBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let support_pos = args.position.down(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::KELP { - args.world - .set_block_state( - &support_pos, - Block::KELP_PLANT.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::KELP_PLANT.default_state.id, + BlockFlags::empty(), + ); } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { let support_pos = args.position.down(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::KELP_PLANT { - args.world - .set_block_state( - &support_pos, - Block::KELP.default_state.id, - BlockFlags::empty(), - ) - .await; - args.world - .set_block_state( - args.position, - Block::WATER.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::KELP.default_state.id, + BlockFlags::empty(), + ); + args.world.set_block_state( + args.position, + Block::WATER.default_state.id, + BlockFlags::empty(), + ); } - }) + } } } @@ -113,8 +104,7 @@ impl PlantBlockBase for KelpBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/leaf_litter.rs b/crates/pumpkin/src/block/blocks/plant/leaf_litter.rs index 0598bd4be..a9502671b 100644 --- a/crates/pumpkin/src/block/blocks/plant/leaf_litter.rs +++ b/crates/pumpkin/src/block/blocks/plant/leaf_litter.rs @@ -3,8 +3,7 @@ use pumpkin_data::{Block, BlockDirection}; use pumpkin_macros::pumpkin_block; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, + BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, }; use super::segmented::Segmented; @@ -24,23 +23,21 @@ impl BlockBehaviour for LeafLitterBlock { Segmented::can_update_at(self, args) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { Segmented::on_place(self, args).await }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + Segmented::on_place(self, args) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - let block_below_state = args.world.get_block_state(&args.position.down()); - if !block_below_state.is_side_solid(BlockDirection::Up) { - return Block::AIR.default_state.id; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down { + let block_below_state = args.world.get_block_state(&args.position.down()); + if !block_below_state.is_side_solid(BlockDirection::Up) { + return Block::AIR.default_state.id; } - args.state_id - }) + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/plant/lily_pad.rs b/crates/pumpkin/src/block/blocks/plant/lily_pad.rs index 2811cfc89..f2dd3d2e2 100644 --- a/crates/pumpkin/src/block/blocks/plant/lily_pad.rs +++ b/crates/pumpkin/src/block/blocks/plant/lily_pad.rs @@ -4,7 +4,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::{BlockAccessor, BlockFlags}; -use crate::block::{BlockFuture, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; +use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs}; @@ -12,8 +12,8 @@ use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs}; pub struct LilyPadBlock; impl BlockBehaviour for LilyPadBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + { // Proberbly not the best solution, but works if args .entity @@ -23,29 +23,25 @@ impl BlockBehaviour for LilyPadBlock { .ends_with("_boat") { args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; + .break_block(args.position, None, BlockFlags::empty()); } - }) + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/mangrove_propagule.rs b/crates/pumpkin/src/block/blocks/plant/mangrove_propagule.rs index 057c6ef98..9c2d5a1ca 100644 --- a/crates/pumpkin/src/block/blocks/plant/mangrove_propagule.rs +++ b/crates/pumpkin/src/block/blocks/plant/mangrove_propagule.rs @@ -11,8 +11,8 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::{ - BlockBehaviour, BlockFuture, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, RandomTickArgs, + BlockBehaviour, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + RandomTickArgs, }; use crate::world::World; @@ -57,7 +57,7 @@ impl MangrovePropaguleBlock { props.to_state_id(&Block::MANGROVE_PROPAGULE) } - async fn advance_tree( + fn advance_tree( world: &Arc, pos: &BlockPos, block: &Block, @@ -65,16 +65,13 @@ impl MangrovePropaguleBlock { ) { if props.stage == 0 { props.stage = 1; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); } else { use crate::plugin::api::events::world::structure_grow::{StructureGrowEvent, TreeType}; let mut event = StructureGrowEvent::new(*pos, TreeType::Mangrove, false); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } - let _ = event.cancelled; } } } @@ -86,52 +83,42 @@ impl BlockBehaviour for MangrovePropaguleBlock { Self::can_survive(args.block_accessor, args.position, &props) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = MangrovePropaguleLikeProperties::from_state_id( - args.block.default_state.id, - args.block, + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + MangrovePropaguleLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.hanging = false; + props.age = MAX_AGE; + props.stage = 0; + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = MangrovePropaguleLikeProperties::from_state_id(args.state_id, args.block); + if !Self::can_survive(args.world, args.position, &props) { + return Block::AIR.default_state.id; + } + args.state_id + } + + fn random_tick(&self, args: RandomTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let mut props = MangrovePropaguleLikeProperties::from_state_id(state_id, args.block); + if !props.hanging { + if rand::random::().is_multiple_of(7) { + Self::advance_tree(args.world, args.position, args.block, props); + } + } else if props.age < MAX_AGE { + props.age += 1; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, ); - props.hanging = false; - props.age = MAX_AGE; - props.stage = 0; - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = MangrovePropaguleLikeProperties::from_state_id(args.state_id, args.block); - if !Self::can_survive(args.world, args.position, &props) { - return Block::AIR.default_state.id; - } - args.state_id - }) - } - - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let mut props = MangrovePropaguleLikeProperties::from_state_id(state_id, args.block); - if !props.hanging { - if rand::random::().is_multiple_of(7) { - Self::advance_tree(args.world, args.position, args.block, props).await; - } - } else if props.age < MAX_AGE { - props.age += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } - }) + } } fn is_valid_bonemeal_target(&self, args: BonemealArgs<'_>) -> bool { @@ -148,22 +135,20 @@ impl BlockBehaviour for MangrovePropaguleBlock { } } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + { let mut props = MangrovePropaguleLikeProperties::from_state_id(args.state_id, args.block); if props.hanging && props.age < MAX_AGE { props.age += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } else { - Self::advance_tree(args.world, args.position, args.block, props).await; + Self::advance_tree(args.world, args.position, args.block, props); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/mod.rs b/crates/pumpkin/src/block/blocks/plant/mod.rs index 504b211ac..093f3d270 100644 --- a/crates/pumpkin/src/block/blocks/plant/mod.rs +++ b/crates/pumpkin/src/block/blocks/plant/mod.rs @@ -45,7 +45,7 @@ trait PlantBlockBase { block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_VEGETATION) } - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs b/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs index b2a44b570..1ea0134c8 100644 --- a/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs +++ b/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs @@ -8,8 +8,8 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::RngExt; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BonemealArgs, CanPlaceAtArgs, - GetStateForNeighborUpdateArgs, RandomTickArgs, blocks::plant::PlantBlockBase, + BlockBehaviour, BlockMetadata, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + RandomTickArgs, blocks::plant::PlantBlockBase, }; use crate::plugin::api::events::world::structure_grow::{StructureGrowEvent, TreeType}; use crate::world::World; @@ -47,15 +47,12 @@ impl MushroomPlantBlock { return true; } - let is_dark_enough = match world { - Some(world) => world.get_max_local_raw_brightness(pos) < 13, - None => true, - }; + let is_dark_enough = world.is_none_or(|world| world.get_max_local_raw_brightness(pos) < 13); is_dark_enough && Self::may_place_on(block_accessor.get_block_state(&below_pos)) } - pub async fn grow_mushroom( + pub fn grow_mushroom( world: &Arc, pos: &BlockPos, block: &Block, @@ -71,7 +68,7 @@ impl MushroomPlantBlock { let mut event = StructureGrowEvent::new(*pos, species, true); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return false; @@ -112,21 +109,19 @@ impl MushroomPlantBlock { } } - world - .set_block_state(pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); if block == &Block::BROWN_MUSHROOM { - place_huge_brown_mushroom(world, pos, tree_height).await; + place_huge_brown_mushroom(world, pos, tree_height); } else if block == &Block::RED_MUSHROOM { - place_huge_red_mushroom(world, pos, tree_height).await; + place_huge_red_mushroom(world, pos, tree_height); } true } } -async fn place_huge_brown_mushroom(world: &Arc, pos: &BlockPos, tree_height: i32) { +fn place_huge_brown_mushroom(world: &Arc, pos: &BlockPos, tree_height: i32) { let radius = 3; let cap_y = pos.0.y + tree_height; for j in -radius..=radius { @@ -148,9 +143,7 @@ async fn place_huge_brown_mushroom(world: &Arc, pos: &BlockPos, tree_heig }; let state_id = props.to_state_id(&Block::BROWN_MUSHROOM_BLOCK); let cap_pos = BlockPos::new(pos.0.x + j, cap_y, pos.0.z + k); - world - .set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL); } } @@ -165,13 +158,11 @@ async fn place_huge_brown_mushroom(world: &Arc, pos: &BlockPos, tree_heig let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM); for i in 0..tree_height { let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z); - world - .set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL); } } -async fn place_huge_red_mushroom(world: &Arc, pos: &BlockPos, tree_height: i32) { +fn place_huge_red_mushroom(world: &Arc, pos: &BlockPos, tree_height: i32) { let radius = 2; for i in (tree_height - 3)..=tree_height { let j = if i < tree_height { radius } else { radius - 1 }; @@ -196,9 +187,7 @@ async fn place_huge_red_mushroom(world: &Arc, pos: &BlockPos, tree_height }; let state_id = props.to_state_id(&Block::RED_MUSHROOM_BLOCK); let cap_pos = BlockPos::new(pos.0.x + l, pos.0.y + i, pos.0.z + m); - world - .set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL); } } } @@ -214,9 +203,7 @@ async fn place_huge_red_mushroom(world: &Arc, pos: &BlockPos, tree_height let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM); for i in 0..tree_height { let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z); - world - .set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL); } } @@ -225,74 +212,67 @@ impl BlockBehaviour for MushroomPlantBlock { Self::can_survive(args.block_accessor, args.world, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !Self::can_survive(args.world, Some(args.world), args.position) { - return Block::AIR.default_state.id; - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !Self::can_survive(args.world, Some(args.world), args.position) { + return Block::AIR.default_state.id; + } + args.state_id } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::rng().random_range(0..25) != 0 { - return; - } - let pos = *args.position; - let world = args.world; - let this_block = args.block; - let state_id = world.get_block_state_id(&pos); + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::rng().random_range(0..25) != 0 { + return; + } + let pos = *args.position; + let world = args.world; + let this_block = args.block; + let state_id = world.get_block_state_id(&pos); - let mut max = 5; - for dx in -4..=4 { - for dy in -1..=1 { - for dz in -4..=4 { - let check_pos = pos.add(dx, dy, dz); - if world.is_loaded(&check_pos) && world.get_block(&check_pos) == this_block - { - max -= 1; - if max <= 0 { - return; - } + let mut max = 5; + for dx in -4..=4 { + for dy in -1..=1 { + for dz in -4..=4 { + let check_pos = pos.add(dx, dy, dz); + if world.is_loaded(&check_pos) && world.get_block(&check_pos) == this_block { + max -= 1; + if max <= 0 { + return; } } } } + } - let mut current_pos = pos; - let mut offset = current_pos.add( - rand::rng().random_range(0..3) - 1, - rand::rng().random_range(0..2) - rand::rng().random_range(0..2), - rand::rng().random_range(0..3) - 1, - ); - - for _ in 0..4 { - if world.is_loaded(&offset) - && world.get_block_state(&offset).is_air() - && Self::can_survive(world.as_ref(), Some(world.as_ref()), &offset) - { - current_pos = offset; - } - offset = current_pos.add( - rand::rng().random_range(0..3) - 1, - rand::rng().random_range(0..2) - rand::rng().random_range(0..2), - rand::rng().random_range(0..3) - 1, - ); - } + let mut current_pos = pos; + let mut offset = current_pos.add( + rand::rng().random_range(0..3) - 1, + rand::rng().random_range(0..2) - rand::rng().random_range(0..2), + rand::rng().random_range(0..3) - 1, + ); + for _ in 0..4 { if world.is_loaded(&offset) && world.get_block_state(&offset).is_air() && Self::can_survive(world.as_ref(), Some(world.as_ref()), &offset) { - world - .set_block_state(&offset, state_id, BlockFlags::NOTIFY_LISTENERS) - .await; + current_pos = offset; } - }) + offset = current_pos.add( + rand::rng().random_range(0..3) - 1, + rand::rng().random_range(0..2) - rand::rng().random_range(0..2), + rand::rng().random_range(0..3) - 1, + ); + } + + if world.is_loaded(&offset) + && world.get_block_state(&offset).is_air() + && Self::can_survive(world.as_ref(), Some(world.as_ref()), &offset) + { + world.set_block_state(&offset, state_id, BlockFlags::NOTIFY_LISTENERS); + } } fn is_valid_bonemeal_target(&self, args: BonemealArgs<'_>) -> bool { @@ -310,10 +290,8 @@ impl BlockBehaviour for MushroomPlantBlock { rand::rng().random::() < 0.4 } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - Self::grow_mushroom(args.world, args.position, args.block, args.state_id).await; - }) + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + Self::grow_mushroom(args.world, args.position, args.block, args.state_id); } } diff --git a/crates/pumpkin/src/block/blocks/plant/nether_sprouts.rs b/crates/pumpkin/src/block/blocks/plant/nether_sprouts.rs index 64f5a69db..ed3e45954 100644 --- a/crates/pumpkin/src/block/blocks/plant/nether_sprouts.rs +++ b/crates/pumpkin/src/block/blocks/plant/nether_sprouts.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs}; use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; use pumpkin_data::BlockStateId; use pumpkin_data::tag::{self, Taggable}; @@ -10,19 +10,16 @@ impl BlockBehaviour for NetherSproutsBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } impl PlantBlockBase for NetherSproutsBlock { diff --git a/crates/pumpkin/src/block/blocks/plant/roots.rs b/crates/pumpkin/src/block/blocks/plant/roots.rs index 70566352b..89d93673d 100644 --- a/crates/pumpkin/src/block/blocks/plant/roots.rs +++ b/crates/pumpkin/src/block/blocks/plant/roots.rs @@ -3,7 +3,6 @@ use pumpkin_data::{Block, BlockId, BlockStateId, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; -use crate::block::BlockFuture; use crate::block::{ BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, @@ -22,19 +21,16 @@ impl BlockBehaviour for RootsBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/sapling.rs b/crates/pumpkin/src/block/blocks/plant/sapling.rs index 0ebec3391..d66c1f66b 100644 --- a/crates/pumpkin/src/block/blocks/plant/sapling.rs +++ b/crates/pumpkin/src/block/blocks/plant/sapling.rs @@ -10,8 +10,7 @@ use pumpkin_world::world::BlockFlags; use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - RandomTickArgs, + BlockBehaviour, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, }; use crate::plugin::api::events::world::structure_grow::{StructureGrowEvent, TreeType}; use crate::world::World; @@ -36,7 +35,7 @@ impl SaplingBlock { } } - pub async fn advance_tree( + pub fn advance_tree( world: &Arc, pos: &BlockPos, block: &Block, @@ -47,9 +46,7 @@ impl SaplingBlock { let mut props = OakSaplingLikeProperties::from_state_id(state_id, block); if props.stage == 0 { props.stage = 1; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); return; } } @@ -57,10 +54,8 @@ impl SaplingBlock { let tree_type = Self::get_tree_type(block); let mut event = StructureGrowEvent::new(*pos, tree_type, bone_meal); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } - let _ = event.cancelled; - // TODO: Generate tree once tree feature generation in world is hooked up } } @@ -69,28 +64,23 @@ impl BlockBehaviour for SaplingBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if rand::random::().is_multiple_of(7) { - let state_id = args.world.get_block_state_id(args.position); - Self::advance_tree(args.world, args.position, args.block, state_id, false).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + if rand::random::().is_multiple_of(7) { + let state_id = args.world.get_block_state_id(args.position); + Self::advance_tree(args.world, args.position, args.block, state_id, false); + } } fn is_valid_bonemeal_target(&self, _args: BonemealArgs<'_>) -> bool { @@ -101,10 +91,10 @@ impl BlockBehaviour for SaplingBlock { rand::random::() < 0.45 } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - Self::advance_tree(args.world, args.position, args.block, args.state_id, true).await; - }) + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + { + Self::advance_tree(args.world, args.position, args.block, args.state_id, true); + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/sea_pickles.rs b/crates/pumpkin/src/block/blocks/plant/sea_pickles.rs index 6799f6f72..dc5a926e3 100644 --- a/crates/pumpkin/src/block/blocks/plant/sea_pickles.rs +++ b/crates/pumpkin/src/block/blocks/plant/sea_pickles.rs @@ -1,10 +1,10 @@ +use crate::block::BlockIsReplacing; use crate::block::blocks::plant::PlantBlockBase; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, UseWithItemArgs, }; -use crate::block::{BlockFuture, BlockIsReplacing}; use crate::entity::EntityBase; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::BlockProperties; @@ -23,11 +23,8 @@ type SeaPickleProperties = pumpkin_data::block_properties::SeaPickleLikeProperti pub struct SeaPickleBlock; impl BlockBehaviour for SeaPickleBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { if args.item_stack.item != &Item::BONE_MEAL || !args .world @@ -72,13 +69,11 @@ impl BlockBehaviour for SeaPickleBlock { let mut sea_pickle_prop = SeaPickleProperties::default(args.block); sea_pickle_prop.pickles = rand::rng().random_range(1..=4); - args.world - .set_block_state( - &lv, - sea_pickle_prop.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + &lv, + sea_pickle_prop.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } } if count < 2 { @@ -92,34 +87,30 @@ impl BlockBehaviour for SeaPickleBlock { } let mut sea_pickle_prop = SeaPickleProperties::default(args.block); sea_pickle_prop.pickles = 4; - args.world - .set_block_state( - args.position, - sea_pickle_prop.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + args.position, + sea_pickle_prop.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); BlockActionResult::Consume - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.player.get_entity().pose.load() != EntityPose::Crouching - && let BlockIsReplacing::Itself(state_id) = args.replacing - { - let mut sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block); - if sea_pickle_prop.pickles < 4 { - sea_pickle_prop.pickles += 1; - } - return sea_pickle_prop.to_state_id(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.player.get_entity().pose.load() != EntityPose::Crouching + && let BlockIsReplacing::Itself(state_id) = args.replacing + { + let mut sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block); + if sea_pickle_prop.pickles < 4 { + sea_pickle_prop.pickles += 1; } + return sea_pickle_prop.to_state_id(args.block); + } - let mut sea_pickle_prop = SeaPickleProperties::default(args.block); - sea_pickle_prop.waterlogged = args.replacing.water_source(); - sea_pickle_prop.to_state_id(args.block) - }) + let mut sea_pickle_prop = SeaPickleProperties::default(args.block); + sea_pickle_prop.waterlogged = args.replacing.water_source(); + sea_pickle_prop.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -132,19 +123,16 @@ impl BlockBehaviour for SeaPickleBlock { && SeaPickleProperties::from_state_id(args.state_id, args.block).pickles < 4 } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/seagrass.rs b/crates/pumpkin/src/block/blocks/plant/seagrass.rs index 17709c6ea..5c4261618 100644 --- a/crates/pumpkin/src/block/blocks/plant/seagrass.rs +++ b/crates/pumpkin/src/block/blocks/plant/seagrass.rs @@ -7,8 +7,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - blocks::plant::PlantBlockBase, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; #[pumpkin_block("minecraft:seagrass")] pub struct SeaGrassBlock; @@ -17,19 +16,16 @@ impl BlockBehaviour for SeaGrassBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } @@ -49,8 +45,7 @@ impl PlantBlockBase for SeaGrassBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/segmented.rs b/crates/pumpkin/src/block/blocks/plant/segmented.rs index 6b6a90fe3..e3ef7cfdd 100644 --- a/crates/pumpkin/src/block/blocks/plant/segmented.rs +++ b/crates/pumpkin/src/block/blocks/plant/segmented.rs @@ -1,7 +1,7 @@ use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing}; -use crate::block::{BlockBehaviour, BlockFuture, CanUpdateAtArgs}; +use crate::block::{BlockBehaviour, CanUpdateAtArgs}; use crate::block::{BlockIsReplacing, OnPlaceArgs}; use crate::entity::EntityBase; @@ -79,27 +79,25 @@ pub trait Segmented: BlockBehaviour { self.can_add_segment(¤t_props) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if let BlockIsReplacing::Itself(existing_state_id) = args.replacing { - let mut props = Self::Properties::from_state_id(existing_state_id, args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if let BlockIsReplacing::Itself(existing_state_id) = args.replacing { + let mut props = Self::Properties::from_state_id(existing_state_id, args.block); - if self.can_add_segment(&props) { - let current_amount = props.get_segment_amount(); - let next_amount = self.get_next_segment_amount(current_amount); - props.set_segment_amount(next_amount); - props.to_state_id(args.block) - } else { - existing_state_id - } - } else { - // Set first segment orientation based on player direction - let player_facing = args.player.get_entity().get_horizontal_facing(); - let mut props = Self::Properties::default(args.block); - props.set_segment_amount(1); - props.set_facing(self.get_facing_for_segment(player_facing, 1)); + if self.can_add_segment(&props) { + let current_amount = props.get_segment_amount(); + let next_amount = self.get_next_segment_amount(current_amount); + props.set_segment_amount(next_amount); props.to_state_id(args.block) + } else { + existing_state_id } - }) + } else { + // Set first segment orientation based on player direction + let player_facing = args.player.get_entity().get_horizontal_facing(); + let mut props = Self::Properties::default(args.block); + props.set_segment_amount(1); + props.set_facing(self.get_facing_for_segment(player_facing, 1)); + props.to_state_id(args.block) + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/short_plant.rs b/crates/pumpkin/src/block/blocks/plant/short_plant.rs index 9bbd8da57..920fa391e 100644 --- a/crates/pumpkin/src/block/blocks/plant/short_plant.rs +++ b/crates/pumpkin/src/block/blocks/plant/short_plant.rs @@ -5,7 +5,7 @@ use pumpkin_data::{Block, BlockId, BlockStateId}; use pumpkin_world::world::BlockFlags; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; @@ -25,8 +25,8 @@ impl BlockBehaviour for ShortPlantBlock { && args.world.get_block_state(&above).is_air() } - fn perform_bonemeal<'a>(&'a self, args: crate::block::BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn perform_bonemeal(&self, args: crate::block::BonemealArgs<'_>) { + { let grown = if args.block == &Block::FERN { &Block::LARGE_FERN } else { @@ -34,37 +34,31 @@ impl BlockBehaviour for ShortPlantBlock { }; let lower = grown.default_state.id; args.world - .set_block_state(args.position, lower, BlockFlags::NOTIFY_LISTENERS) - .await; + .set_block_state(args.position, lower, BlockFlags::NOTIFY_LISTENERS); let mut props = TallSeagrassLikeProperties::from_state_id(lower, grown); props.half = DoubleBlockHalf::Upper; - args.world - .set_block_state( - &args.position.up(), - props.to_state_id(grown), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - }) + args.world.set_block_state( + &args.position.up(), + props.to_state_id(grown), + BlockFlags::NOTIFY_LISTENERS, + ); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/plant/small_dripleaf.rs b/crates/pumpkin/src/block/blocks/plant/small_dripleaf.rs index 7daf9e15b..55a781369 100644 --- a/crates/pumpkin/src/block/blocks/plant/small_dripleaf.rs +++ b/crates/pumpkin/src/block/blocks/plant/small_dripleaf.rs @@ -1,7 +1,6 @@ use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, - PlacedArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{ @@ -20,40 +19,35 @@ impl BlockBehaviour for SmallDripleafBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - let mut small_dripleaf_props = SmallDripleafLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + let mut small_dripleaf_props = SmallDripleafLikeProperties::default(args.block); - small_dripleaf_props.facing = facing; - small_dripleaf_props.waterlogged = args.replacing.water_source(); - small_dripleaf_props.half = DoubleBlockHalf::Lower; + small_dripleaf_props.facing = facing; + small_dripleaf_props.waterlogged = args.replacing.water_source(); + small_dripleaf_props.half = DoubleBlockHalf::Lower; - small_dripleaf_props.to_state_id(args.block) - }) + small_dripleaf_props.to_state_id(args.block) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let lower_small_dripleaf_props = SmallDripleafLikeProperties::from_state_id(args.state_id, args.block); if lower_small_dripleaf_props.half != DoubleBlockHalf::Lower { @@ -68,14 +62,12 @@ impl BlockBehaviour for SmallDripleafBlock { upper_small_dripleaf_props.waterlogged = upper_block == &Block::WATER; upper_small_dripleaf_props.half = DoubleBlockHalf::Upper; - args.world - .set_block_state( - &args.position.up(), - upper_small_dripleaf_props.to_state_id(&Block::SMALL_DRIPLEAF), - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; - }) + args.world.set_block_state( + &args.position.up(), + upper_small_dripleaf_props.to_state_id(&Block::SMALL_DRIPLEAF), + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); + } } } fn is_small_dripleaf_waterlogged(state_id: BlockStateId) -> bool { @@ -107,8 +99,7 @@ impl PlantBlockBase for SmallDripleafBlock { } } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/spore_blossom.rs b/crates/pumpkin/src/block/blocks/plant/spore_blossom.rs index 1115035b6..cfdb4a9c7 100644 --- a/crates/pumpkin/src/block/blocks/plant/spore_blossom.rs +++ b/crates/pumpkin/src/block/blocks/plant/spore_blossom.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs}; use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; use pumpkin_data::BlockStateId; use pumpkin_data::tag::Taggable; @@ -14,19 +14,16 @@ impl BlockBehaviour for SporeBlossomBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } impl PlantBlockBase for SporeBlossomBlock { diff --git a/crates/pumpkin/src/block/blocks/plant/sugar_cane.rs b/crates/pumpkin/src/block/blocks/plant/sugar_cane.rs index f671c33fb..6f88ea0ff 100644 --- a/crates/pumpkin/src/block/blocks/plant/sugar_cane.rs +++ b/crates/pumpkin/src/block/blocks/plant/sugar_cane.rs @@ -12,69 +12,57 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, - OnScheduledTickArgs, RandomTickArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, + RandomTickArgs, }; #[pumpkin_block("minecraft:sugar_cane")] pub struct SugarCaneBlock; impl BlockBehaviour for SugarCaneBlock { - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.world.get_block_state(&args.position.up()).is_air() - && !(args.world.get_block(&args.position.down()) == &Block::SUGAR_CANE - && args.world.get_block(&args.position.down().down()) == &Block::SUGAR_CANE) - { - let state_id = args.world.get_block_state(args.position).id; - let age = CactusLikeProperties::from_state_id(state_id, args.block).age; - if age == 15 { - args.world - .set_block_state(&args.position.up(), state_id, BlockFlags::empty()) - .await; - let props = CactusLikeProperties { age: 0 }; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::empty(), - ) - .await; - } else { - let props = CactusLikeProperties { age: age + 1 }; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::empty(), - ) - .await; - } + fn random_tick(&self, args: RandomTickArgs<'_>) { + if args.world.get_block_state(&args.position.up()).is_air() + && !(args.world.get_block(&args.position.down()) == &Block::SUGAR_CANE + && args.world.get_block(&args.position.down().down()) == &Block::SUGAR_CANE) + { + let state_id = args.world.get_block_state(args.position).id; + let age = CactusLikeProperties::from_state_id(state_id, args.block).age; + if age == 15 { + args.world + .set_block_state(&args.position.up(), state_id, BlockFlags::empty()); + let props = CactusLikeProperties { age: 0 }; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::empty(), + ); + } else { + let props = CactusLikeProperties { age: age + 1 }; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::empty(), + ); } - }) + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/plant/tall_plant.rs b/crates/pumpkin/src/block/blocks/plant/tall_plant.rs index 309706712..6b7b613a5 100644 --- a/crates/pumpkin/src/block/blocks/plant/tall_plant.rs +++ b/crates/pumpkin/src/block/blocks/plant/tall_plant.rs @@ -8,7 +8,6 @@ use pumpkin_data::block_properties::{ }; use pumpkin_world::world::BlockFlags; -use crate::block::BlockFuture; use crate::block::{ BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, @@ -52,53 +51,48 @@ impl BlockBehaviour for TallPlantBlock { && upper_state.is_air() } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let tall_plant_props = - TallSeagrassLikeProperties::from_state_id(args.state_id, args.block); - let (support_block_pos, other_block_pos) = match tall_plant_props.half { - DoubleBlockHalf::Upper => (args.position.down_height(2), args.position.down()), - DoubleBlockHalf::Lower => (args.position.down(), args.position.up()), - }; - if !::can_place_at(self, args.world, &support_block_pos.up()) { - return Block::AIR.default_state.id; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let tall_plant_props = TallSeagrassLikeProperties::from_state_id(args.state_id, args.block); + let (support_block_pos, other_block_pos) = match tall_plant_props.half { + DoubleBlockHalf::Upper => (args.position.down_height(2), args.position.down()), + DoubleBlockHalf::Lower => (args.position.down(), args.position.up()), + }; + if !::can_place_at(self, args.world, &support_block_pos.up()) { + return Block::AIR.default_state.id; + } - let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_block_pos); - if Self::ids().contains(&other_block.id) { - let other_props = - TallSeagrassLikeProperties::from_state_id(other_state_id, other_block); - let opposite_half = match tall_plant_props.half { - DoubleBlockHalf::Upper => DoubleBlockHalf::Lower, - DoubleBlockHalf::Lower => DoubleBlockHalf::Upper, - }; - if other_props.half == opposite_half { - return args.state_id; - } + let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_block_pos); + if Self::ids().contains(&other_block.id) { + let other_props = + TallSeagrassLikeProperties::from_state_id(other_state_id, other_block); + let opposite_half = match tall_plant_props.half { + DoubleBlockHalf::Upper => DoubleBlockHalf::Lower, + DoubleBlockHalf::Lower => DoubleBlockHalf::Upper, + }; + if other_props.half == opposite_half { + return args.state_id; } - Block::AIR.default_state.id - }) + } + Block::AIR.default_state.id } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let mut tall_plant_props = TallSeagrassLikeProperties::from_state_id(args.state_id, args.block); tall_plant_props.half = DoubleBlockHalf::Upper; - args.world - .set_block_state( - &args.position.offset(BlockDirection::Up.to_offset()), - tall_plant_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; - }) + args.world.set_block_state( + &args.position.offset(BlockDirection::Up.to_offset()), + tall_plant_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { // When one half of a tall plant is broken, break the other half too let tall_plant_props = TallSeagrassLikeProperties::from_state_id(args.state.id, args.block); @@ -116,16 +110,14 @@ impl BlockBehaviour for TallPlantBlock { }; if other_props.half == opposite_half { // Break the other half, using SKIP_DROPS to prevent double drops - args.world - .break_block( - &other_block_pos, - None, - BlockFlags::SKIP_DROPS | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; + args.world.break_block( + &other_block_pos, + None, + BlockFlags::SKIP_DROPS | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); } } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/plant/tall_seagrass.rs b/crates/pumpkin/src/block/blocks/plant/tall_seagrass.rs index ef4d43b44..1abe8ff4e 100644 --- a/crates/pumpkin/src/block/blocks/plant/tall_seagrass.rs +++ b/crates/pumpkin/src/block/blocks/plant/tall_seagrass.rs @@ -4,7 +4,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockAccessor; use crate::block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::{PlantBlockBase, seagrass::supports_seagrass}, }; #[pumpkin_block("minecraft:tall_seagrass")] @@ -14,19 +14,16 @@ impl BlockBehaviour for TallSeaGrassBlock { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } @@ -60,8 +57,7 @@ impl PlantBlockBase for TallSeaGrassBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/twisting_vines.rs b/crates/pumpkin/src/block/blocks/plant/twisting_vines.rs index 4554172a2..698725746 100644 --- a/crates/pumpkin/src/block/blocks/plant/twisting_vines.rs +++ b/crates/pumpkin/src/block/blocks/plant/twisting_vines.rs @@ -1,7 +1,7 @@ use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, CanPlaceAtArgs, - GetStateForNeighborUpdateArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + PlacedArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::{Block, BlockId}; @@ -19,49 +19,42 @@ impl BlockBehaviour for TwistingVinesBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let support_pos = args.position.down(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::TWISTING_VINES { - args.world - .set_block_state( - &support_pos, - Block::TWISTING_VINES_PLANT.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::TWISTING_VINES_PLANT.default_state.id, + BlockFlags::empty(), + ); } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { let support_pos = args.position.down(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::TWISTING_VINES_PLANT { - args.world - .set_block_state( - &support_pos, - Block::TWISTING_VINES.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::TWISTING_VINES.default_state.id, + BlockFlags::empty(), + ); } - }) + } } } @@ -86,8 +79,7 @@ impl PlantBlockBase for TwistingVinesBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/weeping_vines.rs b/crates/pumpkin/src/block/blocks/plant/weeping_vines.rs index 23787043d..2fbf01232 100644 --- a/crates/pumpkin/src/block/blocks/plant/weeping_vines.rs +++ b/crates/pumpkin/src/block/blocks/plant/weeping_vines.rs @@ -1,7 +1,7 @@ use crate::block::blocks::plant::PlantBlockBase; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, CanPlaceAtArgs, - GetStateForNeighborUpdateArgs, PlacedArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + PlacedArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::{Block, BlockId}; @@ -19,49 +19,42 @@ impl BlockBehaviour for WeepingVinesBlock { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let support_pos = args.position.up(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::WEEPING_VINES { - args.world - .set_block_state( - &support_pos, - Block::WEEPING_VINES_PLANT.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::WEEPING_VINES_PLANT.default_state.id, + BlockFlags::empty(), + ); } - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { let support_pos = args.position.up(); let support_block = args.world.get_block(&support_pos); if support_block == &Block::WEEPING_VINES_PLANT { - args.world - .set_block_state( - &support_pos, - Block::WEEPING_VINES.default_state.id, - BlockFlags::empty(), - ) - .await; + args.world.set_block_state( + &support_pos, + Block::WEEPING_VINES.default_state.id, + BlockFlags::empty(), + ); } - }) + } } } @@ -85,8 +78,7 @@ impl PlantBlockBase for WeepingVinesBlock { } false } - #[allow(clippy::unused_async_trait_impl)] - async fn get_state_for_neighbor_update( + fn get_state_for_neighbor_update( &self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos, diff --git a/crates/pumpkin/src/block/blocks/plant/wither_rose.rs b/crates/pumpkin/src/block/blocks/plant/wither_rose.rs index 0da3bd850..52d694bd0 100644 --- a/crates/pumpkin/src/block/blocks/plant/wither_rose.rs +++ b/crates/pumpkin/src/block/blocks/plant/wither_rose.rs @@ -8,56 +8,48 @@ use pumpkin_data::{ use pumpkin_macros::pumpkin_block; use pumpkin_util::Difficulty; -use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, OnEntityCollisionArgs}; +use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs}; #[pumpkin_block("minecraft:wither_rose")] pub struct WitherRoseBlock; impl BlockBehaviour for WitherRoseBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living_entity) = args.entity.get_living_entity() { - if args.world.level_info.load().difficulty == Difficulty::Peaceful { - return; - } - let entity_type = args.entity.get_entity().entity_type; - if entity_type == &EntityType::ENDER_DRAGON - || entity_type == &EntityType::WITHER - || entity_type == &EntityType::WITHER_SKELETON - { - return; - } - let effect = pumpkin_data::potion::Effect { - effect_type: &StatusEffect::WITHER, - duration: 40, - amplifier: 0, - ambient: false, - show_particles: true, - show_icon: true, - blend: true, - }; - if let Some(player) = args.entity.get_player() { - player.send_effect(effect.clone()).await; - } - living_entity.add_effect(effect).await; + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + if let Some(living_entity) = args.entity.get_living_entity() { + if args.world.level_info.load().difficulty == Difficulty::Peaceful { + return; } - }) + let entity_type = args.entity.get_entity().entity_type; + if entity_type == &EntityType::ENDER_DRAGON + || entity_type == &EntityType::WITHER + || entity_type == &EntityType::WITHER_SKELETON + { + return; + } + let effect = pumpkin_data::potion::Effect { + effect_type: &StatusEffect::WITHER, + duration: 40, + amplifier: 0, + ambient: false, + show_particles: true, + show_icon: true, + blend: true, + }; + living_entity.add_effect(effect); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { ::can_place_at(self, args.block_accessor, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) } } diff --git a/crates/pumpkin/src/block/blocks/powder_snow.rs b/crates/pumpkin/src/block/blocks/powder_snow.rs index 7a44fa9d8..edf595d90 100644 --- a/crates/pumpkin/src/block/blocks/powder_snow.rs +++ b/crates/pumpkin/src/block/blocks/powder_snow.rs @@ -9,7 +9,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; -use crate::block::{BlockBehaviour, BlockFuture, OnEntityCollisionArgs, OnLandedUponArgs}; +use crate::block::{BlockBehaviour, OnEntityCollisionArgs, OnLandedUponArgs}; use crate::entity::EntityBase; #[pumpkin_block("minecraft:powder_snow")] @@ -19,7 +19,7 @@ const FALLING_COLLISION_SHAPE: BoundingBox = BoundingBox::new_array([0.0, 0.0, 0.0], [1.0, 0.9, 1.0]); const WALK_ON_EPSILON: f64 = 1.0e-7; -pub(crate) async fn can_entity_walk_on_powder_snow(entity: &dyn EntityBase) -> bool { +pub(crate) fn can_entity_walk_on_powder_snow(entity: &dyn EntityBase) -> bool { let base = entity.get_entity(); if base .entity_type @@ -32,11 +32,12 @@ pub(crate) async fn can_entity_walk_on_powder_snow(entity: &dyn EntityBase) -> b return false; }; - let equipment = living.entity_equipment.lock().await; - equipment - .equipment - .get(&EquipmentSlot::FEET) - .is_some_and(|boots| boots.item == &Item::LEATHER_BOOTS) + living.entity_equipment.try_lock().is_ok_and(|equipment| { + equipment + .equipment + .get(&EquipmentSlot::FEET) + .is_some_and(|boots| boots.item == &Item::LEATHER_BOOTS) + }) } fn is_entity_above_block(entity: &crate::entity::Entity, position: &BlockPos) -> bool { @@ -49,7 +50,7 @@ fn is_entity_descending(entity: &crate::entity::Entity) -> bool { entity.velocity.load().y < 0.0 } -pub(crate) async fn collision_shape_for_entity( +pub(crate) fn collision_shape_for_entity( entity: &dyn EntityBase, position: &BlockPos, ) -> Option { @@ -66,7 +67,7 @@ pub(crate) async fn collision_shape_for_entity( return Some(BoundingBox::full_block()); } - if can_entity_walk_on_powder_snow(entity).await + if can_entity_walk_on_powder_snow(entity) && is_entity_above_block(base, position) && !is_entity_descending(base) { @@ -76,55 +77,44 @@ pub(crate) async fn collision_shape_for_entity( None } -pub(crate) async fn inside_collision_shape_for_entity( +pub(crate) fn inside_collision_shape_for_entity( entity: &dyn EntityBase, position: &BlockPos, ) -> BoundingBox { - collision_shape_for_entity(entity, position) - .await - .unwrap_or_else(BoundingBox::full_block) + collision_shape_for_entity(entity, position).unwrap_or_else(BoundingBox::full_block) } impl BlockBehaviour for PowderSnowBlock { - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() - && args.fall_distance >= 4.0 - { - let sound = if args.fall_distance < 7.0 { - Sound::EntityGenericSmallFall - } else { - Sound::EntityGenericBigFall - }; + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() + && args.fall_distance >= 4.0 + { + let sound = if args.fall_distance < 7.0 { + Sound::EntityGenericSmallFall + } else { + Sound::EntityGenericBigFall + }; - living.entity.play_sound(sound); - } - }) + living.entity.play_sound(sound); + } } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = args.entity.get_entity(); - entity - .slow_movement(args.state, Vector3::new(0.9, 1.5, 0.9)) - .await; + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let entity = args.entity.get_entity(); + entity.slow_movement(args.state, Vector3::new(0.9, 1.5, 0.9)); - if entity.fire_ticks.load(std::sync::atomic::Ordering::Relaxed) > 0 { - let can_destroy = args.entity.get_player().is_some() - || args.world.level_info.load().game_rules.mob_griefing; - if can_destroy { - let _ = args - .world - .break_block( - args.position, - None, - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_DROPS, - ) - .await; - } + if entity.fire_ticks.load(std::sync::atomic::Ordering::Relaxed) > 0 { + let can_destroy = args.entity.get_player().is_some() + || args.world.level_info.load().game_rules.mob_griefing; + if can_destroy { + let _ = args.world.break_block( + args.position, + None, + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_DROPS, + ); } + } - entity.extinguish(); - }) + entity.extinguish(); } } diff --git a/crates/pumpkin/src/block/blocks/pumpkin.rs b/crates/pumpkin/src/block/blocks/pumpkin.rs index b4f86b94a..e7ce1ddef 100644 --- a/crates/pumpkin/src/block/blocks/pumpkin.rs +++ b/crates/pumpkin/src/block/blocks/pumpkin.rs @@ -1,5 +1,5 @@ +use crate::block::UseWithItemArgs; use crate::block::registry::BlockActionResult; -use crate::block::{BlockFuture, UseWithItemArgs}; use crate::entity::Entity; use crate::entity::item::ItemEntity; use pumpkin_data::Block; @@ -15,42 +15,37 @@ use std::sync::Arc; pub struct PumpkinBlock; impl crate::block::BlockBehaviour for PumpkinBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if args.item_stack.item != &Item::SHEARS { - return BlockActionResult::Pass; - } - let mut props = WallTorchLikeProperties::default(&Block::CARVED_PUMPKIN); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - args.world - .set_block_state( - args.position, - props.to_state_id(&Block::CARVED_PUMPKIN), - BlockFlags::NOTIFY_ALL, - ) - .await; - let entity = Entity::new( - args.world.clone(), - args.position.to_f64(), - &EntityType::ITEM, - ); - let item_entity = Arc::new(ItemEntity::new( - entity, - ItemStack::new(4, &Item::PUMPKIN_SEEDS), - )); - args.world.spawn_entity(item_entity).await; - args.player - .damage_item_in_slot(args.equipment_slot, 1) - .await; - BlockActionResult::Consume - }) + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + if args.item_stack.item != &Item::SHEARS { + return BlockActionResult::Pass; + } + let mut props = WallTorchLikeProperties::default(&Block::CARVED_PUMPKIN); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + args.world.set_block_state( + args.position, + props.to_state_id(&Block::CARVED_PUMPKIN), + BlockFlags::NOTIFY_ALL, + ); + let entity = Entity::new( + args.world.clone(), + args.position.to_f64(), + &EntityType::ITEM, + ); + let item_entity = Arc::new(ItemEntity::new( + entity, + ItemStack::new(4, &Item::PUMPKIN_SEEDS), + )); + args.world.spawn_entity(item_entity); + let player = Arc::clone(args.player); + let slot = args.equipment_slot.clone(); + tokio::spawn(async move { + player.damage_item_in_slot(&slot, 1).await; + }); + BlockActionResult::Consume } } diff --git a/crates/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs b/crates/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs index 38410c8bd..3428bee6c 100644 --- a/crates/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs +++ b/crates/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs @@ -14,10 +14,7 @@ use pumpkin_world::{ }; use crate::{ - block::{ - BlockFuture, GetRedstonePowerArgs, OnNeighborUpdateArgs, OnStateReplacedArgs, - PlayerPlacedArgs, - }, + block::{GetRedstonePowerArgs, OnNeighborUpdateArgs, OnStateReplacedArgs, PlayerPlacedArgs}, entity::player::Player, world::World, }; @@ -49,194 +46,145 @@ pub trait RedstoneGateBlock(&'a self, args: GetRedstonePowerArgs<'a>) -> BlockFuture<'a, u8> + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 where Self: Send + Sync, { - Box::pin(async move { - let props = T::from_state_id(args.state.id, args.block); - if props.is_powered() && props.get_facing().to_block_direction() == args.direction { - self.get_output_level(args.world, *args.position).await - } else { - 0 - } - }) + let props = T::from_state_id(args.state.id, args.block); + if props.is_powered() && props.get_facing().to_block_direction() == args.direction { + self.get_output_level(args.world, *args.position) + } else { + 0 + } } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 where Self: Send + Sync, { - Box::pin(async move { self.get_weak_redstone_power(args).await }) + self.get_weak_redstone_power(args) } - fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8>; + fn get_output_level(&self, world: &World, pos: BlockPos) -> u8; - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) where Self: Send + Sync, { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - if RedstoneGateBlock::can_place_at(self, args.world.as_ref(), *args.position) { - self.update_powered(args.world, *args.position, state, args.block) - .await; - return; - } + let state = args.world.get_block_state(args.position); + if RedstoneGateBlock::can_place_at(self, args.world.as_ref(), *args.position) { + self.update_powered(args.world, *args.position, state, args.block); + return; + } + args.world.set_block_state( + args.position, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + for dir in BlockDirection::all() { args.world - .set_block_state( - args.position, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - for dir in BlockDirection::all() { - args.world - .update_neighbor(&args.position.offset(dir.to_offset()), args.source_block) - .await; - } - }) + .update_neighbor(&args.position.offset(dir.to_offset()), args.source_block); + } } - fn update_powered<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, ()>; + fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block); - fn has_power<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, bool> + fn has_power(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) -> bool where Self: Send + Sync, { - Box::pin(async move { self.get_power(world, pos, state, block).await > 0 }) + self.get_power(world, pos, state, block) > 0 } - fn get_power<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, u8> + fn get_power(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) -> u8 where Self: Send + Sync, { - Box::pin(async move { get_power::(world, pos, state.id, block).await }) + get_power::(world, pos, state.id, block) } - fn get_max_input_level_sides<'a>( - &'a self, - world: &'a World, + fn get_max_input_level_sides( + &self, + world: &World, pos: BlockPos, state_id: BlockStateId, - block: &'a Block, + block: &Block, only_gate: bool, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = T::from_state_id(state_id, block); - let facing = props.get_facing(); + ) -> u8 { + let props = T::from_state_id(state_id, block); + let facing = props.get_facing(); - let power_left = - get_power_on_side(world, &pos, facing.rotate_clockwise(), only_gate).await; - let power_right = - get_power_on_side(world, &pos, facing.rotate_counter_clockwise(), only_gate).await; + let power_left = get_power_on_side(world, &pos, facing.rotate_clockwise(), only_gate); + let power_right = + get_power_on_side(world, &pos, facing.rotate_counter_clockwise(), only_gate); - std::cmp::max(power_left, power_right) - }) + std::cmp::max(power_left, power_right) } - fn update_target<'a>( - &'a self, - world: &'a Arc, + fn update_target( + &self, + world: &Arc, pos: BlockPos, state_id: BlockStateId, - block: &'a Block, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - let props = T::from_state_id(state_id, block); - let facing = props.get_facing(); - let front_pos = pos.offset(facing.opposite().to_offset()); - world.update_neighbor(&front_pos, block).await; - world - .update_neighbors(&front_pos, Some(facing.to_block_direction())) - .await; - }) + block: &Block, + ) { + let props = T::from_state_id(state_id, block); + let facing = props.get_facing(); + let front_pos = pos.offset(facing.opposite().to_offset()); + world.update_neighbor(&front_pos, block); + world.update_neighbors(&front_pos, Some(facing.to_block_direction())); } - fn on_place<'a>( - &'a self, - player: &'a Player, - block: &'a Block, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async { - let mut props = T::default(block); - let dir = player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.set_facing(dir); + fn on_place(&self, player: &Player, block: &Block) -> BlockStateId { + let mut props = T::default(block); + let dir = player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.set_facing(dir); - props.to_state_id(block) - }) + props.to_state_id(block) } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> + fn player_placed(&self, args: PlayerPlacedArgs<'_>) where Self: Send + Sync, { - Box::pin(async move { - if RedstoneGateBlock::has_power( - self, - args.world, - *args.position, - BlockState::from_id(args.state_id), - args.block, - ) - .await - { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - }) + if RedstoneGateBlock::has_power( + self, + args.world, + *args.position, + BlockState::from_id(args.state_id), + args.block, + ) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) where Self: Send + Sync, { - Box::pin(async move { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - args.old_state_id, - args.block, - ) - .await; - }) + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + args.old_state_id, + args.block, + ); } - fn is_target_not_aligned<'a>( - &'a self, - world: &'a dyn BlockAccessor, + fn is_target_not_aligned( + &self, + world: &dyn BlockAccessor, pos: BlockPos, - state: &'a BlockState, - block: &'a Block, + state: &BlockState, + block: &Block, ) -> bool { let props = T::from_state_id(state.id, block); let facing = props.get_facing().opposite(); @@ -256,7 +204,7 @@ pub trait RedstoneGateBlock u8; } -pub async fn get_power( +pub fn get_power( world: &World, pos: BlockPos, state_id: BlockStateId, @@ -272,8 +220,7 @@ pub async fn get_power( world, &source_pos, facing.to_block_direction(), - ) - .await; + ); if source_level >= 15 { source_level } else { @@ -286,7 +233,7 @@ pub async fn get_power( } } -async fn get_power_on_side( +pub fn get_power_on_side( world: &World, pos: &BlockPos, side: HorizontalFacing, @@ -295,16 +242,13 @@ async fn get_power_on_side( let side_pos = pos.offset(side.to_block_direction().to_offset()); let (side_block, side_state) = world.get_block_and_state(&side_pos); if !only_gate || is_diode(side_block) { - world - .block_registry - .get_weak_redstone_power( - side_block, - world, - &side_pos, - side_state, - side.to_block_direction(), - ) - .await + world.block_registry.get_weak_redstone_power( + side_block, + world, + &side_pos, + side_state, + side.to_block_direction(), + ) } else { 0 } diff --git a/crates/pumpkin/src/block/blocks/redstone/bell.rs b/crates/pumpkin/src/block/blocks/redstone/bell.rs index cad0d3d83..2dfaec825 100644 --- a/crates/pumpkin/src/block/blocks/redstone/bell.rs +++ b/crates/pumpkin/src/block/blocks/redstone/bell.rs @@ -4,7 +4,7 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::entities::bell::BellBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, + BlockBehaviour, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, }; use crate::world::World; @@ -21,7 +21,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; -async fn ring_bell( +fn ring_bell( position: BlockPos, world: &Arc, hit_direction: Option, @@ -35,7 +35,7 @@ async fn ring_bell( cancelled: false, }; if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return false; @@ -131,105 +131,89 @@ impl BlockBehaviour for BellBlock { false } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let world: &World = args.world; - world.remove_block_entity(args.position); - }) + fn broken(&self, args: BrokenArgs<'_>) { + let world: &World = args.world; + world.remove_block_entity(args.position); } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world - .add_block_entity(Arc::new(BellBlockEntity::new(*args.position))); - }) + fn placed(&self, args: PlacedArgs<'_>) { + args.world + .add_block_entity(Arc::new(BellBlockEntity::new(*args.position))); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); - let props = BellLikeProperties::from_state_id(state.id, args.block); + let props = BellLikeProperties::from_state_id(state.id, args.block); - if !is_point_on_bell(args.hit, props.attachment, props.facing) { - return BlockActionResult::Pass; // Pass if Crosshair wasn't correctly positioned - } - if !ring_bell( - *args.position, - args.world, - args.hit.face.to_horizontal_facing(), - Some(args.player.clone()), - ) - .await - { - return BlockActionResult::Pass; - } + if !is_point_on_bell(args.hit, props.attachment, props.facing) { + return BlockActionResult::Pass; // Pass if Crosshair wasn't correctly positioned + } + if !ring_bell( + *args.position, + args.world, + args.hit.face.to_horizontal_facing(), + Some(args.player.clone()), + ) { + return BlockActionResult::Pass; + } - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::BellRing as i32, - 1, - ) - .await; + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::BellRing as i32, + 1, + ); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = BellLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = BellLikeProperties::default(args.block); - let block_face; - let facing; - (block_face, facing) = - WallMountedBlock::get_placement_face(self, args.player, args.direction); + let block_face; + let facing; + (block_face, facing) = + WallMountedBlock::get_placement_face(self, args.player, args.direction); - props.facing = match block_face { - AttachFace::Floor | AttachFace::Ceiling => facing, - AttachFace::Wall => facing.opposite(), - }; + props.facing = match block_face { + AttachFace::Floor | AttachFace::Ceiling => facing, + AttachFace::Wall => facing.opposite(), + }; - props.attachment = match block_face { - AttachFace::Wall => { - if is_single_wall(*args.position, props.facing.opposite(), args.world) { - BellAttachment::SingleWall - } else { - BellAttachment::DoubleWall - } - } - AttachFace::Floor => BellAttachment::Floor, - AttachFace::Ceiling => BellAttachment::Ceiling, - }; - - props.to_state_id(args.block) - }) - } - - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let world: &World = args.world; - - let is_receiving_power = block_receives_redstone_power(world, args.position).await; - let state = args.world.get_block_state(args.position); - - let mut props = BellLikeProperties::from_state_id(state.id, args.block); - - if props.powered != is_receiving_power { - props.powered = is_receiving_power; - - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - if is_receiving_power { - ring_bell(*args.position, args.world, None, None).await; + props.attachment = match block_face { + AttachFace::Wall => { + if is_single_wall(*args.position, props.facing.opposite(), args.world) { + BellAttachment::SingleWall + } else { + BellAttachment::DoubleWall } } - }) + AttachFace::Floor => BellAttachment::Floor, + AttachFace::Ceiling => BellAttachment::Ceiling, + }; + + props.to_state_id(args.block) + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let world: &World = args.world; + + let is_receiving_power = block_receives_redstone_power(world, args.position); + let state = args.world.get_block_state(args.position); + + let mut props = BellLikeProperties::from_state_id(state.id, args.block); + + if props.powered != is_receiving_power { + props.powered = is_receiving_power; + + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + + if is_receiving_power { + ring_bell(*args.position, args.world, None, None); + } + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/buttons.rs b/crates/pumpkin/src/block/blocks/redstone/buttons.rs index e004e688b..cf449130a 100644 --- a/crates/pumpkin/src/block/blocks/redstone/buttons.rs +++ b/crates/pumpkin/src/block/blocks/redstone/buttons.rs @@ -6,6 +6,7 @@ use pumpkin_data::BlockStateId; use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::AttachFace; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::tick::TickPriority; @@ -13,7 +14,6 @@ use pumpkin_world::world::BlockFlags; type ButtonLikeProperties = pumpkin_data::block_properties::LeverLikeProperties; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::EmitsRedstonePowerArgs; use crate::block::GetRedstonePowerArgs; @@ -27,26 +27,39 @@ use crate::block::registry::BlockActionResult; use crate::block::{BlockBehaviour, NormalUseArgs}; use crate::world::World; -async fn click_button(world: &Arc, block_pos: &BlockPos) { +fn get_sound(block: &Block, on: bool) -> Sound { + if block == &Block::STONE_BUTTON || block == &Block::POLISHED_BLACKSTONE_BUTTON { + if on { + Sound::BlockStoneButtonClickOn + } else { + Sound::BlockStoneButtonClickOff + } + } else if on { + Sound::BlockWoodenButtonClickOn + } else { + Sound::BlockWoodenButtonClickOff + } +} + +fn click_button(world: &Arc, block_pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(block_pos); let mut button_props = ButtonLikeProperties::from_state_id(state, block); if !button_props.powered { button_props.powered = true; - world - .set_block_state( - block_pos, - button_props.to_state_id(block), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + block_pos, + button_props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); let delay = if block == &Block::STONE_BUTTON { 20 } else { 30 }; world.schedule_block_tick(block, *block_pos, delay, TickPriority::Normal); - ButtonBlock::update_neighbors(world, block_pos, &button_props).await; + ButtonBlock::update_neighbors(world, block_pos, button_props); + world.play_block_sound(get_sound(block, true), SoundCategory::Blocks, *block_pos); } } @@ -54,82 +67,57 @@ async fn click_button(world: &Arc, block_pos: &BlockPos) { pub struct ButtonBlock; impl BlockBehaviour for ButtonBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - click_button(args.world, args.position).await; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + click_button(args.world, args.position); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = ButtonLikeProperties::from_state_id(state.id, args.block); - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - Self::update_neighbors(args.world, args.position, &props).await; - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let mut props = ButtonLikeProperties::from_state_id(state.id, args.block); + props.powered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); - if button_props.powered { 15 } else { 0 } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); + if button_props.powered { 15 } else { 0 } } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); - if button_props.powered && button_props.get_direction() == args.direction { - 15 - } else { - 0 + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); + if button_props.powered && button_props.get_direction() == args.direction { + 15 + } else { + 0 + } + } + + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if !args.moved { + let button_props = ButtonLikeProperties::from_state_id(args.old_state_id, args.block); + if button_props.powered { + Self::update_neighbors(args.world, args.position, button_props); } - }) + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !args.moved { - let button_props = - ButtonLikeProperties::from_state_id(args.old_state_id, args.block); - if button_props.powered { - Self::update_neighbors(args.world, args.position, &button_props).await; - } - } - }) - } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = + ButtonLikeProperties::from_state_id(args.block.default_state.id, args.block); + (props.face, props.facing) = + WallMountedBlock::get_placement_face(self, args.player, args.direction); - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - ButtonLikeProperties::from_state_id(args.block.default_state.id, args.block); - (props.face, props.facing) = - WallMountedBlock::get_placement_face(self, args.player, args.direction); - - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -141,11 +129,11 @@ impl BlockBehaviour for ButtonBlock { WallMountedBlock::can_place_at(self, args.block_accessor, args.position, direction) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + WallMountedBlock::get_state_for_neighbor_update(self, args) } } @@ -161,15 +149,9 @@ impl WallMountedBlock for ButtonBlock { } impl ButtonBlock { - async fn update_neighbors( - world: &Arc, - block_pos: &BlockPos, - props: &ButtonLikeProperties, - ) { + fn update_neighbors(world: &Arc, block_pos: &BlockPos, props: ButtonLikeProperties) { let direction = props.get_direction().opposite(); - world.update_neighbors(block_pos, None).await; - world - .update_neighbors(&block_pos.offset(direction.to_offset()), None) - .await; + world.update_neighbors(block_pos, None); + world.update_neighbors(&block_pos.offset(direction.to_offset()), None); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/comparator.rs b/crates/pumpkin/src/block/blocks/redstone/comparator.rs index 76e0de48b..b5cfc3fb1 100644 --- a/crates/pumpkin/src/block/blocks/redstone/comparator.rs +++ b/crates/pumpkin/src/block/blocks/redstone/comparator.rs @@ -13,7 +13,7 @@ use pumpkin_world::{tick::TickPriority, world::BlockFlags}; use crate::{ block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, EmitsRedstonePowerArgs, + BlockBehaviour, BrokenArgs, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, registry::BlockActionResult, @@ -28,111 +28,83 @@ use super::abstract_redstone_gate::{self, RedstoneGateBlock, RedstoneGateBlockPr pub struct ComparatorBlock; impl BlockBehaviour for ComparatorBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { RedstoneGateBlock::on_place(self, args.player, args.block).await }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + RedstoneGateBlock::on_place(self, args.player, args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let props = ComparatorLikeProperties::from_state_id(state.id, args.block); - self.on_use(props, args.world, *args.position, args.block) - .await; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + let props = ComparatorLikeProperties::from_state_id(state.id, args.block); + self.on_use(props, args.world, *args.position, args.block); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let comparator = ComparatorBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(comparator)); + fn placed(&self, args: PlacedArgs<'_>) { + let comparator = ComparatorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(comparator)); - RedstoneGateBlock::update_target( + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + args.state_id, + args.block, + ); + } + + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + RedstoneGateBlock::player_placed(self, args); + } + + fn broken(&self, args: BrokenArgs<'_>) { + args.world.remove_block_entity(args.position); + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down + && !RedstoneGateBlock::can_place_above( self, args.world, - *args.position, - args.state_id, - args.block, + *args.neighbor_position, + BlockState::from_id(args.neighbor_state_id), ) - .await; - }) + { + return Block::AIR.default_state.id; + } + args.state_id } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::player_placed(self, args).await; - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + RedstoneGateBlock::get_weak_redstone_power(self, args) } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world.remove_block_entity(args.position); - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + RedstoneGateBlock::get_strong_redstone_power(self, args) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down - && !RedstoneGateBlock::can_place_above( - self, - args.world, - *args.neighbor_position, - BlockState::from_id(args.neighbor_state_id), - ) - { - return Block::AIR.default_state.id; - } - args.state_id - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + RedstoneGateBlock::on_neighbor_update(self, args); } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { RedstoneGateBlock::get_weak_redstone_power(self, args).await }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let (block, state) = args.world.get_block_and_state(args.position); + Self.update(args.world, *args.position, state, block); } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { RedstoneGateBlock::get_strong_redstone_power(self, args).await }) - } - - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::on_neighbor_update(self, args).await; - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - self.update(args.world, *args.position, state, args.block) - .await; - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::on_state_replaced(self, args).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + RedstoneGateBlock::on_state_replaced(self, args); } } @@ -151,140 +123,101 @@ impl RedstoneGateBlockProperties for ComparatorLikeProperties { } impl RedstoneGateBlock for ComparatorBlock { - fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8> { - Box::pin(async move { - if let Some(blockentity) = world.get_block_entity(&pos) - && let Some(comparator) = - blockentity.as_any().downcast_ref::() - { - return comparator.output_signal.load(Ordering::Relaxed); - } - 0 - }) + fn get_output_level(&self, world: &World, pos: BlockPos) -> u8 { + if let Some(blockentity) = world.get_block_entity(&pos) + && let Some(comparator) = blockentity.as_any().downcast_ref::() + { + return comparator.output_signal.load(Ordering::Relaxed); + } + 0 } - fn update_powered<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - if world.is_block_tick_scheduled(&pos, block) { - return; - } - let i = self.calculate_output_signal(world, pos, state, block).await; - let j = RedstoneGateBlock::get_output_level(self, world, pos).await; - let props = ComparatorLikeProperties::from_state_id(state.id, block); + fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) { + if world.is_block_tick_scheduled(&pos, block) { + return; + } + let i = self.calculate_output_signal(world, pos, state, block); + let j = RedstoneGateBlock::get_output_level(self, world, pos); + let props = ComparatorLikeProperties::from_state_id(state.id, block); - if i != j - || props.powered - != RedstoneGateBlock::has_power(self, world, pos, state, block).await - { - let priority = - if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { - TickPriority::High - } else { - TickPriority::Normal - }; - - world.schedule_block_tick( - block, - pos, - RedstoneGateBlock::get_update_delay_internal(self, state.id, block), - priority, - ); - } - }) - } - - fn has_power<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - let i = RedstoneGateBlock::get_power(self, world, pos, state, block).await; - if i == 0 { - return false; - } - let j = RedstoneGateBlock::get_max_input_level_sides( - self, world, pos, state.id, block, false, - ) - .await; - - if i > j { - true - } else { - let props = ComparatorLikeProperties::from_state_id(state.id, block); - i == j && props.mode == ModeComparator::Compare - } - }) - } - - fn get_power<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let redstone_level = abstract_redstone_gate::get_power::( - world, pos, state.id, block, - ) - .await; - - let props = ComparatorLikeProperties::from_state_id(state.id, block); - let facing = props.facing; - let source_pos = pos.offset(facing.to_offset()); - let (source_block, source_state) = world.get_block_and_state(&source_pos); - - if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block.id) - && let Some(level) = pumpkin_block - .get_comparator_output(GetComparatorOutputArgs { - world, - block: source_block, - state: source_state, - position: &source_pos, - }) - .await - { - return level; - } - - if redstone_level < 15 && source_state.is_solid_block() { - let deeper_source_pos = source_pos.offset(facing.to_offset()); - let (deeper_block, deeper_state) = world.get_block_and_state(&deeper_source_pos); - - let itemframe_level = - Self::get_attached_itemframe_level(world, facing, deeper_source_pos).await; - - // This is the correct way to handle the async call within the Option - let block_level = if let Some(pumpkin_block) = - world.block_registry.get_pumpkin_block(deeper_block.id) - { - pumpkin_block - .get_comparator_output(GetComparatorOutputArgs { - world, - block: deeper_block, - state: deeper_state, - position: &deeper_source_pos, - }) - .await + if i != j || props.powered != RedstoneGateBlock::has_power(self, world, pos, state, block) { + let priority = + if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { + TickPriority::High } else { - None + TickPriority::Normal }; - if let Some(level) = itemframe_level.max(block_level) { - return level; - } + world.schedule_block_tick( + block, + pos, + RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + priority, + ); + } + } + + fn has_power(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) -> bool { + let i = RedstoneGateBlock::get_power(self, world, pos, state, block); + if i == 0 { + return false; + } + let j = + RedstoneGateBlock::get_max_input_level_sides(self, world, pos, state.id, block, false); + + if i > j { + true + } else { + let props = ComparatorLikeProperties::from_state_id(state.id, block); + i == j && props.mode == ModeComparator::Compare + } + } + + fn get_power(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) -> u8 { + let redstone_level = abstract_redstone_gate::get_power::( + world, pos, state.id, block, + ); + + let props = ComparatorLikeProperties::from_state_id(state.id, block); + let facing = props.facing; + let source_pos = pos.offset(facing.to_offset()); + let (source_block, source_state) = world.get_block_and_state(&source_pos); + + if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block.id) + && let Some(level) = pumpkin_block.get_comparator_output(GetComparatorOutputArgs { + world, + block: source_block, + state: source_state, + position: &source_pos, + }) + { + return level; + } + + if redstone_level < 15 && source_state.is_solid_block() { + let deeper_source_pos = source_pos.offset(facing.to_offset()); + let (deeper_block, deeper_state) = world.get_block_and_state(&deeper_source_pos); + + let itemframe_level = + Self::get_attached_itemframe_level(world, facing, deeper_source_pos); + + let block_level = world + .block_registry + .get_pumpkin_block(deeper_block.id) + .and_then(|pumpkin_block| { + pumpkin_block.get_comparator_output(GetComparatorOutputArgs { + world, + block: deeper_block, + state: deeper_state, + position: &deeper_source_pos, + }) + }); + + if let Some(level) = itemframe_level.max(block_level) { + return level; } - redstone_level - }) + } + redstone_level } fn get_update_delay_internal(&self, _state_id: BlockStateId, _block: &Block) -> u8 { @@ -293,7 +226,7 @@ impl RedstoneGateBlock for ComparatorBlock { } impl ComparatorBlock { - async fn on_use( + fn on_use( &self, mut props: ComparatorLikeProperties, world: &Arc, @@ -310,29 +243,24 @@ impl ComparatorBlock { }; let state_id = props.to_state_id(block); - world - .set_block_state(&block_pos, state_id, BlockFlags::empty()) - .await; + world.set_block_state(&block_pos, state_id, BlockFlags::empty()); - self.update(world, block_pos, BlockState::from_id(state_id), block) - .await; + self.update(world, block_pos, BlockState::from_id(state_id), block); } - async fn calculate_output_signal( + fn calculate_output_signal( &self, world: &World, pos: BlockPos, state: &BlockState, block: &Block, ) -> u8 { - let power = self.get_power(world, pos, state, block).await; + let power = self.get_power(world, pos, state, block); if power == 0 { return 0; } - let sub_power = self - .get_max_input_level_sides(world, pos, state.id, block, false) - .await; + let sub_power = self.get_max_input_level_sides(world, pos, state.id, block, false); if sub_power > power { return 0; @@ -346,7 +274,7 @@ impl ComparatorBlock { } } - async fn get_attached_itemframe_level( + fn get_attached_itemframe_level( world: &World, facing: HorizontalFacing, pos: BlockPos, @@ -364,13 +292,13 @@ impl ComparatorBlock { // Vanilla only reads a frame when exactly one hangs on this block. return None; } - level = Some(itemframe.get_analog_output().await); + level = Some(itemframe.get_analog_output()); } level } - async fn update(&self, world: &Arc, pos: BlockPos, state: &BlockState, block: &Block) { - let future_level = i32::from(self.calculate_output_signal(world, pos, state, block).await); + fn update(&self, world: &Arc, pos: BlockPos, state: &BlockState, block: &Block) { + let future_level = i32::from(self.calculate_output_signal(world, pos, state, block)); let mut now_level = 0; if let Some(blockentity) = world.get_block_entity(&pos) @@ -384,23 +312,18 @@ impl ComparatorBlock { let mut props = ComparatorLikeProperties::from_state_id(state.id, block); if now_level != future_level || props.mode == ModeComparator::Compare { - let future_power = self.has_power(world, pos, state, block).await; + let future_power = self.has_power(world, pos, state, block); let now_power = props.powered; if now_power && !future_power { props.powered = false; - world - .set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); } else if !now_power && future_power { props.powered = true; - world - .set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); } - RedstoneGateBlock::update_target(self, world, pos, props.to_state_id(block), block) - .await; + RedstoneGateBlock::update_target(self, world, pos, props.to_state_id(block), block); } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/copper_bulb.rs b/crates/pumpkin/src/block/blocks/redstone/copper_bulb.rs index cfba102fc..5519a7992 100644 --- a/crates/pumpkin/src/block/blocks/redstone/copper_bulb.rs +++ b/crates/pumpkin/src/block/blocks/redstone/copper_bulb.rs @@ -4,7 +4,7 @@ use crate::block::blocks::weathering_copper::{ get_first, get_next, get_previous, get_weather_state, }; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs, RandomTickArgs, + BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs, RandomTickArgs, }; use pumpkin_data::BlockId; use pumpkin_data::BlockStateId; @@ -57,28 +57,26 @@ impl BlockMetadata for CopperBulbBlock { } impl BlockBehaviour for CopperBulbBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CopperBulbLikeProperties::default(args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - if is_receiving_power { - props.lit = true; - args.world.play_block_sound( - Sound::BlockCopperBulbTurnOn, - SoundCategory::Blocks, - *args.position, - ); - props.powered = true; - } - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = CopperBulbLikeProperties::default(args.block); + let is_receiving_power = block_receives_redstone_power(args.world, args.position); + if is_receiving_power { + props.lit = true; + args.world.play_block_sound( + Sound::BlockCopperBulbTurnOn, + SoundCategory::Blocks, + *args.position, + ); + props.powered = true; + } + props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state = args.world.get_block_state(args.position); let mut props = CopperBulbLikeProperties::from_state_id(state.id, args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + let is_receiving_power = block_receives_redstone_power(args.world, args.position); if props.powered != is_receiving_power { if !props.powered { props.lit = !props.lit; @@ -93,20 +91,16 @@ impl BlockBehaviour for CopperBulbBlock { ); } props.powered = is_receiving_power; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/crafter.rs b/crates/pumpkin/src/block/blocks/redstone/crafter.rs index 1306b977b..b486e0a4e 100644 --- a/crates/pumpkin/src/block/blocks/redstone/crafter.rs +++ b/crates/pumpkin/src/block/blocks/redstone/crafter.rs @@ -5,13 +5,12 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::entities::crafter::CrafterBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, - OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, PlacedArgs, }; use pumpkin_data::block_properties::{ BlockProperties, CrafterLikeProperties, HorizontalFacing, Orientation, }; -use pumpkin_data::sound::Sound; use pumpkin_data::translation; use pumpkin_data::world::WorldEvent; use pumpkin_data::{BlockDirection, BlockStateId}; @@ -55,160 +54,128 @@ impl ScreenHandlerFactory for CrafterScreenFactory { pub struct CrafterBlock; impl BlockBehaviour for CrafterBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .open_handled_screen(&CrafterScreenFactory(inventory), Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&CrafterScreenFactory(inventory), Some(pos)) .await; - } - BlockActionResult::Success - }) + }); + } + BlockActionResult::Success } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = CrafterLikeProperties::default(args.block); - let facing = args.direction; - let horizontal = args.player.living_entity.entity.get_horizontal_facing(); - props.orientation = match facing { - BlockDirection::Down => match horizontal { - HorizontalFacing::North => Orientation::DownNorth, - HorizontalFacing::South => Orientation::DownSouth, - HorizontalFacing::East => Orientation::DownEast, - HorizontalFacing::West => Orientation::DownWest, - }, - BlockDirection::Up => match horizontal { - HorizontalFacing::North => Orientation::UpNorth, - HorizontalFacing::South => Orientation::UpSouth, - HorizontalFacing::East => Orientation::UpEast, - HorizontalFacing::West => Orientation::UpWest, - }, - BlockDirection::North => Orientation::NorthUp, - BlockDirection::South => Orientation::SouthUp, - BlockDirection::East => Orientation::EastUp, - BlockDirection::West => Orientation::WestUp, - }; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = CrafterLikeProperties::default(args.block); + let facing = args.direction; + let horizontal = args.player.living_entity.entity.get_horizontal_facing(); + props.orientation = match facing { + BlockDirection::Down => match horizontal { + HorizontalFacing::North => Orientation::DownNorth, + HorizontalFacing::South => Orientation::DownSouth, + HorizontalFacing::East => Orientation::DownEast, + HorizontalFacing::West => Orientation::DownWest, + }, + BlockDirection::Up => match horizontal { + HorizontalFacing::North => Orientation::UpNorth, + HorizontalFacing::South => Orientation::UpSouth, + HorizontalFacing::East => Orientation::UpEast, + HorizontalFacing::West => Orientation::UpWest, + }, + BlockDirection::North => Orientation::NorthUp, + BlockDirection::South => Orientation::SouthUp, + BlockDirection::East => Orientation::EastUp, + BlockDirection::West => Orientation::WestUp, + }; + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let crafter_block_entity = CrafterBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(crafter_block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let crafter_block_entity = CrafterBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(crafter_block_entity)); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let powered = block_receives_redstone_power(args.world, args.position).await; - let mut props = CrafterLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let powered = block_receives_redstone_power(args.world, args.position); + let mut props = CrafterLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); - if powered && !props.triggered { - props.triggered = true; - args.world - .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else if !powered && props.triggered { - props.triggered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let mut props = CrafterLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); - - // Set to crafting state - props.crafting = true; + if powered && !props.triggered { + props.triggered = true; args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - - // Recipes/crafting logic not fully implemented yet - play fail effects - args.world.play_sound( - Sound::BlockCrafterFail, - pumpkin_data::sound::SoundCategory::Blocks, - &args.position.to_f64(), + .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, ); - - // Spawn fail smoke particles - args.world.sync_world_event( - WorldEvent::ParticlesShootSmoke, - *args.position, - match props.orientation { - Orientation::DownEast - | Orientation::DownNorth - | Orientation::DownSouth - | Orientation::DownWest => 0, - Orientation::UpEast - | Orientation::UpNorth - | Orientation::UpSouth - | Orientation::UpWest => 1, - Orientation::NorthUp => 2, - Orientation::SouthUp => 3, - Orientation::WestUp => 4, - Orientation::EastUp => 5, - }, + } else if !powered && props.triggered { + props.triggered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, ); - - // Set crafting state back to false - props.crafting = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - }) + } } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) { - let crafter = block_entity.as_any().downcast_ref::()?; + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let mut props = CrafterLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); - let mut occupied = 0u8; - for i in 0..9 { - let stack = crafter.get_stack(i).await; - if !stack.is_empty() { - occupied += 1; - } - } - Some(occupied) - } else { - None - } - }) + // Set to crafting state + props.crafting = true; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + + // Spawn fail smoke particles + args.world.sync_world_event( + WorldEvent::ParticlesShootSmoke, + *args.position, + match props.orientation { + Orientation::DownEast + | Orientation::DownNorth + | Orientation::DownSouth + | Orientation::DownWest => 0, + Orientation::UpEast + | Orientation::UpNorth + | Orientation::UpSouth + | Orientation::UpWest => 1, + Orientation::NorthUp => 2, + Orientation::SouthUp => 3, + Orientation::WestUp => 4, + Orientation::EastUp => 5, + }, + ); + + props.crafting = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } + + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) { + let crafter = block_entity.as_any().downcast_ref::()?; + + let items = crafter.items.blocking_read(); + let occupied = items.iter().filter(|s| !s.is_empty()).count() as u8; + Some(occupied) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs b/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs index 75af4706d..351465898 100644 --- a/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs +++ b/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs @@ -7,8 +7,8 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use crate::block::{ - BlockActionResult, BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, - GetRedstonePowerArgs, NormalUseArgs, PlacedArgs, + BlockActionResult, BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + NormalUseArgs, PlacedArgs, }; use crate::world::World; @@ -18,72 +18,52 @@ type DaylightDetectorProperties = pumpkin_data::block_properties::DaylightDetect pub struct DaylightDetectorBlock; impl BlockBehaviour for DaylightDetectorBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world - .add_block_entity(Arc::new(DaylightDetectorBlockEntity::new(*args.position))); - }) + fn placed(&self, args: PlacedArgs<'_>) { + args.world + .add_block_entity(Arc::new(DaylightDetectorBlockEntity::new(*args.position))); } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world.remove_block_entity(args.position); - }) + fn broken(&self, args: BrokenArgs<'_>) { + args.world.remove_block_entity(args.position); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async { - let player_abilities = args.player.abilities.lock(); - if !player_abilities.await.allow_modify_world { - return BlockActionResult::Pass; - } + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let player_abilities = args.player.abilities.blocking_lock(); + if !player_abilities.allow_modify_world { + return BlockActionResult::Pass; + } - let state = args.world.get_block_state(args.position); - let props = DaylightDetectorProperties::from_state_id(state.id, args.block); + let state = args.world.get_block_state(args.position); + let props = DaylightDetectorProperties::from_state_id(state.id, args.block); - self.update_inverted(props, args.world, args.position, args.block) - .await; + Self::update_inverted(props, args.world, args.position, args.block); - DaylightDetectorBlockEntity::update_power(args.world, args.position).await; + DaylightDetectorBlockEntity::update_power(args.world, args.position); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = DaylightDetectorProperties::from_state_id(args.state.id, args.block); - - props.power - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = DaylightDetectorProperties::from_state_id(args.state.id, args.block); + props.power } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } } impl DaylightDetectorBlock { - async fn update_inverted( - &self, - props: DaylightDetectorProperties, + fn update_inverted( + mut props: DaylightDetectorProperties, world: &Arc, block_pos: &BlockPos, block: &Block, ) { - let mut props = props; props.inverted = !props.inverted; let state = props.to_state_id(block); - world - .set_block_state(block_pos, state, BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(block_pos, state, BlockFlags::NOTIFY_LISTENERS); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs index 91b857483..d28bd7dbe 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -8,8 +8,8 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::blocks::tnt::TNTBlock; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, - OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, PlacedArgs, }; use crate::entity::decoration::armor_stand::ArmorStandEntity; use crate::entity::item::ItemEntity; @@ -98,16 +98,6 @@ struct DispenseContext<'a> { facing: Facing, } -impl<'a> DispenseContext<'a> { - const fn new(args: &OnScheduledTickArgs<'a>, facing: Facing) -> Self { - Self { - world: args.world, - position: args.position, - facing, - } - } -} - fn triangle(rng: &mut R, min: f64, max: f64) -> f64 { (rng.random::() - rng.random::()).mul_add(max, min) } @@ -135,106 +125,98 @@ const fn to_data3d(facing: Facing) -> i32 { } impl BlockBehaviour for DispenserBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .open_handled_screen(&DispenserScreenFactory(inventory), Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&DispenserScreenFactory(inventory), Some(pos)) .await; - } - BlockActionResult::Success - }) + }); + } + BlockActionResult::Success } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = DispenserLikeProperties::default(args.block); - props.facing = args.player.get_entity().get_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = DispenserLikeProperties::default(args.block); + props.facing = args.player.get_entity().get_facing().opposite(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let dispenser_block_entity = DispenserBlockEntity::new(*args.position); + fn placed(&self, args: PlacedArgs<'_>) { + let dispenser_block_entity = DispenserBlockEntity::new(*args.position); + args.world + .add_block_entity(Arc::new(dispenser_block_entity)); + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let powered = block_receives_redstone_power(args.world, args.position) + || block_receives_redstone_power(args.world, &args.position.up()); + + let mut props = DispenserLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); + + if powered && !props.triggered { args.world - .add_block_entity(Arc::new(dispenser_block_entity)); - }) - } - - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let powered = block_receives_redstone_power(args.world, args.position).await - || block_receives_redstone_power(args.world, &args.position.up()).await; - - let mut props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, + .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); + props.triggered = true; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, ); - - if powered && !props.triggered { - args.world - .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); - props.triggered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else if !powered && props.triggered { - props.triggered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } - }) + } else if !powered && props.triggered { + props.triggered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) { + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let world = args.world.clone(); + let position = *args.position; + tokio::spawn(async move { + let (block, state) = world.get_block_and_state(&position); + if let Some(block_entity) = world.get_block_entity(&position) { let Some(dispenser) = block_entity.as_any().downcast_ref::() else { return; }; if let Some((slot_index, mut item)) = dispenser.get_random_slot().await { - let props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); - let ctx = DispenseContext::new(&args, props.facing); + let props = DispenserLikeProperties::from_state_id(state.id, block); + let ctx = DispenseContext { + world: &world, + position: &position, + facing: props.facing, + }; Self::dispense(&ctx, dispenser, &mut item).await; dispenser.set_stack(slot_index, item).await; } else { - args.world - .sync_world_event(WorldEvent::SoundDispenserFail, *args.position, 0); + world.sync_world_event(WorldEvent::SoundDispenserFail, position, 0); } } - }) + }); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } @@ -279,34 +261,34 @@ impl DispenserBlock { if arrows.contains(&item.item.id) { // Arrows - Self::fire_arrow(ctx, item).await; + Self::fire_arrow(ctx, item); } else if boats.contains(&item.item.id) { // Boats - if !Self::dispense_boat(ctx, item).await { - Self::drop_item(ctx, item).await; + if !Self::dispense_boat(ctx, item) { + Self::drop_item(ctx, item); } } else if item.item.id == Item::ARMOR_STAND.id { // Armor stands - if !Self::dispense_armor_stand(ctx, item).await { - Self::drop_item(ctx, item).await; + if !Self::dispense_armor_stand(ctx, item) { + Self::drop_item(ctx, item); } } else if item.item.id == Item::TNT.id { // TNT - Self::dispense_tnt(ctx, item).await; + Self::dispense_tnt(ctx, item); } else if item.item.id == Item::SNOWBALL.id { - Self::dispense_snowball(ctx, item).await; + Self::dispense_snowball(ctx, item); } else if item.item.id == Item::EGG.id { - Self::dispense_egg(ctx, item).await; + Self::dispense_egg(ctx, item); } else if item.item.id == Item::SPLASH_POTION.id { - Self::dispense_splash_potion(ctx, item).await; + Self::dispense_splash_potion(ctx, item); } else if item.item.id == Item::LINGERING_POTION.id { - Self::dispense_lingering_potion(ctx, item).await; + Self::dispense_lingering_potion(ctx, item); } else if item.item.id == Item::FIRE_CHARGE.id { - Self::dispense_fire_charge(ctx, item).await; + Self::dispense_fire_charge(ctx, item); } else if item.item.id == Item::WIND_CHARGE.id { - Self::dispense_wind_charge(ctx, item).await; + Self::dispense_wind_charge(ctx, item); } else if item.item.id == Item::FIREWORK_ROCKET.id { - Self::dispense_firework_rocket(ctx, item).await; + Self::dispense_firework_rocket(ctx, item); } else if item.item.id == Item::BUCKET.id { // Empty buckets pick up the fluid in front of the dispenser Self::dispense_empty_bucket(ctx, dispenser, item).await; @@ -321,10 +303,10 @@ impl DispenserBlock { Self::dispense_honeycomb(ctx, item).await; } else if entity_from_egg(item.item.id).is_some() { // Spawn eggs - Self::dispense_spawn_egg(ctx, item).await; + Self::dispense_spawn_egg(ctx, item); } else { // Default / Drop - Self::drop_item(ctx, item).await; + Self::drop_item(ctx, item); } } @@ -344,12 +326,12 @@ impl DispenserBlock { thrown.set_velocity(facing.x, facing.y + 0.1, facing.z, power, uncertainty); } - async fn finish_projectile_launch( + fn finish_projectile_launch( ctx: &DispenseContext<'_>, projectile: Arc, launch_event: WorldEvent, ) { - ctx.world.spawn_entity(projectile).await; + ctx.world.spawn_entity(projectile); Self::play_dispense_effects(ctx, launch_event); } @@ -362,7 +344,7 @@ impl DispenserBlock { ); } - async fn fire_arrow(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn fire_arrow(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let projectile = item.split(1); let facing = to_normal(ctx.facing); @@ -386,8 +368,7 @@ impl DispenserBlock { ctx, Arc::new(arrow), WorldEvent::SoundDispenserProjectileLaunch, - ) - .await; + ); } fn target_position(ctx: &DispenseContext<'_>) -> BlockPos { @@ -409,7 +390,7 @@ impl DispenserBlock { && ctx.world.get_entities_at_box(&bounding_box).is_empty() } - async fn dispense_boat(ctx: &DispenseContext<'_>, item: &mut ItemStack) -> bool { + fn dispense_boat(ctx: &DispenseContext<'_>, item: &mut ItemStack) -> bool { let target = Self::target_position(ctx); let is_water = |id: u16| id == Fluid::WATER.id || id == Fluid::FLOWING_WATER.id; @@ -437,16 +418,14 @@ impl DispenserBlock { let facing = to_normal(ctx.facing); let entity = Entity::new(ctx.world.clone(), spawn_pos, entity_type); entity.set_rotation(facing.x.atan2(facing.z) as f32 * 57.295_776, 0.0); - ctx.world - .spawn_entity(Arc::new(BoatEntity::new(entity))) - .await; + ctx.world.spawn_entity(Arc::new(BoatEntity::new(entity))); ctx.world .sync_world_event(WorldEvent::SoundDispenserDispense, *ctx.position, 0); true } - async fn dispense_armor_stand(ctx: &DispenseContext<'_>, item: &mut ItemStack) -> bool { + fn dispense_armor_stand(ctx: &DispenseContext<'_>, item: &mut ItemStack) -> bool { let target = Self::target_position(ctx); let spawn_pos = target.to_f64(); let dimensions = EntityDimensions::new( @@ -469,15 +448,14 @@ impl DispenserBlock { &spawn_pos, ); ctx.world - .spawn_entity(Arc::new(ArmorStandEntity::new(entity))) - .await; + .spawn_entity(Arc::new(ArmorStandEntity::new(entity))); ctx.world .sync_world_event(WorldEvent::SoundDispenserDispense, *ctx.position, 0); true } - async fn dispense_tnt(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_tnt(ctx: &DispenseContext<'_>, item: &mut ItemStack) { const TNT_POWER: f32 = 4.0; const TNT_FUSE: u32 = 80; @@ -486,7 +464,7 @@ impl DispenserBlock { let entity = Entity::new(ctx.world.clone(), spawn_pos, &EntityType::TNT); let tnt = Arc::new(TNTEntity::new(entity, TNT_POWER, TNT_FUSE)); - ctx.world.spawn_entity(tnt).await; + ctx.world.spawn_entity(tnt); ctx.world .play_sound(Sound::EntityTntPrimed, SoundCategory::Blocks, &spawn_pos); @@ -494,7 +472,7 @@ impl DispenserBlock { .sync_world_event(WorldEvent::SoundDispenserDispense, *ctx.position, 0); } - async fn dispense_spawn_egg(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_spawn_egg(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let Some(entity_type) = entity_from_egg(item.item.id) else { return; }; @@ -507,13 +485,13 @@ impl DispenserBlock { mob.get_entity().set_rotation(yaw, 0.0); apply_entity_variant(item, mob.as_ref()); - ctx.world.spawn_entity(mob).await; + ctx.world.spawn_entity(mob); ctx.world .sync_world_event(WorldEvent::SoundDispenserDispense, *ctx.position, 0); } - async fn dispense_snowball(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_snowball(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let _ = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -531,11 +509,10 @@ impl DispenserBlock { ctx, Arc::new(snowball), WorldEvent::SoundDispenserProjectileLaunch, - ) - .await; + ); } - async fn dispense_egg(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_egg(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let projectile = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -543,7 +520,7 @@ impl DispenserBlock { &EntityType::EGG, ); let egg = EggEntity::new(entity); - egg.set_item_stack(projectile).await; + egg.set_item_stack(projectile); Self::launch_thrown( ctx, &egg.thrown, @@ -554,11 +531,10 @@ impl DispenserBlock { ctx, Arc::new(egg), WorldEvent::SoundDispenserProjectileLaunch, - ) - .await; + ); } - async fn dispense_splash_potion(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_splash_potion(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let projectile = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -566,7 +542,7 @@ impl DispenserBlock { &EntityType::SPLASH_POTION, ); let potion = SplashPotionEntity::new(entity); - potion.set_item_stack(projectile).await; + potion.set_item_stack(projectile); Self::launch_thrown( ctx, &potion.thrown, @@ -577,11 +553,10 @@ impl DispenserBlock { ctx, Arc::new(potion), WorldEvent::SoundDispenserProjectileLaunch, - ) - .await; + ); } - async fn dispense_lingering_potion(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_lingering_potion(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let projectile = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -589,7 +564,7 @@ impl DispenserBlock { &EntityType::LINGERING_POTION, ); let potion = LingeringPotionEntity::new(entity); - potion.set_item_stack(projectile).await; + potion.set_item_stack(projectile); Self::launch_thrown( ctx, &potion.thrown, @@ -600,11 +575,10 @@ impl DispenserBlock { ctx, Arc::new(potion), WorldEvent::SoundDispenserProjectileLaunch, - ) - .await; + ); } - async fn dispense_fire_charge(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_fire_charge(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let _ = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -622,11 +596,10 @@ impl DispenserBlock { Self::FIREBALL_PROJECTILE_POWER, Self::FIREBALL_PROJECTILE_UNCERTAINTY, ); - Self::finish_projectile_launch(ctx, Arc::new(fireball), WorldEvent::SoundBlazeFireball) - .await; + Self::finish_projectile_launch(ctx, Arc::new(fireball), WorldEvent::SoundBlazeFireball); } - async fn dispense_wind_charge(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_wind_charge(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let _ = item.split(1); let entity = Entity::new( ctx.world.clone(), @@ -650,11 +623,10 @@ impl DispenserBlock { ctx, Arc::new(WindChargeEntity::new_normal(thrown)), WorldEvent::SoundWindChargeShoot, - ) - .await; + ); } - async fn dispense_firework_rocket(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn dispense_firework_rocket(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let _ = item.split(1); let facing = to_normal(ctx.facing); // Vanilla spawns fireworks closer to the dispenser face and slightly above center. @@ -688,7 +660,7 @@ impl DispenserBlock { velocity.y.atan2(velocity.horizontal_length()) as f32 * 57.295_776, ); - Self::finish_projectile_launch(ctx, Arc::new(rocket), WorldEvent::SoundFireworkShoot).await; + Self::finish_projectile_launch(ctx, Arc::new(rocket), WorldEvent::SoundFireworkShoot); } async fn dispense_empty_bucket( @@ -698,7 +670,7 @@ impl DispenserBlock { ) { let front = Self::target_position(ctx); let Some(filled) = try_pickup_fluid_at(ctx.world, front).await else { - Self::drop_item(ctx, item).await; + Self::drop_item(ctx, item); return; }; @@ -707,7 +679,7 @@ impl DispenserBlock { if item.is_empty() { *item = filled_stack; } else if let Some(rest) = Self::add_to_first_free_slot(dispenser, filled_stack).await { - Self::eject_item(ctx, rest).await; + Self::eject_item(ctx, rest); } Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense); @@ -752,7 +724,7 @@ impl DispenserBlock { *item = ItemStack::new(1, &Item::BUCKET); Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense); } else { - Self::drop_item(ctx, item).await; + Self::drop_item(ctx, item); } } @@ -761,14 +733,12 @@ impl DispenserBlock { let front_block = ctx.world.get_block(&front); let ignited = if front_block == &Block::TNT { - TNTBlock::prime(ctx.world, &front).await; + TNTBlock::prime(ctx.world, &front); true } else { Ignition::ignite_block( |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { - world - .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); }, ctx.world, front, @@ -799,13 +769,13 @@ impl DispenserBlock { } } - async fn drop_item(ctx: &DispenseContext<'_>, item: &mut ItemStack) { + fn drop_item(ctx: &DispenseContext<'_>, item: &mut ItemStack) { let drop_item = item.split(1); - Self::eject_item(ctx, drop_item).await; + Self::eject_item(ctx, drop_item); Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense); } - async fn eject_item(ctx: &DispenseContext<'_>, stack: ItemStack) { + fn eject_item(ctx: &DispenseContext<'_>, stack: ItemStack) { let facing = to_normal(ctx.facing); let mut position = ctx.position.to_centered_f64().add(&(facing * 0.7)); @@ -824,6 +794,6 @@ impl DispenserBlock { ); let item_entity = Arc::new(ItemEntity::new_with_velocity(entity, stack, velocity, 40)); - ctx.world.spawn_entity(item_entity).await; + ctx.world.spawn_entity(item_entity); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/dropper.rs b/crates/pumpkin/src/block/blocks/redstone/dropper.rs index 031823fed..74dc8ce3b 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dropper.rs @@ -5,8 +5,8 @@ use tokio::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, - OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, PlacedArgs, }; use crate::entity::item::ItemEntity; use crate::entity::{Entity, EntityBase}; @@ -87,87 +87,77 @@ const fn to_data3d(facing: Facing) -> i32 { } impl BlockBehaviour for DropperBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .open_handled_screen(&DropperScreenFactory(inventory), Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&DropperScreenFactory(inventory), Some(pos)) .await; - } - BlockActionResult::Success - }) + }); + } + BlockActionResult::Success } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = DispenserLikeProperties::default(args.block); - props.facing = args.player.get_entity().get_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = DispenserLikeProperties::default(args.block); + props.facing = args.player.get_entity().get_facing().opposite(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let dropper_block_entity = DropperBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(dropper_block_entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let dropper_block_entity = DropperBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(dropper_block_entity)); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let powered = block_receives_redstone_power(args.world, args.position).await - || block_receives_redstone_power(args.world, &args.position.up()).await; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + let powered = block_receives_redstone_power(args.world, args.position) + || block_receives_redstone_power(args.world, &args.position.up()); - let mut props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, + let mut props = DispenserLikeProperties::from_state_id( + args.world.get_block_state(args.position).id, + args.block, + ); + + if powered && !props.triggered { + args.world + .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); + props.triggered = true; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, ); - - if powered && !props.triggered { - args.world - .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); - props.triggered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else if !powered && props.triggered { - props.triggered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } - }) + } else if !powered && props.triggered { + props.triggered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) { + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let world = args.world.clone(); + let position = *args.position; + tokio::spawn(async move { + let (block, state) = world.get_block_and_state(&position); + if let Some(block_entity) = world.get_block_entity(&position) { let Some(dropper) = block_entity.as_any().downcast_ref::() else { return; }; if let Some((slot_index, mut item)) = dropper.get_random_slot().await { - let props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).id, - args.block, - ); + let props = DispenserLikeProperties::from_state_id(state.id, block); - let target_pos = args - .position - .offset(props.facing.to_block_direction().to_offset()); + let target_pos = position.offset(props.facing.to_block_direction().to_offset()); - if let Some(entity) = args.world.get_block_entity(&target_pos) + if let Some(entity) = world.get_block_entity(&target_pos) && let Some(container) = entity.get_inventory() { let backup = item.clone(); @@ -188,14 +178,14 @@ impl BlockBehaviour for DropperBlock { let drop_item = item.split(1); dropper.set_stack(slot_index, item).await; let facing = to_normal(props.facing); - let mut position = args.position.to_centered_f64().add(&(facing * 0.7)); + let mut pos = position.to_centered_f64().add(&(facing * 0.7)); - position.y -= match props.facing { + pos.y -= match props.facing { Facing::Up | Facing::Down => 0.125, _ => 0.15625, }; - let entity = Entity::new(args.world.clone(), position, &EntityType::ITEM); + let entity = Entity::new(world.clone(), pos, &EntityType::ITEM); let rd = rng().random::().mul_add(0.1, 0.2); let velocity = Vector3::new( @@ -207,42 +197,31 @@ impl BlockBehaviour for DropperBlock { let item_entity = Arc::new(ItemEntity::new_with_velocity( entity, drop_item, velocity, 40, )); - args.world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); - args.world.sync_world_event( - WorldEvent::SoundDispenserDispense, - *args.position, - 0, - ); + world.sync_world_event(WorldEvent::SoundDispenserDispense, position, 0); - args.world.sync_world_event( + world.sync_world_event( WorldEvent::ParticlesShootSmoke, - *args.position, + position, to_data3d(props.facing), ); } else { - args.world.sync_world_event( - WorldEvent::SoundDispenserDispense, - *args.position, - 0, - ); + world.sync_world_event(WorldEvent::SoundDispenserDispense, position, 0); } } - }) + }); } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/lever.rs b/crates/pumpkin/src/block/blocks/redstone/lever.rs index a94c09ab1..4d3874ab2 100644 --- a/crates/pumpkin/src/block/blocks/redstone/lever.rs +++ b/crates/pumpkin/src/block/blocks/redstone/lever.rs @@ -1,13 +1,12 @@ use std::sync::Arc; use crate::block::{ - BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, - GetStateForNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, - blocks::abstract_wall_mounting::WallMountedBlock, + CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, + OnPlaceArgs, OnStateReplacedArgs, blocks::abstract_wall_mounting::WallMountedBlock, }; use pumpkin_data::{ Block, BlockDirection, BlockStateId, HorizontalFacingExt, - block_properties::{AttachFace, BlockProperties, LeverLikeProperties}, + block_properties::{AttachFace, BlockProperties, HorizontalFacing, LeverLikeProperties}, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; @@ -21,85 +20,84 @@ use crate::{ world::World, }; -async fn toggle_lever(world: &Arc, block_pos: &BlockPos) { +fn toggle_lever(world: &Arc, block_pos: &BlockPos) { let (block, state) = world.get_block_and_state_id(block_pos); let mut lever_props = LeverLikeProperties::from_state_id(state, block); lever_props.powered = !lever_props.powered; - world - .set_block_state( - block_pos, - lever_props.to_state_id(block), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + block_pos, + lever_props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); - LeverBlock::update_neighbors(world, block_pos, &lever_props).await; + LeverBlock::update_neighbors(world, block_pos, lever_props); } #[pumpkin_block("minecraft:lever")] pub struct LeverBlock; impl BlockBehaviour for LeverBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - toggle_lever(args.world, args.position).await; - - BlockActionResult::Success - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + toggle_lever(args.world, args.position); + BlockActionResult::Consume } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); - if lever_props.powered { 15 } else { 0 } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LeverLikeProperties::from_state_id(args.state.id, args.block); + if props.powered { 15 } else { 0 } } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); - if lever_props.powered && lever_props.get_direction() == args.direction { - 15 - } else { - 0 - } - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LeverLikeProperties::from_state_id(args.state.id, args.block); + if props.powered && props.get_direction() == args.direction { + 15 + } else { + 0 + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !args.moved { - let lever_props = LeverLikeProperties::from_state_id(args.old_state_id, args.block); - if lever_props.powered { - Self::update_neighbors(args.world, args.position, &lever_props).await; + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + let block_pos = args.position; + let block = args.block; + + let lever_props = LeverLikeProperties::from_state_id(args.old_state_id, block); + + if lever_props.powered { + Self::update_neighbors(args.world, block_pos, lever_props); + } + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = LeverLikeProperties::default(&pumpkin_data::Block::LEVER); + + props.face = match args.direction { + BlockDirection::Down => AttachFace::Ceiling, + BlockDirection::Up => AttachFace::Floor, + _ => AttachFace::Wall, + }; + + props.facing = match props.face { + AttachFace::Floor | AttachFace::Ceiling => { + let player_direction = args.player.living_entity.entity.get_horizontal_facing(); + match player_direction { + HorizontalFacing::North | HorizontalFacing::South => HorizontalFacing::South, + HorizontalFacing::West | HorizontalFacing::East => HorizontalFacing::East, } } - }) - } + AttachFace::Wall => match args.direction { + BlockDirection::South => HorizontalFacing::South, + BlockDirection::West => HorizontalFacing::West, + BlockDirection::East => HorizontalFacing::East, + _ => HorizontalFacing::North, + }, + }; - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = - LeverLikeProperties::from_state_id(args.block.default_state.id, args.block); - (props.face, props.facing) = - WallMountedBlock::get_placement_face(self, args.player, args.direction); - - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -111,11 +109,11 @@ impl BlockBehaviour for LeverBlock { WallMountedBlock::can_place_at(self, args.block_accessor, args.position, direction) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + WallMountedBlock::get_state_for_neighbor_update(self, args) } } @@ -131,16 +129,14 @@ impl WallMountedBlock for LeverBlock { } impl LeverBlock { - async fn update_neighbors( + fn update_neighbors( world: &Arc, block_pos: &BlockPos, - lever_props: &LeverLikeProperties, + lever_props: LeverLikeProperties, ) { let direction = lever_props.get_direction().opposite(); - world.update_neighbors(block_pos, None).await; - world - .update_neighbors(&block_pos.offset(direction.to_offset()), None) - .await; + world.update_neighbors(block_pos, None); + world.update_neighbors(&block_pos.offset(direction.to_offset()), None); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/lightning_rod.rs b/crates/pumpkin/src/block/blocks/redstone/lightning_rod.rs index 2cc0cc856..b0337a4db 100644 --- a/crates/pumpkin/src/block/blocks/redstone/lightning_rod.rs +++ b/crates/pumpkin/src/block/blocks/redstone/lightning_rod.rs @@ -1,8 +1,7 @@ use std::sync::Arc; use crate::block::{ - BlockBehaviour, BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnPlaceArgs, - OnScheduledTickArgs, + BlockBehaviour, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, }; use crate::world::World; use pumpkin_data::block_properties::{BlockProperties, LightningRodLikeProperties}; @@ -16,90 +15,64 @@ use pumpkin_world::world::BlockFlags; pub struct LightningRodBlock; impl LightningRodBlock { - pub async fn trigger(world: &Arc, pos: &BlockPos) { + pub fn trigger(world: &Arc, pos: &BlockPos) { let (block, state_id) = world.get_block_and_state_id(pos); let mut props = LightningRodLikeProperties::from_state_id(state_id, block); if !props.powered { props.powered = true; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); - Self::update_neighbors(world, pos, &props).await; + Self::update_neighbors(world, pos, props); // In vanilla, it stays powered for 8 ticks (4 redstone ticks) before scheduled tick turns it off. world.schedule_block_tick(block, *pos, 8, TickPriority::Normal); } } - async fn update_neighbors( - world: &Arc, - pos: &BlockPos, - props: &LightningRodLikeProperties, - ) { - world.update_neighbors(pos, None).await; + fn update_neighbors(world: &Arc, pos: &BlockPos, props: LightningRodLikeProperties) { + world.update_neighbors(pos, None); // The block it is attached to is in the opposite of the facing direction let attached_pos = pos.offset(props.facing.opposite().to_block_direction().to_offset()); - world.update_neighbors(&attached_pos, None).await; + world.update_neighbors(&attached_pos, None); } } impl BlockBehaviour for LightningRodBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = LightningRodLikeProperties::default(args.block); - props.facing = args.direction.to_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = LightningRodLikeProperties::default(args.block); + props.facing = args.direction.to_facing().opposite(); + props.to_state_id(args.block) } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = LightningRodLikeProperties::from_state_id(args.state.id, args.block); - if props.powered { 15 } else { 0 } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LightningRodLikeProperties::from_state_id(args.state.id, args.block); + if props.powered { 15 } else { 0 } } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = LightningRodLikeProperties::from_state_id(args.state.id, args.block); - // It emits strong power only in its facing direction (the direction pointing outward) - if props.powered && props.facing.to_block_direction() == args.direction { - 15 - } else { - 0 - } - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = LightningRodLikeProperties::from_state_id(args.state.id, args.block); + // It emits strong power only in its facing direction (the direction pointing outward) + if props.powered && props.facing.to_block_direction() == args.direction { + 15 + } else { + 0 + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = LightningRodLikeProperties::from_state_id(state.id, args.block); - if props.powered { - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - Self::update_neighbors(args.world, args.position, &props).await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let mut props = LightningRodLikeProperties::from_state_id(state.id, args.block); + if props.powered { + props.powered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/mod.rs b/crates/pumpkin/src/block/blocks/redstone/mod.rs index a403176d8..927a55468 100644 --- a/crates/pumpkin/src/block/blocks/redstone/mod.rs +++ b/crates/pumpkin/src/block/blocks/redstone/mod.rs @@ -34,17 +34,17 @@ pub mod tripwire_hook; pub mod abstract_redstone_gate; pub mod dispenser; -pub async fn is_emitting_redstone_power( +pub fn is_emitting_redstone_power( block: &Block, state: &BlockState, world: &World, pos: &BlockPos, facing: BlockDirection, ) -> bool { - get_redstone_power(block, state, world, pos, facing).await > 0 + get_redstone_power(block, state, world, pos, facing) > 0 } -pub async fn get_redstone_power( +pub fn get_redstone_power( block: &Block, state: &BlockState, world: &World, @@ -53,14 +53,14 @@ pub async fn get_redstone_power( ) -> u8 { if state.is_solid_block() { return std::cmp::max( - get_max_strong_power(world, pos, true).await, - get_weak_power(block, state, world, pos, facing, true).await, + get_max_strong_power(world, pos, true), + get_weak_power(block, state, world, pos, facing, true), ); } - get_weak_power(block, state, world, pos, facing, true).await + get_weak_power(block, state, world, pos, facing, true) } -async fn get_redstone_power_no_dust( +pub fn get_redstone_power_no_dust( block: &Block, state: &BlockState, world: &World, @@ -69,52 +69,46 @@ async fn get_redstone_power_no_dust( ) -> u8 { if state.is_solid_block() { return std::cmp::max( - get_max_strong_power(world, &pos, false).await, - get_weak_power(block, state, world, &pos, facing, false).await, + get_max_strong_power(world, &pos, false), + get_weak_power(block, state, world, &pos, facing, false), ); } - get_weak_power(block, state, world, &pos, facing, false).await + get_weak_power(block, state, world, &pos, facing, false) } -async fn get_max_strong_power(world: &World, pos: &BlockPos, dust_power: bool) -> u8 { +pub fn get_max_strong_power(world: &World, pos: &BlockPos, dust_power: bool) -> u8 { let mut max_power = 0; for side in BlockDirection::all() { let (block, state) = world.get_block_and_state(&pos.offset(side.to_offset())); - max_power = max_power.max( - get_strong_power( - block, - state, - world, - &pos.offset(side.to_offset()), - side, - dust_power, - ) - .await, - ); + max_power = max_power.max(get_strong_power( + block, + state, + world, + &pos.offset(side.to_offset()), + side, + dust_power, + )); } max_power } -async fn get_max_weak_power(world: &World, pos: &BlockPos, dust_power: bool) -> u8 { +pub fn get_max_weak_power(world: &World, pos: &BlockPos, dust_power: bool) -> u8 { let mut max_power = 0; for side in BlockDirection::all() { let (block, state) = world.get_block_and_state(&pos.offset(side.to_offset())); - max_power = max_power.max( - get_weak_power( - block, - state, - world, - &pos.offset(side.to_offset()), - side, - dust_power, - ) - .await, - ); + max_power = max_power.max(get_weak_power( + block, + state, + world, + &pos.offset(side.to_offset()), + side, + dust_power, + )); } max_power } -async fn get_weak_power( +fn get_weak_power( block: &Block, state: &BlockState, world: &World, @@ -128,10 +122,9 @@ async fn get_weak_power( world .block_registry .get_weak_redstone_power(block, world, pos, state, side) - .await } -async fn get_strong_power( +fn get_strong_power( block: &Block, state: &BlockState, world: &World, @@ -145,14 +138,13 @@ async fn get_strong_power( world .block_registry .get_strong_redstone_power(block, world, pos, state, side) - .await } -pub async fn block_receives_redstone_power(world: &World, pos: &BlockPos) -> bool { +pub fn block_receives_redstone_power(world: &World, pos: &BlockPos) -> bool { for facing in BlockDirection::all() { let neighbor_pos = pos.offset(facing.to_offset()); let (block, state) = world.get_block_and_state(&neighbor_pos); - if is_emitting_redstone_power(block, state, world, &neighbor_pos, facing).await { + if is_emitting_redstone_power(block, state, world, &neighbor_pos, facing) { return true; } } @@ -164,12 +156,12 @@ pub fn is_diode(block: &Block) -> bool { block == &Block::REPEATER || block == &Block::COMPARATOR } -pub async fn diode_get_input_strength(world: &World, pos: &BlockPos, facing: BlockDirection) -> u8 { +pub fn diode_get_input_strength(world: &World, pos: &BlockPos, facing: BlockDirection) -> u8 { let input_pos = pos.offset(facing.to_offset()); let (input_block, input_state) = world.get_block_and_state(&input_pos); - let power: u8 = get_redstone_power(input_block, input_state, world, &input_pos, facing).await; + let power: u8 = get_redstone_power(input_block, input_state, world, &input_pos, facing); if power == 0 && input_state.is_solid_block() { - return get_max_weak_power(world, &input_pos, true).await; + return get_max_weak_power(world, &input_pos, true); } power } diff --git a/crates/pumpkin/src/block/blocks/redstone/observer.rs b/crates/pumpkin/src/block/blocks/redstone/observer.rs index 0f4379bfe..84abfde93 100644 --- a/crates/pumpkin/src/block/blocks/redstone/observer.rs +++ b/crates/pumpkin/src/block/blocks/redstone/observer.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use crate::block::{ - BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, - OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, + EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, OnStateReplacedArgs, }; use crate::entity::EntityBase; use pumpkin_data::{ @@ -19,123 +19,96 @@ use crate::{block::BlockBehaviour, world::World}; pub struct ObserverBlock; impl BlockBehaviour for ObserverBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = ObserverLikeProperties::default(args.block); - props.facing = args.player.get_entity().get_facing(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = ObserverLikeProperties::default(args.block); + props.facing = args.player.get_entity().get_facing(); + props.to_state_id(args.block) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = ObserverLikeProperties::from_state_id(state.id, args.block); + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let mut props = ObserverLikeProperties::from_state_id(state.id, args.block); - if props.powered { - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else { - props.powered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - args.world - .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal); - } - - Self::update_neighbors(args.world, args.block, args.position, &props).await; - }) + if props.powered { + props.powered = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } else { + props.powered = true; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = ObserverLikeProperties::from_state_id(args.state_id, args.block); + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = ObserverLikeProperties::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction() == args.direction - && !props.powered - && !args + if props.facing.to_block_direction() == args.direction + && !props.powered + && !args + .world + .is_block_tick_scheduled(args.position, &Block::OBSERVER) + { + Self::schedule_tick(args.world, args.position); + } + + args.state_id + } + + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true + } + + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); + if props.facing.to_block_direction() == args.direction && props.powered { + 15 + } else { + 0 + } + } + + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + self.get_weak_redstone_power(args) + } + + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if !args.moved { + let props = ObserverLikeProperties::from_state_id(args.old_state_id, args.block); + if props.powered + && args .world .is_block_tick_scheduled(args.position, &Block::OBSERVER) { - Self::schedule_tick(args.world, args.position); + Self::update_neighbors(args.world, args.block, args.position, props); } - - args.state_id - }) - } - - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) - } - - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); - if props.facing.to_block_direction() == args.direction && props.powered { - 15 - } else { - 0 - } - }) - } - - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { self.get_weak_redstone_power(args).await }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !args.moved { - let props = ObserverLikeProperties::from_state_id(args.old_state_id, args.block); - if props.powered - && args - .world - .is_block_tick_scheduled(args.position, &Block::OBSERVER) - { - Self::update_neighbors(args.world, args.block, args.position, &props).await; - } - } - }) + } } } impl ObserverBlock { - async fn update_neighbors( + fn update_neighbors( world: &Arc, block: &Block, block_pos: &BlockPos, - props: &ObserverLikeProperties, + props: ObserverLikeProperties, ) { let facing = props.facing.to_block_direction(); let opposite_facing_pos = block_pos.offset(facing.opposite().to_offset()); - world.update_neighbor(&opposite_facing_pos, block).await; - world - .update_neighbors(&opposite_facing_pos, Some(facing)) - .await; + world.update_neighbor(&opposite_facing_pos, block); + world.update_neighbors(&opposite_facing_pos, Some(facing)); } fn schedule_tick(world: &World, block_pos: &BlockPos) { diff --git a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs index 9023e1f6b..f86a269d3 100644 --- a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs +++ b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs @@ -5,7 +5,7 @@ use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; use pumpkin_world::{tick::TickPriority, world::BlockFlags}; use crate::{ - block::{OnEntityCollisionArgs, OnScheduledTickArgs, OnStateReplacedArgs}, + block::{OnEntityCollisionArgs, OnStateReplacedArgs}, world::World, }; @@ -26,33 +26,21 @@ fn detection_box_at(pos: &BlockPos) -> BoundingBox { } pub(crate) trait PressurePlate { - async fn on_entity_collision_pp(&self, args: OnEntityCollisionArgs<'_>) { + fn on_entity_collision_pp(&self, args: OnEntityCollisionArgs<'_>) { let output = self.get_redstone_output(args.block, args.state.id); if output == 0 { - self.update_plate_state(args.world, args.position, args.block, args.state, output) - .await; + self.update_plate_state(args.world, args.position, args.block, args.state, output); } } - async fn on_scheduled_tick_pp(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position); - let output = self.get_redstone_output(args.block, state.id); - if output > 0 { - self.update_plate_state(args.world, args.position, args.block, state, output) - .await; - } - } - - async fn on_state_replaced_pp(&self, args: OnStateReplacedArgs<'_>) { + fn on_state_replaced_pp(&self, args: OnStateReplacedArgs<'_>) { if !args.moved && self.get_redstone_output(args.block, args.old_state_id) > 0 { - args.world.update_neighbors(args.position, None).await; - args.world - .update_neighbors(&args.position.down(), None) - .await; + args.world.update_neighbors(args.position, None); + args.world.update_neighbors(&args.position.down(), None); } } - async fn update_plate_state( + fn update_plate_state( &self, world: &Arc, pos: &BlockPos, @@ -60,7 +48,7 @@ pub(crate) trait PressurePlate { state: &BlockState, output: u8, ) { - let calc_output = self.calculate_redstone_output(world, block, pos).await; + let calc_output = self.calculate_redstone_output(world, block, pos); let has_output = calc_output > 0; if calc_output != output { let next_output = if let Some(server) = world.server.upgrade() { @@ -71,7 +59,7 @@ pub(crate) trait PressurePlate { i32::from(output), i32::from(calc_output), ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -80,11 +68,9 @@ pub(crate) trait PressurePlate { calc_output }; let state = self.set_redstone_output(block, state, next_output); - world - .set_block_state(pos, state, BlockFlags::NOTIFY_LISTENERS) - .await; - world.update_neighbors(pos, None).await; - world.update_neighbors(&pos.down(), None).await; + world.set_block_state(pos, state, BlockFlags::NOTIFY_LISTENERS); + world.update_neighbors(pos, None); + world.update_neighbors(&pos.down(), None); } if has_output { world.schedule_block_tick(block, *pos, self.tick_rate(), TickPriority::Normal); @@ -100,7 +86,7 @@ pub(crate) trait PressurePlate { fn set_redstone_output(&self, block: &Block, state: &BlockState, output: u8) -> BlockStateId; - async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8; + fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8; fn tick_rate(&self) -> u8 { 20 diff --git a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs index 60df57a29..ab850837a 100644 --- a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs +++ b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs @@ -8,7 +8,7 @@ use pumpkin_world::world::BlockFlags; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnEntityCollisionArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, OnStateReplacedArgs, }, @@ -32,58 +32,43 @@ impl BlockMetadata for PressurePlateBlock { } impl BlockBehaviour for PressurePlateBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_entity_collision_pp(args).await; - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + self.on_entity_collision_pp(args); } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_scheduled_tick_pp(args).await; - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let output = self.get_redstone_output(args.block, state.id); + if output > 0 { + let (block, state) = args.world.get_block_and_state(args.position); + Self.update_plate_state(args.world, args.position, block, state, output); + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_state_replaced_pp(args).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + self.on_state_replaced_pp(args); } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { self.get_redstone_output(args.block, args.state.id) }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + self.get_redstone_output(args.block, args.state.id) } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - if args.direction == BlockDirection::Up { - return self.get_redstone_output(args.block, args.state.id); - } - 0 - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + if args.direction == BlockDirection::Up { + return self.get_redstone_output(args.block, args.state.id); + } + 0 } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !Self::can_pressure_plate_place_at(args.world, args.position) { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - } - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !Self::can_pressure_plate_place_at(args.world, args.position) { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -98,8 +83,7 @@ impl PressurePlate for PressurePlateBlock { if props.powered { 15 } else { 0 } } - #[allow(clippy::unused_async_trait_impl)] - async fn calculate_redstone_output(&self, world: &World, _block: &Block, pos: &BlockPos) -> u8 { + fn calculate_redstone_output(&self, world: &World, _block: &Block, pos: &BlockPos) -> u8 { let aabb = detection_box_at(pos); if !world.get_entities_at_box(&aabb).is_empty() || !world.get_players_at_box(&aabb).is_empty() diff --git a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs index e8ab2cf5f..643713945 100644 --- a/crates/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs +++ b/crates/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs @@ -6,7 +6,7 @@ use pumpkin_world::world::BlockFlags; use crate::{ block::{ - BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnEntityCollisionArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, OnStateReplacedArgs, }, @@ -33,58 +33,43 @@ impl BlockMetadata for WeightedPressurePlateBlock { } impl BlockBehaviour for WeightedPressurePlateBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_entity_collision_pp(args).await; - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + self.on_entity_collision_pp(args); } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_scheduled_tick_pp(args).await; - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let output = self.get_redstone_output(args.block, state.id); + if output > 0 { + let (block, state) = args.world.get_block_and_state(args.position); + Self.update_plate_state(args.world, args.position, block, state, output); + } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_state_replaced_pp(args).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + self.on_state_replaced_pp(args); } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { self.get_redstone_output(args.block, args.state.id) }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + self.get_redstone_output(args.block, args.state.id) } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - if args.direction == BlockDirection::Up { - return self.get_redstone_output(args.block, args.state.id); - } - 0 - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + if args.direction == BlockDirection::Up { + return self.get_redstone_output(args.block, args.state.id); + } + 0 } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !Self::can_pressure_plate_place_at(args.world, args.position) { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - } - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !Self::can_pressure_plate_place_at(args.world, args.position) { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -99,8 +84,7 @@ impl PressurePlate for WeightedPressurePlateBlock { props.power } - #[allow(clippy::unused_async_trait_impl)] - async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 { + fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 { // light = Gold // heavy = Iron let weight = if block == &Block::LIGHT_WEIGHTED_PRESSURE_PLATE { diff --git a/crates/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs b/crates/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs index 64ce8e1ee..0b946d16e 100644 --- a/crates/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs +++ b/crates/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs @@ -4,7 +4,6 @@ use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -39,177 +38,112 @@ use super::common::{ pub struct ActivatorRailBlock; impl BlockBehaviour for ActivatorRailBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.get_entity().get_horizontal_facing(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.get_entity().get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape(compute_placed_rail_shape( + args.world, + args.position, + player_facing, + )); - rail_props.to_state_id(args.block) - }) + rail_props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position); - self.update_powered_state(args.world, args.block, args.position) - .await; + self.update_powered_state(args.world, args.block, args.position); - let final_state_id = args.world.get_block_state_id(args.position); - let rail_props = RailProperties::new(final_state_id, args.block); + let final_state_id = args.world.get_block_state_id(args.position); + let rail_props = RailProperties::new(final_state_id, args.block); - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; + self.update_connected_rails(args.world, args.position, &rail_props, true, 0); + self.update_connected_rails(args.world, args.position, &rail_props, false, 0); - for direction in rail_props.directions() { - let neighbor_pos = args.position.offset(direction.to_offset()); + for direction in rail_props.directions() { + let neighbor_pos = args.position.offset(direction.to_offset()); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) - { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &neighbor_pos, - false, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - true, - 0, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - false, - 0, - ) - .await; - } - - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { - self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } - - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &down_pos, - false, - ) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &neighbor_pos, + false, + ); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0); } - }) + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { + self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0); + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { + self.update_powered_state_internal(args.world, neighbor_rail.0, &down_pos, false); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0); + } + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } - - self.update_powered_state(args.world, args.block, args.position) - .await; - - let state_id = args.world.get_block_state_id(args.position); - let rail_props = RailProperties::new(state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let rail_props = RailProperties::new(args.old_state_id, args.block); - - if rail_props.shape().is_ascending() { - args.world - .update_neighbor(&args.position.up(), args.block) - .await; - } - - args.world.update_neighbor(args.position, args.block).await; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !rail_placement_is_valid(args.world, args.block, args.position) { args.world - .update_neighbor(&args.position.down(), args.block) - .await; + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + return; + } - let directions = rail_props.directions(); - for direction in directions { - let neighbor_pos = args.position.offset(direction.to_offset()); + self.update_powered_state(args.world, args.block, args.position); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) - { - self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - true, - 0, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - false, - 0, - ) - .await; - } + let state_id = args.world.get_block_state_id(args.position); + let rail_props = RailProperties::new(state_id, args.block); - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { - self.update_powered_state(args.world, neighbor_rail.0, &up_pos) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } + self.update_connected_rails(args.world, args.position, &rail_props, true, 0); + self.update_connected_rails(args.world, args.position, &rail_props, false, 0); + } - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { - self.update_powered_state(args.world, neighbor_rail.0, &down_pos) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + let rail_props = RailProperties::new(args.old_state_id, args.block); + + if rail_props.shape().is_ascending() { + args.world.update_neighbor(&args.position.up(), args.block); + } + + args.world.update_neighbor(args.position, args.block); + args.world + .update_neighbor(&args.position.down(), args.block); + + let directions = rail_props.directions(); + for direction in directions { + let neighbor_pos = args.position.offset(direction.to_offset()); + + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0); } - }) + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &up_pos); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0); + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &down_pos); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0); + } + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -218,7 +152,7 @@ impl BlockBehaviour for ActivatorRailBlock { } impl ActivatorRailBlock { - async fn is_powered_by_other_rails( + fn is_powered_by_other_rails( &self, world: &World, pos: &BlockPos, @@ -294,29 +228,27 @@ impl ActivatorRailBlock { _ => return false, } - let next_pos = BlockPos::new(x, y, z); - - if self - .is_powered_by_other_rails_at(world, &next_pos, direction, distance, next_shape) - .await - { + if self.is_powered_at_position( + world, + &BlockPos::new(x, y, z), + direction, + distance, + next_shape, + ) { return true; } - if check_down { - let down_pos = BlockPos::new(x, y - 1, z); - if self - .is_powered_by_other_rails_at(world, &down_pos, direction, distance, next_shape) - .await - { - return true; - } - } - - false + check_down + && self.is_powered_at_position( + world, + &BlockPos::new(x, y - 1, z), + direction, + distance, + next_shape, + ) } - async fn is_powered_by_other_rails_at( + fn is_powered_at_position( &self, world: &World, pos: &BlockPos, @@ -361,20 +293,18 @@ impl ActivatorRailBlock { return false; } - if block_receives_redstone_power(world, pos).await { + if block_receives_redstone_power(world, pos) { return true; } - Box::pin(self.is_powered_by_other_rails(world, pos, &rail_props, direction, distance + 1)) - .await + self.is_powered_by_other_rails(world, pos, &rail_props, direction, distance + 1) } - async fn update_powered_state(&self, world: &Arc, block: &Block, pos: &BlockPos) { - self.update_powered_state_internal(world, block, pos, true) - .await; + fn update_powered_state(&self, world: &Arc, block: &Block, pos: &BlockPos) { + self.update_powered_state_internal(world, block, pos, true); } - async fn update_powered_state_internal( + fn update_powered_state_internal( &self, world: &Arc, block: &Block, @@ -385,40 +315,32 @@ impl ActivatorRailBlock { let mut rail_props = RailProperties::new(state_id, block); let current_powered = rail_props.is_powered(); - let direct_power = block_receives_redstone_power(world, pos).await; + let direct_power = block_receives_redstone_power(world, pos); - let rail_power = self - .is_powered_by_other_rails(world, pos, &rail_props, true, 0) - .await - || self - .is_powered_by_other_rails(world, pos, &rail_props, false, 0) - .await; + let rail_power = self.is_powered_by_other_rails(world, pos, &rail_props, true, 0) + || self.is_powered_by_other_rails(world, pos, &rail_props, false, 0); let should_be_powered = direct_power || rail_power; if current_powered != should_be_powered { rail_props.set_powered(should_be_powered); - world - .set_block_state(pos, rail_props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, rail_props.to_state_id(block), BlockFlags::NOTIFY_ALL); - world.update_neighbor(&pos.down(), block).await; + world.update_neighbor(&pos.down(), block); if rail_props.shape().is_ascending() { - world.update_neighbor(&pos.up(), block).await; + world.update_neighbor(&pos.up(), block); } if propagate { let updated_rail_props = RailProperties::new(rail_props.to_state_id(block), block); - Box::pin(self.update_connected_rails(world, pos, &updated_rail_props, true, 0)) - .await; - Box::pin(self.update_connected_rails(world, pos, &updated_rail_props, false, 0)) - .await; + self.update_connected_rails(world, pos, &updated_rail_props, true, 0); + self.update_connected_rails(world, pos, &updated_rail_props, false, 0); } } } - async fn update_connected_rails( + fn update_connected_rails( &self, world: &Arc, pos: &BlockPos, @@ -495,17 +417,15 @@ impl ActivatorRailBlock { } let next_pos = BlockPos::new(x, y, z); - self.update_rail_at_position(world, &next_pos, direction, distance, next_shape) - .await; + self.update_rail_at_position(world, &next_pos, direction, distance, next_shape); if check_down { let down_pos = BlockPos::new(x, y - 1, z); - self.update_rail_at_position(world, &down_pos, direction, distance, next_shape) - .await; + self.update_rail_at_position(world, &down_pos, direction, distance, next_shape); } } - async fn update_rail_at_position( + fn update_rail_at_position( &self, world: &Arc, pos: &BlockPos, @@ -539,11 +459,9 @@ impl ActivatorRailBlock { }; if shapes_compatible { - self.update_powered_state_internal(world, block, pos, false) - .await; + self.update_powered_state_internal(world, block, pos, false); - Box::pin(self.update_connected_rails(world, pos, &rail_props, direction, distance + 1)) - .await; + self.update_connected_rails(world, pos, &rail_props, direction, distance + 1); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/rails/common.rs b/crates/pumpkin/src/block/blocks/redstone/rails/common.rs index 3feac5909..b27240d0f 100644 --- a/crates/pumpkin/src/block/blocks/redstone/rails/common.rs +++ b/crates/pumpkin/src/block/blocks/redstone/rails/common.rs @@ -11,7 +11,7 @@ use crate::world::World; use super::{HorizontalFacingRailExt, Rail, RailElevation, RailProperties, StraightRailShapeExt}; -pub(super) async fn rail_placement_is_valid(world: &World, block: &Block, pos: &BlockPos) -> bool { +pub(super) fn rail_placement_is_valid(world: &World, block: &Block, pos: &BlockPos) -> bool { if !can_place_rail_at(world, pos) { return false; } @@ -40,7 +40,7 @@ pub(super) fn can_place_rail_at(world: &dyn BlockAccessor, pos: &BlockPos) -> bo state.is_side_solid(BlockDirection::Up) } -pub(super) async fn compute_placed_rail_shape( +pub(super) fn compute_placed_rail_shape( world: &World, block_pos: &BlockPos, player_facing: HorizontalFacing, @@ -107,7 +107,7 @@ pub(super) async fn compute_placed_rail_shape( player_facing.to_rail_shape_flat() } -pub(super) async fn update_flanking_rails_shape( +pub(super) fn update_flanking_rails_shape( world: &Arc, block: &Block, state_id: BlockStateId, @@ -126,13 +126,11 @@ pub(super) async fn update_flanking_rails_shape( if new_shape != flanking_rail.properties.shape() { flanking_rail.properties.set_shape(new_shape); - world - .set_block_state( - &flanking_rail.position, - flanking_rail.properties.to_state_id(flanking_rail.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &flanking_rail.position, + flanking_rail.properties.to_state_id(flanking_rail.block), + BlockFlags::NOTIFY_ALL, + ); } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs b/crates/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs index aceec6a01..2b5c076b7 100644 --- a/crates/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs +++ b/crates/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs @@ -3,7 +3,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -20,34 +19,29 @@ use super::common::{ pub struct DetectorRailBlock; impl BlockBehaviour for DetectorRailBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.get_entity().get_horizontal_facing(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.get_entity().get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape(compute_placed_rail_shape( + args.world, + args.position, + player_facing, + )); - rail_props.to_state_id(args.block) - }) + rail_props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; - }) + fn placed(&self, args: PlacedArgs<'_>) { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - } - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !rail_placement_is_valid(args.world, args.block, args.position) { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs b/crates/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs index de54435d7..df9c4d6da 100644 --- a/crates/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs +++ b/crates/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs @@ -4,7 +4,6 @@ use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -39,178 +38,113 @@ use super::common::{ pub struct PoweredRailBlock; impl BlockBehaviour for PoweredRailBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.get_entity().get_horizontal_facing(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.get_entity().get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape(compute_placed_rail_shape( + args.world, + args.position, + player_facing, + )); - rail_props.to_state_id(args.block) - }) + rail_props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position); - self.update_powered_state(args.world, args.block, args.position) - .await; + self.update_powered_state(args.world, args.block, args.position); - let final_state_id = args.world.get_block_state_id(args.position); - let rail_props = RailProperties::new(final_state_id, args.block); + let final_state_id = args.world.get_block_state_id(args.position); + let rail_props = RailProperties::new(final_state_id, args.block); - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; + self.update_connected_rails(args.world, args.position, &rail_props, true, 0); + self.update_connected_rails(args.world, args.position, &rail_props, false, 0); - for direction in rail_props.directions() { - let neighbor_pos = args.position.offset(direction.to_offset()); + for direction in rail_props.directions() { + let neighbor_pos = args.position.offset(direction.to_offset()); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) - { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &neighbor_pos, - false, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - true, - 0, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - false, - 0, - ) - .await; - } - - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { - self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } - - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &down_pos, - false, - ) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &neighbor_pos, + false, + ); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0); } - }) + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { + self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0); + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { + self.update_powered_state_internal(args.world, neighbor_rail.0, &down_pos, false); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0); + } + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } - - self.update_powered_state(args.world, args.block, args.position) - .await; - - let state_id = args.world.get_block_state_id(args.position); - let rail_props = RailProperties::new(state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.old_state_id; - let rail_props = RailProperties::new(state_id, args.block); - - if rail_props.shape().is_ascending() { - args.world - .update_neighbor(&args.position.up(), args.block) - .await; - } - - args.world.update_neighbor(args.position, args.block).await; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !rail_placement_is_valid(args.world, args.block, args.position) { args.world - .update_neighbor(&args.position.down(), args.block) - .await; + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + return; + } - let directions = rail_props.directions(); - for direction in directions { - let neighbor_pos = args.position.offset(direction.to_offset()); + self.update_powered_state(args.world, args.block, args.position); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) - { - self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - true, - 0, - ) - .await; - self.update_connected_rails( - args.world, - &neighbor_pos, - &neighbor_rail.1, - false, - 0, - ) - .await; - } + let state_id = args.world.get_block_state_id(args.position); + let rail_props = RailProperties::new(state_id, args.block); - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { - self.update_powered_state(args.world, neighbor_rail.0, &up_pos) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } + self.update_connected_rails(args.world, args.position, &rail_props, true, 0); + self.update_connected_rails(args.world, args.position, &rail_props, false, 0); + } - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { - self.update_powered_state(args.world, neighbor_rail.0, &down_pos) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + let state_id = args.old_state_id; + let rail_props = RailProperties::new(state_id, args.block); + + if rail_props.shape().is_ascending() { + args.world.update_neighbor(&args.position.up(), args.block); + } + + args.world.update_neighbor(args.position, args.block); + args.world + .update_neighbor(&args.position.down(), args.block); + + let directions = rail_props.directions(); + for direction in directions { + let neighbor_pos = args.position.offset(direction.to_offset()); + + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &neighbor_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0); } - }) + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &up_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &up_pos); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0); + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = Self::find_rail_at_position(args.world, &down_pos) { + self.update_powered_state(args.world, neighbor_rail.0, &down_pos); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0); + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0); + } + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -219,7 +153,7 @@ impl BlockBehaviour for PoweredRailBlock { } impl PoweredRailBlock { - async fn is_powered_by_other_rails( + fn is_powered_by_other_rails( &self, world: &World, pos: &BlockPos, @@ -295,29 +229,27 @@ impl PoweredRailBlock { _ => return false, } - let next_pos = BlockPos::new(x, y, z); - - if self - .is_powered_by_other_rails_at(world, &next_pos, direction, distance, next_shape) - .await - { + if self.is_powered_at_position( + world, + &BlockPos::new(x, y, z), + direction, + distance, + next_shape, + ) { return true; } - if check_down { - let down_pos = BlockPos::new(x, y - 1, z); - if self - .is_powered_by_other_rails_at(world, &down_pos, direction, distance, next_shape) - .await - { - return true; - } - } - - false + check_down + && self.is_powered_at_position( + world, + &BlockPos::new(x, y - 1, z), + direction, + distance, + next_shape, + ) } - async fn is_powered_by_other_rails_at( + fn is_powered_at_position( &self, world: &World, pos: &BlockPos, @@ -362,20 +294,18 @@ impl PoweredRailBlock { return false; } - if block_receives_redstone_power(world, pos).await { + if block_receives_redstone_power(world, pos) { return true; } - Box::pin(self.is_powered_by_other_rails(world, pos, &rail_props, direction, distance + 1)) - .await + self.is_powered_by_other_rails(world, pos, &rail_props, direction, distance + 1) } - async fn update_powered_state(&self, world: &Arc, block: &Block, pos: &BlockPos) { - self.update_powered_state_internal(world, block, pos, true) - .await; + fn update_powered_state(&self, world: &Arc, block: &Block, pos: &BlockPos) { + self.update_powered_state_internal(world, block, pos, true); } - async fn update_powered_state_internal( + fn update_powered_state_internal( &self, world: &Arc, block: &Block, @@ -386,40 +316,32 @@ impl PoweredRailBlock { let mut rail_props = RailProperties::new(state_id, block); let current_powered = rail_props.is_powered(); - let direct_power = block_receives_redstone_power(world, pos).await; + let direct_power = block_receives_redstone_power(world, pos); - let rail_power = self - .is_powered_by_other_rails(world, pos, &rail_props, true, 0) - .await - || self - .is_powered_by_other_rails(world, pos, &rail_props, false, 0) - .await; + let rail_power = self.is_powered_by_other_rails(world, pos, &rail_props, true, 0) + || self.is_powered_by_other_rails(world, pos, &rail_props, false, 0); let should_be_powered = direct_power || rail_power; if current_powered != should_be_powered { rail_props.set_powered(should_be_powered); - world - .set_block_state(pos, rail_props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, rail_props.to_state_id(block), BlockFlags::NOTIFY_ALL); - world.update_neighbor(&pos.down(), block).await; + world.update_neighbor(&pos.down(), block); if rail_props.shape().is_ascending() { - world.update_neighbor(&pos.up(), block).await; + world.update_neighbor(&pos.up(), block); } if propagate { let updated_rail_props = RailProperties::new(rail_props.to_state_id(block), block); - Box::pin(self.update_connected_rails(world, pos, &updated_rail_props, true, 0)) - .await; - Box::pin(self.update_connected_rails(world, pos, &updated_rail_props, false, 0)) - .await; + self.update_connected_rails(world, pos, &updated_rail_props, true, 0); + self.update_connected_rails(world, pos, &updated_rail_props, false, 0); } } } - async fn update_connected_rails( + fn update_connected_rails( &self, world: &Arc, pos: &BlockPos, @@ -496,17 +418,15 @@ impl PoweredRailBlock { } let next_pos = BlockPos::new(x, y, z); - self.update_rail_at_position(world, &next_pos, direction, distance, next_shape) - .await; + self.update_rail_at_position(world, &next_pos, direction, distance, next_shape); if check_down { let down_pos = BlockPos::new(x, y - 1, z); - self.update_rail_at_position(world, &down_pos, direction, distance, next_shape) - .await; + self.update_rail_at_position(world, &down_pos, direction, distance, next_shape); } } - async fn update_rail_at_position( + fn update_rail_at_position( &self, world: &Arc, pos: &BlockPos, @@ -540,11 +460,9 @@ impl PoweredRailBlock { }; if shapes_compatible { - self.update_powered_state_internal(world, block, pos, false) - .await; + self.update_powered_state_internal(world, block, pos, false); - Box::pin(self.update_connected_rails(world, pos, &rail_props, direction, distance + 1)) - .await; + self.update_connected_rails(world, pos, &rail_props, direction, distance + 1); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/rails/rail.rs b/crates/pumpkin/src/block/blocks/redstone/rails/rail.rs index fe2f3b61b..b4d680fba 100644 --- a/crates/pumpkin/src/block/blocks/redstone/rails/rail.rs +++ b/crates/pumpkin/src/block/blocks/redstone/rails/rail.rs @@ -5,7 +5,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -19,97 +18,88 @@ use super::{HorizontalFacingRailExt, Rail, RailElevation, RailProperties}; pub struct RailBlock; impl BlockBehaviour for RailBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let world = args.world; - let block_pos = args.position; - let mut rail_props = RailProperties::default(args.block); - rail_props.set_waterlogged(args.replacing.water_source()); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let world = args.world; + let block_pos = args.position; + let mut rail_props = RailProperties::default(args.block); + rail_props.set_waterlogged(args.replacing.water_source()); - let shape = if let Some(east_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::East) - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South).is_some() { - RailShape::SouthEast - } else if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) - .is_some() - { - RailShape::NorthEast - } else { - match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) { - Some(west_rail) if west_rail.elevation == RailElevation::Up => { - RailShape::AscendingWest - } - _ => { - if east_rail.elevation == RailElevation::Up { - RailShape::AscendingEast - } else { - RailShape::EastWest - } - } - } - } - } else if let Some(south_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South) - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).is_some() { - RailShape::SouthWest - } else if south_rail.elevation == RailElevation::Up { - RailShape::AscendingSouth - } else { - match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) { - Some(north_rail) if north_rail.elevation == RailElevation::Up => { - RailShape::AscendingNorth - } - _ => RailShape::NorthSouth, - } - } - } else if let Some(west_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).is_some() { - RailShape::NorthWest - } else if west_rail.elevation == RailElevation::Up { - RailShape::AscendingWest - } else { - RailShape::EastWest - } - } else if let Some(north_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) - { - if north_rail.elevation == RailElevation::Up { - RailShape::AscendingNorth - } else { - RailShape::NorthSouth - } + let shape = if let Some(east_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::East) + { + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South).is_some() { + RailShape::SouthEast + } else if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).is_some() { + RailShape::NorthEast } else { - args.player - .living_entity - .entity - .get_horizontal_facing() - .to_rail_shape_flat() - .as_shape() - }; - - rail_props.set_shape(shape); - rail_props.to_state_id(args.block) - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; - }) - } - - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; + match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) { + Some(west_rail) if west_rail.elevation == RailElevation::Up => { + RailShape::AscendingWest + } + _ => { + if east_rail.elevation == RailElevation::Up { + RailShape::AscendingEast + } else { + RailShape::EastWest + } + } + } } - }) + } else if let Some(south_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South) + { + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).is_some() { + RailShape::SouthWest + } else if south_rail.elevation == RailElevation::Up { + RailShape::AscendingSouth + } else { + match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) { + Some(north_rail) if north_rail.elevation == RailElevation::Up => { + RailShape::AscendingNorth + } + _ => RailShape::NorthSouth, + } + } + } else if let Some(west_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) + { + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).is_some() { + RailShape::NorthWest + } else if west_rail.elevation == RailElevation::Up { + RailShape::AscendingWest + } else { + RailShape::EastWest + } + } else if let Some(north_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) + { + if north_rail.elevation == RailElevation::Up { + RailShape::AscendingNorth + } else { + RailShape::NorthSouth + } + } else { + args.player + .living_entity + .entity + .get_horizontal_facing() + .to_rail_shape_flat() + .as_shape() + }; + + rail_props.set_shape(shape); + rail_props.to_state_id(args.block) + } + + fn placed(&self, args: PlacedArgs<'_>) { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position); + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if !rail_placement_is_valid(args.world, args.block, args.position) { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/redstone/redstone_block.rs b/crates/pumpkin/src/block/blocks/redstone/redstone_block.rs index d5d557385..8c93c9ffd 100644 --- a/crates/pumpkin/src/block/blocks/redstone/redstone_block.rs +++ b/crates/pumpkin/src/block/blocks/redstone/redstone_block.rs @@ -1,22 +1,16 @@ use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs}; +use crate::block::{BlockBehaviour, EmitsRedstonePowerArgs, GetRedstonePowerArgs}; #[pumpkin_block("minecraft:redstone_block")] pub struct RedstoneBlock; impl BlockBehaviour for RedstoneBlock { - fn get_weak_redstone_power<'a>( - &'a self, - _args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { 15 }) + fn get_weak_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { + 15 } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } } diff --git a/crates/pumpkin/src/block/blocks/redstone/redstone_lamp.rs b/crates/pumpkin/src/block/blocks/redstone/redstone_lamp.rs index 935f33854..e09a14f0b 100644 --- a/crates/pumpkin/src/block/blocks/redstone/redstone_lamp.rs +++ b/crates/pumpkin/src/block/blocks/redstone/redstone_lamp.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockFuture, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs}; +use crate::block::{OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::BlockProperties; use pumpkin_macros::pumpkin_block; @@ -14,20 +14,18 @@ type RedstoneLampProperties = pumpkin_data::block_properties::RedstoneOreLikePro pub struct RedstoneLamp; impl BlockBehaviour for RedstoneLamp { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = RedstoneLampProperties::default(args.block); - props.lit = block_receives_redstone_power(args.world, args.position).await; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = RedstoneLampProperties::default(args.block); + props.lit = block_receives_redstone_power(args.world, args.position); + props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state = args.world.get_block_state(args.position); let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); let is_lit = props.lit; - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + let is_receiving_power = block_receives_redstone_power(args.world, args.position); if is_lit != is_receiving_power { if is_lit { @@ -39,35 +37,31 @@ impl BlockBehaviour for RedstoneLamp { ); } else { props.lit = !props.lit; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } - } - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); - let is_lit = props.lit; - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - - if is_lit && !is_receiving_power { - props.lit = !props.lit; - args.world - .set_block_state( + args.world.set_block_state( args.position, props.to_state_id(args.block), BlockFlags::NOTIFY_LISTENERS, - ) - .await; + ); + } } - }) + } + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let props = RedstoneLampProperties::from_state_id(state.id, args.block); + let is_lit = props.lit; + let is_receiving_power = block_receives_redstone_power(args.world, args.position); + + if is_lit && !is_receiving_power { + let block = args.world.get_block(args.position); + let mut props = RedstoneLampProperties::from_state_id(state.id, block); + props.lit = !props.lit; + args.world.set_block_state( + args.position, + props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/redstone_ore.rs b/crates/pumpkin/src/block/blocks/redstone/redstone_ore.rs index d83324cb5..abe0bc3ba 100644 --- a/crates/pumpkin/src/block/blocks/redstone/redstone_ore.rs +++ b/crates/pumpkin/src/block/blocks/redstone/redstone_ore.rs @@ -1,6 +1,6 @@ use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, NormalUseArgs, OnEntityCollisionArgs, - OnEntityStepArgs, RandomTickArgs, registry::BlockActionResult, + BlockBehaviour, BlockMetadata, NormalUseArgs, OnEntityCollisionArgs, OnEntityStepArgs, + RandomTickArgs, registry::BlockActionResult, }; use crate::world::World; use pumpkin_data::block_properties::{BlockProperties, RedstoneOreLikeProperties}; @@ -18,55 +18,43 @@ impl BlockMetadata for RedstoneOreBlock { } impl RedstoneOreBlock { - async fn light_up(world: &Arc, pos: &BlockPos, block: &Block, state: &BlockState) { + fn light_up(world: &Arc, pos: &BlockPos, block: &Block, state: &BlockState) { let mut props = RedstoneOreLikeProperties::from_state_id(state.id, block); if !props.lit { props.lit = true; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); } } } impl BlockBehaviour for RedstoneOreBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - Self::light_up(args.world, args.position, args.block, state).await; - BlockActionResult::Success - }) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + Self::light_up(args.world, args.position, args.block, state); + BlockActionResult::Success } - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - Self::light_up(args.world, args.position, args.block, state).await; - }) + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let state = args.world.get_block_state(args.position); + Self::light_up(args.world, args.position, args.block, state); } - fn on_entity_step<'a>(&'a self, args: OnEntityStepArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - Self::light_up(args.world, args.position, args.block, state).await; - }) + fn on_entity_step(&self, args: OnEntityStepArgs<'_>) { + let state = args.world.get_block_state(args.position); + Self::light_up(args.world, args.position, args.block, state); } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = RedstoneOreLikeProperties::from_state_id(state.id, args.block); + fn random_tick(&self, args: RandomTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let mut props = RedstoneOreLikeProperties::from_state_id(state.id, args.block); - if props.lit { - props.lit = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } - }) + if props.lit { + props.lit = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/redstone_torch.rs b/crates/pumpkin/src/block/blocks/redstone/redstone_torch.rs index 6955a16d0..5d96b28b8 100644 --- a/crates/pumpkin/src/block/blocks/redstone/redstone_torch.rs +++ b/crates/pumpkin/src/block/blocks/redstone/redstone_torch.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use crate::block::BlockFuture; use crate::block::BlockIsReplacing; use crate::block::CanPlaceAtArgs; use crate::block::EmitsRedstonePowerArgs; @@ -42,58 +41,56 @@ impl BlockMetadata for RedstoneTorchBlock { } impl BlockBehaviour for RedstoneTorchBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let world = args.world; - let block = args.block; - let location = args.position; - - if args.direction == BlockDirection::Down { - let support_block = world.get_block_state(&location.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return block.default_state.id; - } - } - let mut directions = args.player.get_entity().get_entity_facing_order(); - - if args.replacing == BlockIsReplacing::None { - let face = args.direction.to_facing(); - let mut i = 0; - while i < directions.len() && directions[i] != face { - i += 1; - } - - if i > 0 { - directions.copy_within(0..i, 1); - directions[0] = face; - } - } else if directions[0] == Facing::Down { - let support_block = world.get_block_state(&location.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return block.default_state.id; - } - } - - for dir in directions { - if dir != Facing::Up - && dir != Facing::Down - && can_place_at(world, location, dir.to_block_direction()) - { - let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); - if let Some(facing) = dir.opposite().to_horizontal_facing() { - torch_props.facing = facing; - return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); - } - } - } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let world = args.world; + let block = args.block; + let location = args.position; + if args.direction == BlockDirection::Down { let support_block = world.get_block_state(&location.down()); if support_block.is_center_solid(BlockDirection::Up) { - block.default_state.id - } else { - BlockStateId::AIR + return block.default_state.id; } - }) + } + let mut directions = args.player.get_entity().get_entity_facing_order(); + + if args.replacing == BlockIsReplacing::None { + let face = args.direction.to_facing(); + let mut i = 0; + while i < directions.len() && directions[i] != face { + i += 1; + } + + if i > 0 { + directions.copy_within(0..i, 1); + directions[0] = face; + } + } else if directions[0] == Facing::Down { + let support_block = world.get_block_state(&location.down()); + if support_block.is_center_solid(BlockDirection::Up) { + return block.default_state.id; + } + } + + for dir in directions { + if dir != Facing::Up + && dir != Facing::Down + && can_place_at(world, location, dir.to_block_direction()) + { + let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); + if let Some(facing) = dir.opposite().to_horizontal_facing() { + torch_props.facing = facing; + return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); + } + } + } + + let support_block = world.get_block_state(&location.down()); + if support_block.is_center_solid(BlockDirection::Up) { + block.default_state.id + } else { + BlockStateId::AIR + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -109,34 +106,32 @@ impl BlockBehaviour for RedstoneTorchBlock { false } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.block == &Block::REDSTONE_WALL_TORCH { - let props = RWallTorchProps::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) - { - return BlockStateId::AIR; - } - } else if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if !support_block.is_center_solid(BlockDirection::Up) { - return BlockStateId::AIR; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.block == &Block::REDSTONE_WALL_TORCH { + let props = RWallTorchProps::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_at( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) + { + return BlockStateId::AIR; } - args.state_id - }) + } else if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if !support_block.is_center_solid(BlockDirection::Up) { + return BlockStateId::AIR; + } + } + args.state_id } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state = args.world.get_block_state(args.position); if args @@ -154,7 +149,6 @@ impl BlockBehaviour for RedstoneTorchBlock { args.position, props.facing.to_block_direction().opposite(), ) - .await { args.world.schedule_block_tick( args.block, @@ -165,8 +159,7 @@ impl BlockBehaviour for RedstoneTorchBlock { } } else if args.block == &Block::REDSTONE_TORCH { let props = RTorchProps::from_state_id(state.id, args.block); - if props.lit != should_be_lit(args.world, args.position, BlockDirection::Down).await - { + if props.lit != should_be_lit(args.world, args.position, BlockDirection::Down) { args.world.schedule_block_tick( args.block, *args.position, @@ -175,122 +168,97 @@ impl BlockBehaviour for RedstoneTorchBlock { ); } } - }) + } } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + if args.block == &Block::REDSTONE_WALL_TORCH { + let props = RWallTorchProps::from_state_id(args.state.id, args.block); + if props.lit && args.direction != props.facing.to_block_direction() { + return 15; + } + } else if args.block == &Block::REDSTONE_TORCH { + let props = RTorchProps::from_state_id(args.state.id, args.block); + if props.lit && args.direction != BlockDirection::Up { + return 15; + } + } + 0 + } + + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + if args.direction == BlockDirection::Down { if args.block == &Block::REDSTONE_WALL_TORCH { let props = RWallTorchProps::from_state_id(args.state.id, args.block); - if props.lit && args.direction != props.facing.to_block_direction() { + if props.lit { return 15; } } else if args.block == &Block::REDSTONE_TORCH { let props = RTorchProps::from_state_id(args.state.id, args.block); - if props.lit && args.direction != BlockDirection::Up { + if props.lit { return 15; } } - 0 - }) + } + 0 } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - if args.block == &Block::REDSTONE_WALL_TORCH { - let props = RWallTorchProps::from_state_id(args.state.id, args.block); - if props.lit { - return 15; - } - } else if args.block == &Block::REDSTONE_TORCH { - let props = RTorchProps::from_state_id(args.state.id, args.block); - if props.lit { - return 15; - } - } - } - 0 - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - if args.block == &Block::REDSTONE_WALL_TORCH { - let mut props = RWallTorchProps::from_state_id(state.id, args.block); - let should_be_lit_now = should_be_lit( - args.world, + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let (block, state) = args.world.get_block_and_state(args.position); + if block == &Block::REDSTONE_WALL_TORCH { + let mut props = RWallTorchProps::from_state_id(state.id, block); + let should_be_lit_now = should_be_lit( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ); + if props.lit != should_be_lit_now { + props.lit = should_be_lit_now; + args.world.set_block_state( args.position, - props.facing.to_block_direction().opposite(), - ) - .await; - if props.lit != should_be_lit_now { - props.lit = should_be_lit_now; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - update_neighbors(args.world, args.position).await; - } - } else if args.block == &Block::REDSTONE_TORCH { - let mut props = RTorchProps::from_state_id(state.id, args.block); - let should_be_lit_now = - should_be_lit(args.world, args.position, BlockDirection::Down).await; - if props.lit != should_be_lit_now { - props.lit = should_be_lit_now; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - update_neighbors(args.world, args.position).await; - } + props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); + update_neighbors(args.world, args.position); } - }) + } else if block == &Block::REDSTONE_TORCH { + let mut props = RTorchProps::from_state_id(state.id, block); + let should_be_lit_now = should_be_lit(args.world, args.position, BlockDirection::Down); + if props.lit != should_be_lit_now { + props.lit = should_be_lit_now; + args.world.set_block_state( + args.position, + props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); + update_neighbors(args.world, args.position); + } + } } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_neighbors(args.world, args.position).await; - }) + fn placed(&self, args: PlacedArgs<'_>) { + update_neighbors(args.world, args.position); } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_neighbors(args.world, args.position).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + update_neighbors(args.world, args.position); } } -pub async fn should_be_lit(world: &World, pos: &BlockPos, face: BlockDirection) -> bool { +pub fn should_be_lit(world: &World, pos: &BlockPos, face: BlockDirection) -> bool { let other_pos = pos.offset(face.to_offset()); let (block, state) = world.get_block_and_state(&other_pos); - get_redstone_power(block, state, world, &other_pos, face).await == 0 + get_redstone_power(block, state, world, &other_pos, face) == 0 } -pub async fn update_neighbors(world: &Arc, pos: &BlockPos) { +pub fn update_neighbors(world: &Arc, pos: &BlockPos) { for dir in BlockDirection::all() { let other_pos = pos.offset(dir.to_offset()); - world.update_neighbors(&other_pos, None).await; + world.update_neighbors(&other_pos, None); } } diff --git a/crates/pumpkin/src/block/blocks/redstone/redstone_wire.rs b/crates/pumpkin/src/block/blocks/redstone/redstone_wire.rs index 6a2fe4d33..c0db4706f 100644 --- a/crates/pumpkin/src/block/blocks/redstone/redstone_wire.rs +++ b/crates/pumpkin/src/block/blocks/redstone/redstone_wire.rs @@ -14,7 +14,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetRedstonePowerArgs, + BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, PrepareArgs, }; @@ -32,222 +32,187 @@ impl BlockBehaviour for RedstoneWireBlock { can_survive(args.block_accessor, args.position) } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let initial_state = make_cross(0); - let wire = get_connection_state(args.world, initial_state, args.position).await; - wire.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let initial_state = make_cross(0); + let wire = get_connection_state(args.world, initial_state, args.position); + wire.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - update_power_strength(args.world, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + update_power_strength(args.world, args.position); - for direction in [BlockDirection::Up, BlockDirection::Down] { - let neighbor_pos = args.position.offset(direction.to_offset()); - update_neighbors_at(args.world, &neighbor_pos, &Block::REDSTONE_WIRE).await; + for direction in [BlockDirection::Up, BlockDirection::Down] { + let neighbor_pos = args.position.offset(direction.to_offset()); + update_neighbors_at(args.world, &neighbor_pos, &Block::REDSTONE_WIRE); + } + + update_neighbors_of_neighboring_wires(args.world, args.position); + } + + fn broken(&self, args: BrokenArgs<'_>) { + for direction in BlockDirection::all() { + let neighbor_pos = args.position.offset(direction.to_offset()); + update_neighbors_at(args.world, &neighbor_pos, &Block::REDSTONE_WIRE); + } + + update_neighbors_of_neighboring_wires(args.world, args.position); + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down { + let (below_block, below_state) = args.world.get_block_and_state(args.neighbor_position); + if !can_survive_on(below_block, below_state) { + return Block::AIR.default_state.id; } + return args.state_id; + } - update_neighbors_of_neighboring_wires(args.world, args.position).await; - }) + let wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); + + if args.direction == BlockDirection::Up { + let new_wire = get_connection_state(args.world, wire, args.position); + return new_wire.to_state_id(args.block); + } + + let can_connect_up = !args + .world + .get_block_state(&args.position.up()) + .is_solid_block(); + let side_connection = + get_connecting_side(args.world, args.position, args.direction, can_connect_up); + + let Some(horizontal) = args.direction.to_horizontal_facing() else { + return args.state_id; + }; + + let current_side = get_side_connection(wire, horizontal); + let is_connected_same = side_connection.is_connected() == current_side.is_connected(); + + if is_connected_same && !is_cross(wire) { + let mut new_wire = wire; + set_side_connection(&mut new_wire, horizontal, side_connection); + new_wire.to_state_id(args.block) + } else { + let mut cross = make_cross(wire.power); + set_side_connection(&mut cross, horizontal, side_connection); + let new_wire = get_connection_state(args.world, cross, args.position); + new_wire.to_state_id(args.block) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - for direction in BlockDirection::all() { - let neighbor_pos = args.position.offset(direction.to_offset()); - update_neighbors_at(args.world, &neighbor_pos, &Block::REDSTONE_WIRE).await; - } + fn prepare(&self, args: PrepareArgs<'_>) { + let wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); - update_neighbors_of_neighboring_wires(args.world, args.position).await; - }) - } + for direction in BlockDirection::horizontal() { + if is_side_connected_prop(wire, direction) { + let dir_block_pos = args.position.offset(direction.to_offset()); + if args.world.get_block(&dir_block_pos) != &Block::REDSTONE_WIRE { + let down_pos = dir_block_pos.down(); + if args.world.get_block(&down_pos) == &Block::REDSTONE_WIRE { + args.world.replace_with_state_for_neighbor_update( + &down_pos, + direction.opposite().to_block_direction(), + args.flags, + ); + } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - let (below_block, below_state) = - args.world.get_block_and_state(args.neighbor_position); - if !can_survive_on(below_block, below_state) { - return Block::AIR.default_state.id; + let up_pos = dir_block_pos.up(); + if args.world.get_block(&up_pos) == &Block::REDSTONE_WIRE { + args.world.replace_with_state_for_neighbor_update( + &up_pos, + direction.opposite().to_block_direction(), + args.flags, + ); + } } - return args.state_id; } + } + } - let wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + let wire = RedstoneWireProperties::from_state_id(state.id, args.block); - if args.direction == BlockDirection::Up { - let new_wire = get_connection_state(args.world, wire, args.position).await; - return new_wire.to_state_id(args.block); - } - - let can_connect_up = !args - .world - .get_block_state(&args.position.up()) - .is_solid_block(); - let side_connection = - get_connecting_side(args.world, args.position, args.direction, can_connect_up) - .await; - - let Some(horizontal) = args.direction.to_horizontal_facing() else { - return args.state_id; + if is_cross(wire) || is_dot(wire) { + let mut new_wire = if is_cross(wire) { + RedstoneWireProperties::default(&Block::REDSTONE_WIRE) + } else { + make_cross(wire.power) }; + new_wire.power = wire.power; + new_wire = get_connection_state(args.world, new_wire, args.position); - let current_side = get_side_connection(wire, horizontal); - let is_connected_same = side_connection.is_connected() == current_side.is_connected(); + if wire != new_wire { + args.world.set_block_state( + args.position, + new_wire.to_state_id(&Block::REDSTONE_WIRE), + BlockFlags::NOTIFY_ALL, + ); - if is_connected_same && !is_cross(wire) { - let mut new_wire = wire; - set_side_connection(&mut new_wire, horizontal, side_connection); - new_wire.to_state_id(args.block) - } else { - let mut cross = make_cross(wire.power); - set_side_connection(&mut cross, horizontal, side_connection); - let new_wire = get_connection_state(args.world, cross, args.position).await; - new_wire.to_state_id(args.block) - } - }) - } + for direction in BlockDirection::horizontal() { + let relative_pos = args.position.offset(direction.to_offset()); + let old_connected = is_side_connected_prop(wire, direction); + let new_connected = is_side_connected_prop(new_wire, direction); - fn prepare<'a>(&'a self, args: PrepareArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); - - for direction in BlockDirection::horizontal() { - if is_side_connected_prop(wire, direction) { - let dir_block_pos = args.position.offset(direction.to_offset()); - if args.world.get_block(&dir_block_pos) != &Block::REDSTONE_WIRE { - let down_pos = dir_block_pos.down(); - if args.world.get_block(&down_pos) == &Block::REDSTONE_WIRE { - args.world - .replace_with_state_for_neighbor_update( - &down_pos, - direction.opposite().to_block_direction(), - args.flags, - ) - .await; - } - - let up_pos = dir_block_pos.up(); - if args.world.get_block(&up_pos) == &Block::REDSTONE_WIRE { - args.world - .replace_with_state_for_neighbor_update( - &up_pos, - direction.opposite().to_block_direction(), - args.flags, - ) - .await; - } + if old_connected != new_connected + && args.world.get_block_state(&relative_pos).is_solid_block() + { + args.world.update_neighbors( + &relative_pos, + Some(direction.opposite().to_block_direction()), + ); } } + + return BlockActionResult::Success; } - }) + } + + BlockActionResult::Pass } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let wire = RedstoneWireProperties::from_state_id(state.id, args.block); - - if is_cross(wire) || is_dot(wire) { - let mut new_wire = if is_cross(wire) { - RedstoneWireProperties::default(&Block::REDSTONE_WIRE) - } else { - make_cross(wire.power) - }; - new_wire.power = wire.power; - new_wire = get_connection_state(args.world, new_wire, args.position).await; - - if wire != new_wire { - args.world - .set_block_state( - args.position, - new_wire.to_state_id(&Block::REDSTONE_WIRE), - BlockFlags::NOTIFY_ALL, - ) - .await; - - for direction in BlockDirection::horizontal() { - let relative_pos = args.position.offset(direction.to_offset()); - let old_connected = is_side_connected_prop(wire, direction); - let new_connected = is_side_connected_prop(new_wire, direction); - - if old_connected != new_connected - && args.world.get_block_state(&relative_pos).is_solid_block() - { - args.world - .update_neighbors( - &relative_pos, - Some(direction.opposite().to_block_direction()), - ) - .await; - } - } - - return BlockActionResult::Success; - } - } - - BlockActionResult::Pass - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + if can_survive(args.world.as_ref(), args.position) { + update_power_strength(args.world, args.position); + } else { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL); + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if can_survive(args.world.as_ref(), args.position) { - update_power_strength(args.world, args.position).await; - } else { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); + if wire.power == 0 || args.direction == BlockDirection::Down { + return 0; + } + if args.direction == BlockDirection::Up { + return wire.power; + } + if let Some(horizontal) = args.direction.opposite().to_horizontal_facing() + && is_side_connected_prop(wire, horizontal) + { + return wire.power; + } + 0 } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); - if wire.power == 0 || args.direction == BlockDirection::Down { - return 0; - } - if args.direction == BlockDirection::Up { - return wire.power; - } - if let Some(horizontal) = args.direction.opposite().to_horizontal_facing() - && is_side_connected_prop(wire, horizontal) - { - return wire.power; - } - 0 - }) - } - - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); - if wire.power == 0 || args.direction == BlockDirection::Down { - return 0; - } - if args.direction == BlockDirection::Up { - return wire.power; - } - if let Some(horizontal) = args.direction.opposite().to_horizontal_facing() - && is_side_connected_prop(wire, horizontal) - { - return wire.power; - } - 0 - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); + if wire.power == 0 || args.direction == BlockDirection::Down { + return 0; + } + if args.direction == BlockDirection::Up { + return wire.power; + } + if let Some(horizontal) = args.direction.opposite().to_horizontal_facing() + && is_side_connected_prop(wire, horizontal) + { + return wire.power; + } + 0 } fn rotate( @@ -311,22 +276,20 @@ impl BlockBehaviour for RedstoneWireBlock { // Evaluator Logic (matching DefaultRedstoneWireEvaluator) // --------------------------------------------------------------------------- -pub async fn update_power_strength(world: &Arc, pos: &BlockPos) { +pub fn update_power_strength(world: &Arc, pos: &BlockPos) { let (block, state) = world.get_block_and_state(pos); if block != &Block::REDSTONE_WIRE { return; } let mut wire = RedstoneWireProperties::from_state_id(state.id, block); - let target_strength = calculate_target_strength(world, pos).await; + let target_strength = calculate_target_strength(world, pos); if wire.power != target_strength { wire.power = target_strength; let new_state_id = wire.to_state_id(&Block::REDSTONE_WIRE); - world - .set_block_state(pos, new_state_id, BlockFlags::empty()) - .await; + world.set_block_state(pos, new_state_id, BlockFlags::empty()); let mut to_update = Vec::with_capacity(7); to_update.push(*pos); @@ -335,13 +298,13 @@ pub async fn update_power_strength(world: &Arc, pos: &BlockPos) { } for block_pos in to_update { - update_neighbors_at(world, &block_pos, &Block::REDSTONE_WIRE).await; + update_neighbors_at(world, &block_pos, &Block::REDSTONE_WIRE); } } } -async fn calculate_target_strength(world: &World, pos: &BlockPos) -> u8 { - let block_signal = get_block_signal(world, pos).await; +fn calculate_target_strength(world: &World, pos: &BlockPos) -> u8 { + let block_signal = get_block_signal(world, pos); if block_signal == 15 { return 15; } @@ -349,14 +312,13 @@ async fn calculate_target_strength(world: &World, pos: &BlockPos) -> u8 { block_signal.max(wire_signal) } -async fn get_block_signal(world: &World, pos: &BlockPos) -> u8 { +fn get_block_signal(world: &World, pos: &BlockPos) -> u8 { let mut max_signal = 0; for side in BlockDirection::all() { let neighbor_pos = pos.offset(side.to_offset()); let (neighbor_block, neighbor_state) = world.get_block_and_state(&neighbor_pos); let signal = - get_redstone_power_no_dust(neighbor_block, neighbor_state, world, neighbor_pos, side) - .await; + get_redstone_power_no_dust(neighbor_block, neighbor_state, world, neighbor_pos, side); if signal == 15 { return 15; } @@ -402,35 +364,35 @@ fn get_incoming_wire_signal(world: &World, pos: &BlockPos) -> u8 { max_wire_signal.saturating_sub(1) } -pub async fn update_neighbors_at(world: &Arc, pos: &BlockPos, source_block: &Block) { +pub fn update_neighbors_at(world: &Arc, pos: &BlockPos, source_block: &Block) { for direction in BlockDirection::update_order() { let neighbor_pos = pos.offset(direction.to_offset()); - world.update_neighbor(&neighbor_pos, source_block).await; + world.update_neighbor(&neighbor_pos, source_block); } } -async fn check_corner_change_at(world: &Arc, pos: &BlockPos) { +fn check_corner_change_at(world: &Arc, pos: &BlockPos) { if world.get_block(pos) == &Block::REDSTONE_WIRE { - update_neighbors_at(world, pos, &Block::REDSTONE_WIRE).await; + update_neighbors_at(world, pos, &Block::REDSTONE_WIRE); for direction in BlockDirection::all() { let neighbor_pos = pos.offset(direction.to_offset()); - update_neighbors_at(world, &neighbor_pos, &Block::REDSTONE_WIRE).await; + update_neighbors_at(world, &neighbor_pos, &Block::REDSTONE_WIRE); } } } -async fn update_neighbors_of_neighboring_wires(world: &Arc, pos: &BlockPos) { +fn update_neighbors_of_neighboring_wires(world: &Arc, pos: &BlockPos) { for direction in BlockDirection::horizontal() { let neighbor_pos = pos.offset(direction.to_offset()); - check_corner_change_at(world, &neighbor_pos).await; + check_corner_change_at(world, &neighbor_pos); } for direction in BlockDirection::horizontal() { let target = pos.offset(direction.to_offset()); if world.get_block_state(&target).is_solid_block() { - check_corner_change_at(world, &target.up()).await; + check_corner_change_at(world, &target.up()); } else { - check_corner_change_at(world, &target.down()).await; + check_corner_change_at(world, &target.down()); } } } @@ -439,7 +401,7 @@ async fn update_neighbors_of_neighboring_wires(world: &Arc, pos: &BlockPo // Connection & Shape Helper Functions // --------------------------------------------------------------------------- -pub async fn get_connection_state( +pub fn get_connection_state( world: &World, state: RedstoneWireProperties, pos: &BlockPos, @@ -448,7 +410,7 @@ pub async fn get_connection_state( let mut default_state = RedstoneWireProperties::default(&Block::REDSTONE_WIRE); default_state.power = state.power; - let mut new_state = get_missing_connections(world, default_state, pos).await; + let mut new_state = get_missing_connections(world, default_state, pos); if was_dot && is_dot(new_state) { return new_state; } @@ -477,7 +439,7 @@ pub async fn get_connection_state( new_state } -async fn get_missing_connections( +fn get_missing_connections( world: &World, mut state: RedstoneWireProperties, pos: &BlockPos, @@ -487,8 +449,7 @@ async fn get_missing_connections( for direction in BlockDirection::horizontal() { if !is_side_connected_prop(state, direction) { let side_connection = - get_connecting_side(world, pos, direction.to_block_direction(), can_connect_up) - .await; + get_connecting_side(world, pos, direction.to_block_direction(), can_connect_up); set_side_connection(&mut state, direction, side_connection); } } @@ -496,7 +457,7 @@ async fn get_missing_connections( state } -async fn get_connecting_side( +fn get_connecting_side( world: &World, pos: &BlockPos, direction: BlockDirection, @@ -509,7 +470,7 @@ async fn get_connecting_side( let is_placeable_above = is_trapdoor(relative_block) || can_survive_on(relative_block, relative_state); let (above_block, above_state) = world.get_block_and_state(&relative_pos.up()); - if is_placeable_above && should_connect_to(world, above_block, above_state, None).await { + if is_placeable_above && should_connect_to(world, above_block, above_state, None) { if relative_state.is_side_solid(direction.opposite()) { return WireConnection::Up; } @@ -518,9 +479,9 @@ async fn get_connecting_side( } let connects_to_relative = - should_connect_to(world, relative_block, relative_state, Some(direction)).await; + should_connect_to(world, relative_block, relative_state, Some(direction)); let (below_block, below_state) = world.get_block_and_state(&relative_pos.down()); - let connects_to_below = should_connect_to(world, below_block, below_state, None).await; + let connects_to_below = should_connect_to(world, below_block, below_state, None); if !connects_to_relative && (relative_state.is_solid_block() || !connects_to_below) { WireConnection::None @@ -529,7 +490,7 @@ async fn get_connecting_side( } } -async fn should_connect_to( +fn should_connect_to( world: &World, block: &Block, state: &BlockState, @@ -549,14 +510,7 @@ async fn should_connect_to( let observer_facing = observer_props.facing; return direction.is_some_and(|dir| dir.to_facing() == observer_facing); } - if let Some(dir) = direction { - world - .block_registry - .emits_redstone_power(block, state, dir) - .await - } else { - false - } + direction.is_some_and(|dir| world.block_registry.emits_redstone_power(block, state, dir)) } fn is_trapdoor(block: &Block) -> bool { diff --git a/crates/pumpkin/src/block/blocks/redstone/repeater.rs b/crates/pumpkin/src/block/blocks/redstone/repeater.rs index dfcda4bb9..541254f6e 100644 --- a/crates/pumpkin/src/block/blocks/redstone/repeater.rs +++ b/crates/pumpkin/src/block/blocks/redstone/repeater.rs @@ -11,7 +11,7 @@ use pumpkin_world::world::BlockFlags; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + BlockBehaviour, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, registry::BlockActionResult, @@ -27,180 +27,132 @@ type RepeaterProperties = pumpkin_data::block_properties::RepeaterLikeProperties pub struct RepeaterBlock; impl BlockBehaviour for RepeaterBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let state_id = RedstoneGateBlock::on_place(self, args.player, args.block).await; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let state_id = RedstoneGateBlock::on_place(self, args.player, args.block); - let mut props = RepeaterProperties::from_state_id(state_id, args.block); - props.locked = self - .is_locked(args.world, *args.position, state_id, args.block) - .await; + let mut props = RepeaterProperties::from_state_id(state_id, args.block); + props.locked = self.is_locked(args.world, *args.position, state_id, args.block); - props.to_state_id(args.block) - }) + props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::on_neighbor_update(self, args).await; - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + RedstoneGateBlock::on_neighbor_update(self, args); } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - if self - .is_locked(args.world, *args.position, state.id, args.block) - .await - { + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let world = args.world.clone(); + let pos = *args.position; + tokio::spawn(async move { + let (block, state) = world.get_block_and_state(&pos); + if Self.is_locked(&world, pos, state.id, block) { return; } - let mut props = RepeaterProperties::from_state_id(state.id, args.block); + let mut props = RepeaterProperties::from_state_id(state.id, block); let now_powered = props.powered; - let should_be_powered = self - .has_power(args.world, *args.position, state, args.block) - .await; + let should_be_powered = Self.has_power(&world, pos, state, block); if now_powered && !should_be_powered { props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - props.to_state_id(args.block), - args.block, - ) - .await; + &Self, + &world, + pos, + props.to_state_id(block), + block, + ); } else if !now_powered { props.powered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS); if !should_be_powered { - args.world.schedule_block_tick( - args.block, - *args.position, + world.schedule_block_tick( + block, + pos, RedstoneGateBlock::get_update_delay_internal( - self, - props.to_state_id(args.block), - args.block, + &Self, + props.to_state_id(block), + block, ), TickPriority::VeryHigh, ); } RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - props.to_state_id(args.block), - args.block, - ) - .await; + &Self, + &world, + pos, + props.to_state_id(block), + block, + ); } - }) + }); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let props = RepeaterProperties::from_state_id(state.id, args.block); - self.on_use(props, args.world, *args.position, args.block) - .await; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state = args.world.get_block_state(args.position); + let props = RepeaterProperties::from_state_id(state.id, args.block); + Self::on_use(props, args.world, *args.position, args.block); - BlockActionResult::Success - }) + BlockActionResult::SuccessServer } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { RedstoneGateBlock::get_weak_redstone_power(self, args).await }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + RedstoneGateBlock::get_weak_redstone_power(self, args) } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { RedstoneGateBlock::get_strong_redstone_power(self, args).await }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + RedstoneGateBlock::get_strong_redstone_power(self, args) } - fn emits_redstone_power<'a>( - &'a self, - args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - let repeater_props = RepeaterProperties::from_state_id(args.state.id, args.block); - repeater_props.facing.to_block_direction() == args.direction - || repeater_props.facing.to_block_direction() == args.direction.opposite() - }) + fn emits_redstone_power(&self, args: EmitsRedstonePowerArgs<'_>) -> bool { + let repeater_props = RepeaterProperties::from_state_id(args.state.id, args.block); + repeater_props.facing.to_block_direction() == args.direction + || repeater_props.facing.to_block_direction() == args.direction.opposite() } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::update_target( + fn placed(&self, args: PlacedArgs<'_>) { + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + args.state_id, + args.block, + ); + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down + && !RedstoneGateBlock::can_place_above( self, args.world, - *args.position, - args.state_id, - args.block, + *args.neighbor_position, + BlockState::from_id(args.neighbor_state_id), ) - .await; - }) + { + return Block::AIR.default_state.id; + } + let mut props = RepeaterProperties::from_state_id(args.state_id, args.block); + if args.direction.to_axis() != props.facing.to_block_direction().to_axis() { + props.locked = self.is_locked(args.world, *args.position, args.state_id, args.block); + return props.to_state_id(args.block); + } + args.state_id } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down - && !RedstoneGateBlock::can_place_above( - self, - args.world, - *args.neighbor_position, - BlockState::from_id(args.neighbor_state_id), - ) - { - return Block::AIR.default_state.id; - } - let mut props = RepeaterProperties::from_state_id(args.state_id, args.block); - if args.direction.to_axis() != props.facing.to_block_direction().to_axis() { - props.locked = self - .is_locked(args.world, *args.position, args.state_id, args.block) - .await; - return props.to_state_id(args.block); - } - args.state_id - }) + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + RedstoneGateBlock::player_placed(self, args); } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::player_placed(self, args).await; - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - RedstoneGateBlock::on_state_replaced(self, args).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + RedstoneGateBlock::on_state_replaced(self, args); } } @@ -219,46 +171,37 @@ impl RedstoneGateBlockProperties for RepeaterProperties { } impl RedstoneGateBlock for RepeaterBlock { - fn get_output_level<'a>(&'a self, _world: &'a World, _pos: BlockPos) -> BlockFuture<'a, u8> { - Box::pin(async { 15 }) + fn get_output_level(&self, _world: &World, _pos: BlockPos) -> u8 { + 15 } - fn update_powered<'a>( - &'a self, - world: &'a World, - pos: BlockPos, - state: &'a BlockState, - block: &'a Block, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Note: is_locked is assumed to remain an async fn or return a future - if self.is_locked(world, pos, state.id, block).await { - return; - } - let props = RepeaterProperties::from_state_id(state.id, block); - let powered = props.powered; + fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) { + if self.is_locked(world, pos, state.id, block) { + return; + } + let props = RepeaterProperties::from_state_id(state.id, block); + let powered = props.powered; - // Note: The signature for has_power must be called without self, as it's a trait method. - let has_power = RedstoneGateBlock::has_power(self, world, pos, state, block).await; + // Note: The signature for has_power must be called without self, as it's a trait method. + let has_power = RedstoneGateBlock::has_power(self, world, pos, state, block); - if powered != has_power && !world.is_block_tick_scheduled(&pos, block) { - let priority = - if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { - TickPriority::ExtremelyHigh - } else if powered { - TickPriority::VeryHigh - } else { - TickPriority::High - }; + if powered != has_power && !world.is_block_tick_scheduled(&pos, block) { + let priority = + if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { + TickPriority::ExtremelyHigh + } else if powered { + TickPriority::VeryHigh + } else { + TickPriority::High + }; - world.schedule_block_tick( - block, - pos, - RedstoneGateBlock::get_update_delay_internal(self, state.id, block), - priority, - ); - } - }) + world.schedule_block_tick( + block, + pos, + RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + priority, + ); + } } fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8 { @@ -268,28 +211,20 @@ impl RedstoneGateBlock for RepeaterBlock { } impl RepeaterBlock { - async fn on_use( - &self, - props: RepeaterProperties, - world: &Arc, - block_pos: BlockPos, - block: &Block, - ) { + fn on_use(props: RepeaterProperties, world: &Arc, block_pos: BlockPos, block: &Block) { let mut props = props; props.delay = if props.delay == 4 { 1 } else { props.delay + 1 }; let state = props.to_state_id(block); - world - .set_block_state(&block_pos, state, BlockFlags::empty()) - .await; + world.set_block_state(&block_pos, state, BlockFlags::empty()); } - async fn is_locked( + fn is_locked( &self, world: &World, pos: BlockPos, state_id: BlockStateId, block: &Block, ) -> bool { - Self::get_max_input_level_sides(self, world, pos, state_id, block, true).await > 0 + Self::get_max_input_level_sides(self, world, pos, state_id, block, true) > 0 } } diff --git a/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs b/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs index 0a70c724c..a55c09122 100644 --- a/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs +++ b/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::block::entities::calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity; use crate::block::entities::sculk_sensor::SculkSensorBlockEntity; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, EmitsRedstonePowerArgs, GetComparatorOutputArgs, + BlockBehaviour, BlockMetadata, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::world::World; @@ -34,7 +34,7 @@ const fn horizontal_facing_to_dir(facing: HorizontalFacing) -> BlockDirection { } impl SculkSensorBlock { - pub async fn trigger(world: &Arc, pos: &BlockPos, block: &Block, power: u8) { + pub fn trigger(world: &Arc, pos: &BlockPos, block: &Block, power: u8) { if block.id == BlockId::SCULK_SENSOR { let state = world.get_block_state(pos); let mut props = SculkSensorLikeProperties::from_state_id(state.id, block); @@ -42,15 +42,13 @@ impl SculkSensorBlock { if let Some(be) = world.get_block_entity(pos) && let Some(sensor_be) = be.as_any().downcast_ref::() { - *sensor_be.last_vibration_frequency.lock().await = power as i32; + *sensor_be.last_vibration_frequency.blocking_lock() = power as i32; } props.sculk_sensor_phase = SculkSensorPhase::Active; props.power = power; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; - world.update_neighbors(pos, None).await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); + world.update_neighbors(pos, None); world.schedule_block_tick(block, *pos, 30, TickPriority::Normal); } } else if block.id == BlockId::CALIBRATED_SCULK_SENSOR { @@ -64,8 +62,7 @@ impl SculkSensorBlock { let calibrated_freq = world .block_registry - .get_weak_redstone_power(back_block, world, &back_pos, back_state, back_dir) - .await; + .get_weak_redstone_power(back_block, world, &back_pos, back_state, back_dir); if calibrated_freq > 0 && calibrated_freq != power { return; @@ -76,15 +73,13 @@ impl SculkSensorBlock { .as_any() .downcast_ref::() { - *cal_be.last_vibration_frequency.lock().await = power as i32; + *cal_be.last_vibration_frequency.blocking_lock() = power as i32; } props.sculk_sensor_phase = SculkSensorPhase::Active; props.power = power; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; - world.update_neighbors(pos, None).await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); + world.update_neighbors(pos, None); world.schedule_block_tick(block, *pos, 30, TickPriority::Normal); } } @@ -92,158 +87,127 @@ impl SculkSensorBlock { } impl BlockBehaviour for SculkSensorBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { - let mut props = CalibratedSculkSensorLikeProperties::default(args.block); - props.facing = args.player.living_entity.entity.get_horizontal_facing(); - props.to_state_id(args.block) - } else { - let props = SculkSensorLikeProperties::default(args.block); - props.to_state_id(args.block) - } - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { + let mut props = CalibratedSculkSensorLikeProperties::default(args.block); + props.facing = args.player.living_entity.entity.get_horizontal_facing(); + props.to_state_id(args.block) + } else { + let props = SculkSensorLikeProperties::default(args.block); + props.to_state_id(args.block) + } } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { - let entity = CalibratedSculkSensorBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - } else if args.block.id == BlockId::SCULK_SENSOR { - let entity = SculkSensorBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - } - }) + fn placed(&self, args: PlacedArgs<'_>) { + if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { + let entity = CalibratedSculkSensorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + } else if args.block.id == BlockId::SCULK_SENSOR { + let entity = SculkSensorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + } } - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - if args.block.id == BlockId::SCULK_SENSOR { - let props = SculkSensorLikeProperties::from_state_id(args.state.id, args.block); - if props.sculk_sensor_phase == SculkSensorPhase::Active { - props.power - } else { - 0 - } - } else if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { - let props = - CalibratedSculkSensorLikeProperties::from_state_id(args.state.id, args.block); - if props.sculk_sensor_phase == SculkSensorPhase::Active { - props.power - } else { - 0 - } + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + if args.block.id == BlockId::SCULK_SENSOR { + let props = SculkSensorLikeProperties::from_state_id(args.state.id, args.block); + if props.sculk_sensor_phase == SculkSensorPhase::Active { + props.power } else { 0 } - }) + } else if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { + let props = + CalibratedSculkSensorLikeProperties::from_state_id(args.state.id, args.block); + if props.sculk_sensor_phase == SculkSensorPhase::Active { + props.power + } else { + 0 + } + } else { + 0 + } } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - let be = args.world.get_block_entity(args.position)?; - if let Some(sensor_be) = be.as_any().downcast_ref::() { - return Some(*sensor_be.last_vibration_frequency.lock().await as u8); - } - if let Some(cal_be) = be - .as_any() - .downcast_ref::() - { - return Some(*cal_be.last_vibration_frequency.lock().await as u8); - } - None - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + let be = args.world.get_block_entity(args.position)?; + if let Some(sensor_be) = be.as_any().downcast_ref::() { + return Some(*sensor_be.last_vibration_frequency.blocking_lock() as u8); + } + if let Some(cal_be) = be + .as_any() + .downcast_ref::() + { + return Some(*cal_be.last_vibration_frequency.blocking_lock() as u8); + } + None } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - if args.block.id == BlockId::SCULK_SENSOR { - let mut props = SculkSensorLikeProperties::from_state_id(state.id, args.block); - match props.sculk_sensor_phase { - SculkSensorPhase::Active => { - props.sculk_sensor_phase = SculkSensorPhase::Cooldown; - props.power = 0; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world.schedule_block_tick( - args.block, - *args.position, - 10, - TickPriority::Normal, - ); - args.world.update_neighbors(args.position, None).await; - } - SculkSensorPhase::Cooldown => { - props.sculk_sensor_phase = SculkSensorPhase::Inactive; - props.power = 0; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world.update_neighbors(args.position, None).await; - } - SculkSensorPhase::Inactive => {} + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + if args.block.id == BlockId::SCULK_SENSOR { + let mut props = SculkSensorLikeProperties::from_state_id(state.id, args.block); + match props.sculk_sensor_phase { + SculkSensorPhase::Active => { + props.sculk_sensor_phase = SculkSensorPhase::Cooldown; + props.power = 0; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + args.world.schedule_block_tick( + args.block, + *args.position, + 10, + TickPriority::Normal, + ); } - } else if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { - let mut props = - CalibratedSculkSensorLikeProperties::from_state_id(state.id, args.block); - match props.sculk_sensor_phase { - SculkSensorPhase::Active => { - props.sculk_sensor_phase = SculkSensorPhase::Cooldown; - props.power = 0; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world.schedule_block_tick( - args.block, - *args.position, - 10, - TickPriority::Normal, - ); - args.world.update_neighbors(args.position, None).await; - } - SculkSensorPhase::Cooldown => { - props.sculk_sensor_phase = SculkSensorPhase::Inactive; - props.power = 0; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - args.world.update_neighbors(args.position, None).await; - } - SculkSensorPhase::Inactive => {} + SculkSensorPhase::Cooldown => { + props.sculk_sensor_phase = SculkSensorPhase::Inactive; + props.power = 0; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } + SculkSensorPhase::Inactive => {} } - }) + } else if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { + let mut props = + CalibratedSculkSensorLikeProperties::from_state_id(state.id, args.block); + match props.sculk_sensor_phase { + SculkSensorPhase::Active => { + props.sculk_sensor_phase = SculkSensorPhase::Cooldown; + props.power = 0; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + args.world.schedule_block_tick( + args.block, + *args.position, + 10, + TickPriority::Normal, + ); + } + SculkSensorPhase::Cooldown => { + props.sculk_sensor_phase = SculkSensorPhase::Inactive; + props.power = 0; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + SculkSensorPhase::Inactive => {} + } + } } } diff --git a/crates/pumpkin/src/block/blocks/redstone/target_block.rs b/crates/pumpkin/src/block/blocks/redstone/target_block.rs index cbb96c0da..4ed3b3593 100644 --- a/crates/pumpkin/src/block/blocks/redstone/target_block.rs +++ b/crates/pumpkin/src/block/blocks/redstone/target_block.rs @@ -1,15 +1,12 @@ use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, BlockFuture, EmitsRedstonePowerArgs}; +use crate::block::{BlockBehaviour, EmitsRedstonePowerArgs}; #[pumpkin_block("minecraft:target")] pub struct TargetBlock; impl BlockBehaviour for TargetBlock { - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } } diff --git a/crates/pumpkin/src/block/blocks/redstone/tripwire.rs b/crates/pumpkin/src/block/blocks/redstone/tripwire.rs index a28b6e030..2116459cb 100644 --- a/crates/pumpkin/src/block/blocks/redstone/tripwire.rs +++ b/crates/pumpkin/src/block/blocks/redstone/tripwire.rs @@ -9,7 +9,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; use pumpkin_world::{tick::TickPriority, world::BlockFlags}; -use crate::block::BlockFuture; use crate::{ block::{ BlockBehaviour, BrokenArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, @@ -27,145 +26,122 @@ type TripwireHookProperties = pumpkin_data::block_properties::TripwireHookLikePr pub struct TripwireBlock; impl BlockBehaviour for TripwireBlock { - fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let mut props = TripwireProperties::from_state_id(args.state.id, args.block); - if props.powered { - return; - } - props.powered = true; + fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { + let mut props = TripwireProperties::from_state_id(args.state.id, args.block); + if props.powered { + return; + } + props.powered = true; + let state_id = props.to_state_id(args.block); + args.world + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL); + + Self::update(args.world, args.position, state_id); + + args.world + .schedule_block_tick(args.block, *args.position, 10, TickPriority::Normal); + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let [connect_north, connect_east, connect_south, connect_west] = [ + BlockDirection::North, + BlockDirection::East, + BlockDirection::South, + BlockDirection::West, + ] + .map(|dir| { + let current_pos = args.position.offset(dir.to_offset()); + let state_id = args.world.get_block_state_id(¤t_pos); + Self::should_connect_to(state_id, dir) + }); + + let mut props = TripwireProperties::from_state_id(args.block.default_state.id, args.block); + + props.north = connect_north; + props.south = connect_south; + props.west = connect_west; + props.east = connect_east; + + props.to_state_id(args.block) + } + + fn placed(&self, args: PlacedArgs<'_>) { + if Block::from_state_id(args.old_state_id) == Block::from_state_id(args.state_id) { + return; + } + + Self::update(args.world, args.position, args.state_id); + } + + fn broken(&self, args: BrokenArgs<'_>) { + let has_shears = args.player.inventory().held_item().get_item() == &Item::SHEARS; + if has_shears { + let mut props = TripwireProperties::from_state_id(args.state.id, args.block); + props.disarmed = true; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::empty(), + ); + // TODO world.emitGameEvent(player, GameEvent.SHEAR, pos); + // TODO: Deduct 1 durability from held shears (skip in Creative mode). + } + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + args.direction + .to_horizontal_facing() + .map_or(args.state_id, |facing| { + let mut props = TripwireProperties::from_state_id(args.state_id, args.block); + *match facing { + HorizontalFacing::North => &mut props.north, + HorizontalFacing::South => &mut props.south, + HorizontalFacing::West => &mut props.west, + HorizontalFacing::East => &mut props.east, + } = Self::should_connect_to(args.neighbor_state_id, args.direction); + props.to_state_id(args.block) + }) + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + + let mut props = TripwireProperties::from_state_id(state_id, args.block); + if !props.powered { + return; + } + + let aabb = BoundingBox::from_block(args.position); + // TODO entity.canAvoidTraps() + if args.world.get_entities_at_box(&aabb).is_empty() + && args.world.get_players_at_box(&aabb).is_empty() + { + props.powered = false; let state_id = props.to_state_id(args.block); args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - - Self::update(args.world, args.position, state_id).await; - + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL); + Self::update(args.world, args.position, state_id); + } else { args.world .schedule_block_tick(args.block, *args.position, 10, TickPriority::Normal); - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let [connect_north, connect_east, connect_south, connect_west] = [ - BlockDirection::North, - BlockDirection::East, - BlockDirection::South, - BlockDirection::West, - ] - .map(async |dir| { - let current_pos = args.position.offset(dir.to_offset()); - let state_id = args.world.get_block_state_id(¤t_pos); - Self::should_connect_to(state_id, dir) - }); - - let mut props = - TripwireProperties::from_state_id(args.block.default_state.id, args.block); - - props.north = connect_north.await; - props.south = connect_south.await; - props.west = connect_west.await; - props.east = connect_east.await; - - props.to_state_id(args.block) - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if Block::from_state_id(args.old_state_id) == Block::from_state_id(args.state_id) { - return; - } - - Self::update(args.world, args.position, args.state_id).await; - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let has_shears = args.player.inventory().held_item().await.get_item() == &Item::SHEARS; - if has_shears { - let mut props = TripwireProperties::from_state_id(args.state.id, args.block); - props.disarmed = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::empty(), - ) - .await; - // TODO world.emitGameEvent(player, GameEvent.SHEAR, pos); - // TODO: Deduct 1 durability from held shears (skip in Creative mode). - } - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - args.direction - .to_horizontal_facing() - .map_or(args.state_id, |facing| { - let mut props = TripwireProperties::from_state_id(args.state_id, args.block); - *match facing { - HorizontalFacing::North => &mut props.north, - HorizontalFacing::South => &mut props.south, - HorizontalFacing::West => &mut props.west, - HorizontalFacing::East => &mut props.east, - } = Self::should_connect_to(args.neighbor_state_id, args.direction); - props.to_state_id(args.block) - }) - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - - let mut props = TripwireProperties::from_state_id(state_id, args.block); - if !props.powered { - return; - } - - let aabb = BoundingBox::from_block(args.position); - // TODO entity.canAvoidTraps() - if args.world.get_entities_at_box(&aabb).is_empty() - && args.world.get_players_at_box(&aabb).is_empty() - { - props.powered = false; - let state_id = props.to_state_id(args.block); - args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - Self::update(args.world, args.position, state_id).await; - } else { - args.world.schedule_block_tick( - args.block, - *args.position, - 10, - TickPriority::Normal, - ); - } - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - let state_id = args.world.get_block_state_id(args.position); - Self::update(args.world, args.position, state_id).await; - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + let state_id = args.world.get_block_state_id(args.position); + Self::update(args.world, args.position, state_id); } } impl TripwireBlock { - async fn update(world: &Arc, pos: &BlockPos, state_id: BlockStateId) { + fn update(world: &Arc, pos: &BlockPos, state_id: BlockStateId) { for dir in [BlockDirection::South, BlockDirection::West] { for i in 1..42 { let current_pos = pos.offset_dir(dir.to_offset(), i); @@ -186,8 +162,7 @@ impl TripwireBlock { true, i, Some(state_id), - ) - .await; + ); } break; } diff --git a/crates/pumpkin/src/block/blocks/redstone/tripwire_hook.rs b/crates/pumpkin/src/block/blocks/redstone/tripwire_hook.rs index 57838b556..03f7dcdb5 100644 --- a/crates/pumpkin/src/block/blocks/redstone/tripwire_hook.rs +++ b/crates/pumpkin/src/block/blocks/redstone/tripwire_hook.rs @@ -15,7 +15,7 @@ use rand::{RngExt, rng}; use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + BlockBehaviour, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, PlayerPlacedArgs, }, @@ -29,17 +29,15 @@ type TripwireHookProperties = pumpkin_data::block_properties::TripwireHookLikePr pub struct TripwireHookBlock; impl BlockBehaviour for TripwireHookBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = TripwireHookProperties::default(args.block); - props.powered = false; - props.attached = false; - if Self::can_place_at(args.world, args.position, args.direction) { - props.facing = args.direction.opposite().to_cardinal_direction(); - return props.to_state_id(args.block); - } - args.block.default_state.id - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = TripwireHookProperties::default(args.block); + props.powered = false; + props.attached = false; + if Self::can_place_at(args.world, args.position, args.direction) { + props.facing = args.direction.opposite().to_cardinal_direction(); + return props.to_state_id(args.block); + } + args.block.default_state.id } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -52,110 +50,85 @@ impl BlockBehaviour for TripwireHookBlock { ) } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + Self::update( + args.world, + *args.position, + args.state_id, + false, + false, + -1, + None, + ); + } + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction.to_horizontal_facing().is_some_and(|facing| { + let props = TripwireHookProperties::from_state_id(args.state_id, args.block); + facing.opposite() == props.facing + }) && !Self::can_place_at(args.world, args.position, args.direction) + { + Block::AIR.default_state.id + } else { + args.state_id + } + } + + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + Self::update(args.world, *args.position, state_id, false, true, -1, None); + } + + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + let props = TripwireHookProperties::from_state_id(args.old_state_id, args.block); + if props.powered || props.attached { Self::update( args.world, *args.position, - args.state_id, - false, + args.old_state_id, + true, false, -1, None, - ) - .await; - }) - } - - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction.to_horizontal_facing().is_some_and(|facing| { - let props = TripwireHookProperties::from_state_id(args.state_id, args.block); - facing.opposite() == props.facing - }) && !Self::can_place_at(args.world, args.position, args.direction) - { - Block::AIR.default_state.id - } else { - args.state_id - } - }) - } - - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - Self::update(args.world, *args.position, state_id, false, true, -1, None).await; - }) - } - - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - let props = TripwireHookProperties::from_state_id(args.old_state_id, args.block); - if props.powered || props.attached { - Self::update( - args.world, - *args.position, - args.old_state_id, - true, - false, - -1, - None, - ) - .await; - } - if props.powered { - args.world.update_neighbor(args.position, args.block).await; - args.world - .update_neighbor( - &args.position.offset(props.facing.opposite().to_offset()), - args.block, - ) - .await; - } - }) + ); + } + if props.powered { + args.world.update_neighbor(args.position, args.block); + args.world.update_neighbor( + &args.position.offset(props.facing.opposite().to_offset()), + args.block, + ); + } } #[inline] - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + true } - fn get_weak_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = TripwireHookProperties::from_state_id(args.state.id, args.block); - if props.powered { 15 } else { 0 } - }) + fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = TripwireHookProperties::from_state_id(args.state.id, args.block); + if props.powered { 15 } else { 0 } } - fn get_strong_redstone_power<'a>( - &'a self, - args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { - let props = TripwireHookProperties::from_state_id(args.state.id, args.block); - if props.powered - && args - .direction - .to_horizontal_facing() - .is_some_and(|facing| props.facing == facing) - { - 15 - } else { - 0 - } - }) + fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { + let props = TripwireHookProperties::from_state_id(args.state.id, args.block); + if props.powered + && args + .direction + .to_horizontal_facing() + .is_some_and(|facing| props.facing == facing) + { + 15 + } else { + 0 + } } } @@ -174,7 +147,7 @@ impl TripwireHookBlock { } #[expect(clippy::too_many_lines)] - pub async fn update( + pub fn update( world: &Arc, start_hook_pos: BlockPos, start_hook_state_id: BlockStateId, @@ -241,20 +214,17 @@ impl TripwireHookBlock { let future_hook_facing = start_hook_props.facing.opposite(); let mut future_end_hook_state = future_hook_state; future_end_hook_state.facing = future_hook_facing; - world - .set_block_state( - &end_hook_pos, - future_end_hook_state.to_state_id(&Block::TRIPWIRE_HOOK), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &end_hook_pos, + future_end_hook_state.to_state_id(&Block::TRIPWIRE_HOOK), + BlockFlags::NOTIFY_ALL, + ); Self::update_neighbors_on_axis( &Block::TRIPWIRE_HOOK, world, end_hook_pos, BlockDirection::from_cardinal_direction(future_hook_facing), - ) - .await; + ); Self::play_sound( world, &end_hook_pos, @@ -277,21 +247,18 @@ impl TripwireHookBlock { if !skip_state_update { let mut future_start_hook_state = future_hook_state; future_start_hook_state.facing = start_hook_props.facing; - world - .set_block_state( - &start_hook_pos, - future_start_hook_state.to_state_id(&Block::TRIPWIRE_HOOK), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &start_hook_pos, + future_start_hook_state.to_state_id(&Block::TRIPWIRE_HOOK), + BlockFlags::NOTIFY_ALL, + ); if notify_neighbors { Self::update_neighbors_on_axis( &Block::TRIPWIRE_HOOK, world, start_hook_pos, BlockDirection::from_cardinal_direction(start_hook_props.facing), - ) - .await; + ); } } @@ -301,13 +268,11 @@ impl TripwireHookBlock { start_hook_pos.offset_dir(start_hook_props.facing.to_offset(), l); if let Some(mut lv8) = wires_props[l as usize] { lv8.attached = future_attached; - world - .set_block_state( - ¤t_wrie_pos, - lv8.to_state_id(&Block::TRIPWIRE), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + ¤t_wrie_pos, + lv8.to_state_id(&Block::TRIPWIRE), + BlockFlags::NOTIFY_ALL, + ); // if world.get_block(&lv7) != Block::AIR {} } } @@ -341,18 +306,16 @@ impl TripwireHookBlock { } } - pub async fn update_neighbors_on_axis( + pub fn update_neighbors_on_axis( block: &Block, world: &Arc, block_pos: BlockPos, direction: BlockDirection, ) { - world.update_neighbor(&block_pos, block).await; - world - .update_neighbors( - &block_pos.offset(direction.opposite().to_offset()), - Some(direction), - ) - .await; + world.update_neighbor(&block_pos, block); + world.update_neighbors( + &block_pos.offset(direction.opposite().to_offset()), + Some(direction), + ); } } diff --git a/crates/pumpkin/src/block/blocks/respawn_anchor.rs b/crates/pumpkin/src/block/blocks/respawn_anchor.rs index b084318d9..47b4a987c 100644 --- a/crates/pumpkin/src/block/blocks/respawn_anchor.rs +++ b/crates/pumpkin/src/block/blocks/respawn_anchor.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use pumpkin_data::block_properties::{BlockProperties, RespawnAnchorLikeProperties}; use pumpkin_data::dimension::Dimension; use pumpkin_data::item::Item; @@ -7,106 +9,103 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, UseWithItemArgs}; use crate::entity::EntityBase; #[pumpkin_block("minecraft:respawn_anchor")] pub struct RespawnAnchorBlock; impl BlockBehaviour for RespawnAnchorBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if args.item_stack.item.id != Item::GLOWSTONE.id { - return BlockActionResult::Pass; - } + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + if args.item_stack.item.id != Item::GLOWSTONE.id { + return BlockActionResult::Pass; + } - let state_id = args.world.get_block_state_id(args.position); - let mut props = RespawnAnchorLikeProperties::from_state_id(state_id, args.block); + let state_id = args.world.get_block_state_id(args.position); + let mut props = RespawnAnchorLikeProperties::from_state_id(state_id, args.block); - if props.charges >= 4 { - return BlockActionResult::Pass; - } + if props.charges >= 4 { + return BlockActionResult::Pass; + } - props.charges += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + props.charges += 1; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); - args.item_stack - .decrement_unless_creative(args.player.gamemode.load(), 1); + args.item_stack + .decrement_unless_creative(args.player.gamemode.load(), 1); - args.world.play_sound( - Sound::BlockRespawnAnchorCharge, - SoundCategory::Blocks, - &args.position.to_f64(), - ); + args.world.play_sound( + Sound::BlockRespawnAnchorCharge, + SoundCategory::Blocks, + &args.position.to_f64(), + ); - BlockActionResult::Success - }) + BlockActionResult::Success } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let props = RespawnAnchorLikeProperties::from_state_id(state_id, args.block); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + let props = RespawnAnchorLikeProperties::from_state_id(state_id, args.block); - if args.world.dimension != Dimension::THE_NETHER { - args.world - .break_block(args.position, None, BlockFlags::SKIP_DROPS) + if args.world.dimension != Dimension::THE_NETHER { + args.world + .break_block(args.position, None, BlockFlags::SKIP_DROPS); + let world = Arc::clone(args.world); + let center_pos = args.position.to_centered_f64(); + tokio::spawn(async move { + world + .explode(center_pos, 5.0, crate::world::ExplosionInteraction::Block) .await; - args.world - .explode( - args.position.to_centered_f64(), - 5.0, - crate::world::ExplosionInteraction::Block, - ) - .await; - return BlockActionResult::SuccessServer; - } + }); + return BlockActionResult::SuccessServer; + } - if props.charges == 0 { - args.player + if props.charges == 0 { + let player = Arc::clone(args.player); + tokio::spawn(async move { + player .send_system_message(&pumpkin_macros::translate_cross!( translation::java::BLOCK_MINECRAFT_BED_NO_SLEEP, translation::bedrock::TILE_BED_NOSLEEP )) .await; - return BlockActionResult::SuccessServer; - } + }); + return BlockActionResult::SuccessServer; + } - if args - .player + let player = Arc::clone(args.player); + let world = Arc::clone(args.world); + let pos = *args.position; + tokio::spawn(async move { + if player .set_respawn_point( - args.world.dimension.clone(), - *args.position, - args.player.get_entity().yaw.load(), - args.player.get_entity().pitch.load(), + world.dimension.clone(), + pos, + player.get_entity().yaw.load(), + player.get_entity().pitch.load(), false, ) .await { - args.world.play_sound( + world.play_sound( Sound::BlockRespawnAnchorSetSpawn, SoundCategory::Blocks, - &args.position.to_f64(), + &pos.to_f64(), ); - args.player + player .send_system_message(&pumpkin_macros::translate_cross!( translation::java::BLOCK_MINECRAFT_SET_SPAWN, translation::bedrock::TILE_BED_RESPAWNSET )) .await; } + }); - BlockActionResult::SuccessServer - }) + BlockActionResult::SuccessServer } } diff --git a/crates/pumpkin/src/block/blocks/rooted_dirt.rs b/crates/pumpkin/src/block/blocks/rooted_dirt.rs index 5ff2dcd13..2823c94a5 100644 --- a/crates/pumpkin/src/block/blocks/rooted_dirt.rs +++ b/crates/pumpkin/src/block/blocks/rooted_dirt.rs @@ -2,7 +2,7 @@ use pumpkin_data::Block; use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; -use crate::block::{BlockBehaviour, BlockFuture, BonemealArgs}; +use crate::block::{BlockBehaviour, BonemealArgs}; #[pumpkin_block("minecraft:rooted_dirt")] pub struct RootedDirtBlock; @@ -15,15 +15,13 @@ impl BlockBehaviour for RootedDirtBlock { && args.world.get_block_state(&below).is_air() } - fn perform_bonemeal<'a>(&'a self, args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world - .set_block_state( - &args.position.down(), - Block::HANGING_ROOTS.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - }) + fn perform_bonemeal(&self, args: BonemealArgs<'_>) { + { + args.world.set_block_state( + &args.position.down(), + Block::HANGING_ROOTS.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/sculk/sculk_catalyst.rs b/crates/pumpkin/src/block/blocks/sculk/sculk_catalyst.rs index 97fee6577..8b1932415 100644 --- a/crates/pumpkin/src/block/blocks/sculk/sculk_catalyst.rs +++ b/crates/pumpkin/src/block/blocks/sculk/sculk_catalyst.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs}; +use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs}; use pumpkin_data::{ BlockId, BlockStateId, block_properties::{BlockProperties, SculkCatalystLikeProperties}, @@ -13,11 +13,9 @@ impl BlockMetadata for SculkCatalystBlock { } impl BlockBehaviour for SculkCatalystBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = SculkCatalystLikeProperties::default(args.block); - props.bloom = false; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = SculkCatalystLikeProperties::default(args.block); + props.bloom = false; + props.to_state_id(args.block) } } diff --git a/crates/pumpkin/src/block/blocks/sculk/sculk_shrieker.rs b/crates/pumpkin/src/block/blocks/sculk/sculk_shrieker.rs index ee520a21d..d6de5b1e6 100644 --- a/crates/pumpkin/src/block/blocks/sculk/sculk_shrieker.rs +++ b/crates/pumpkin/src/block/blocks/sculk/sculk_shrieker.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::block::entities::sculk_shrieker::SculkShriekerBlockEntity; -use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs, OnScheduledTickArgs}; +use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs, OnScheduledTickArgs}; use crate::world::World; use pumpkin_data::potion::Effect; use pumpkin_data::{ @@ -40,9 +40,7 @@ impl SculkShriekerBlock { } props.shrieking = true; - world - .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL); world.play_sound( Sound::BlockSculkShriekerShriek, @@ -64,7 +62,7 @@ impl SculkShriekerBlock { blend: true, }; player.send_effect(darkness.clone()).await; - player.living_entity.add_effect(darkness).await; + player.living_entity.add_effect(darkness); } if let Some(entity) = world.get_block_entity(pos) @@ -82,29 +80,23 @@ impl SculkShriekerBlock { } impl BlockBehaviour for SculkShriekerBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = SculkShriekerLikeProperties::default(args.block); - props.shrieking = false; - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = SculkShriekerLikeProperties::default(args.block); + props.shrieking = false; + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state = args.world.get_block_state(args.position); - let mut props = SculkShriekerLikeProperties::from_state_id(state.id, args.block); - if props.shrieking { - props.shrieking = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state = args.world.get_block_state(args.position); + let mut props = SculkShriekerLikeProperties::from_state_id(state.id, args.block); + if props.shrieking { + props.shrieking = false; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/sculk/sculk_vein.rs b/crates/pumpkin/src/block/blocks/sculk/sculk_vein.rs index 3bad94711..945e4b602 100644 --- a/crates/pumpkin/src/block/blocks/sculk/sculk_vein.rs +++ b/crates/pumpkin/src/block/blocks/sculk/sculk_vein.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use crate::block::{ - BlockBehaviour, BlockFuture, BlockIsReplacing, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, + BlockBehaviour, BlockIsReplacing, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, UseWithItemArgs, registry::BlockActionResult, }; use crate::entity::{EntityBase, player::Player}; @@ -22,37 +22,35 @@ impl BlockMetadata for SculkVeinBlock { } impl BlockBehaviour for SculkVeinBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if let BlockIsReplacing::Itself(state_id) = args.replacing { - let (Some(direction), _) = get_attach_direction( - args.world, - args.position, - Some(args.player), - args.direction, - true, - ) else { - return Block::AIR.default_state.id; - }; - let mut props = GlowLichenLikeProperties::from_state_id(state_id, args.block); - set_face(&mut props, direction); - props.waterlogged = args.replacing.water_source(); - return props.to_state_id(args.block); - } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if let BlockIsReplacing::Itself(state_id) = args.replacing { let (Some(direction), _) = get_attach_direction( args.world, args.position, Some(args.player), args.direction, - false, + true, ) else { return Block::AIR.default_state.id; }; - let mut props = GlowLichenLikeProperties::default(args.block); + let mut props = GlowLichenLikeProperties::from_state_id(state_id, args.block); set_face(&mut props, direction); props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + return props.to_state_id(args.block); + } + let (Some(direction), _) = get_attach_direction( + args.world, + args.position, + Some(args.player), + args.direction, + false, + ) else { + return Block::AIR.default_state.id; + }; + let mut props = GlowLichenLikeProperties::default(args.block); + set_face(&mut props, direction); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -79,37 +77,32 @@ impl BlockBehaviour for SculkVeinBlock { .is_some() } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let old_props = GlowLichenLikeProperties::from_state_id(args.state_id, args.block); - let mut new_directions = active_directions(old_props); + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let old_props = GlowLichenLikeProperties::from_state_id(args.state_id, args.block); + let mut new_directions = active_directions(old_props); - let support = args - .world - .get_block(&args.position.offset(args.direction.to_offset())); - if !is_solid_face(support) { - new_directions.remove(&args.direction); - } + let support = args + .world + .get_block(&args.position.offset(args.direction.to_offset())); + if !is_solid_face(support) { + new_directions.remove(&args.direction); + } - if new_directions.is_empty() { - return Block::AIR.default_state.id; - } - let mut new_props = GlowLichenLikeProperties::default(args.block); - for dir in new_directions { - set_face(&mut new_props, dir); - } - new_props.to_state_id(args.block) - }) + if new_directions.is_empty() { + return Block::AIR.default_state.id; + } + let mut new_props = GlowLichenLikeProperties::default(args.block); + for dir in new_directions { + set_face(&mut new_props, dir); + } + new_props.to_state_id(args.block) } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { if args.item_stack.item.id != Item::SCULK_VEIN.id { return BlockActionResult::Pass; } @@ -127,15 +120,13 @@ impl BlockBehaviour for SculkVeinBlock { }; set_face(&mut props, accurate_dir); - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); BlockActionResult::Consume - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/shelf.rs b/crates/pumpkin/src/block/blocks/shelf.rs index b42eceba2..977798b62 100644 --- a/crates/pumpkin/src/block/blocks/shelf.rs +++ b/crates/pumpkin/src/block/blocks/shelf.rs @@ -3,7 +3,7 @@ use pumpkin_data::block_properties::{AcaciaShelfLikeProperties, BlockProperties} use pumpkin_macros::pumpkin_block_from_tag; use crate::block::entities::shelf::ShelfBlockEntity; -use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs}; use crate::entity::EntityBase; use std::sync::Arc; @@ -11,21 +11,19 @@ use std::sync::Arc; pub struct ShelfBlock; impl BlockBehaviour for ShelfBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut properties = AcaciaShelfLikeProperties::default(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut properties = AcaciaShelfLikeProperties::default(args.block); - // Face in the opposite direction the player is facing - properties.facing = args.player.get_entity().get_horizontal_facing().opposite(); + // Face in the opposite direction the player is facing + properties.facing = args.player.get_entity().get_horizontal_facing().opposite(); - properties.to_state_id(args.block) - }) + properties.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = ShelfBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/shulker_box.rs b/crates/pumpkin/src/block/blocks/shulker_box.rs index 36fc3341e..de931ab0e 100644 --- a/crates/pumpkin/src/block/blocks/shulker_box.rs +++ b/crates/pumpkin/src/block/blocks/shulker_box.rs @@ -1,8 +1,6 @@ use std::sync::Arc; -use crate::block::{ - BlockFuture, GetComparatorOutputArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, -}; +use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -53,66 +51,56 @@ pub struct ShulkerBoxBlock; type EndRodLikeProperties = pumpkin_data::block_properties::EndRodLikeProperties; impl BlockBehaviour for ShulkerBoxBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = EndRodLikeProperties::default(args.block); - props.facing = args.direction.to_facing().opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = EndRodLikeProperties::default(args.block); + props.facing = args.direction.to_facing().opposite(); + props.to_state_id(args.block) } - fn on_synced_block_event<'a>( - &'a self, - args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { - // On the server, we don't need the Animation steps for now, because the client is responsible for that. - // TODO: Do not open the shulker box when it is currently closing - args.r#type == Self::OPEN_ANIMATION_EVENT_TYPE - }) + fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { + // On the server, we don't need the Animation steps for now, because the client is responsible for that. + // TODO: Do not open the shulker box when it is currently closing + args.r#type == Self::OPEN_ANIMATION_EVENT_TYPE } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let barrel_block_entity = ShulkerBoxBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(barrel_block_entity)); - }) + } } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::OpenShulkerBox as i32, - 1, - ) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::OpenShulkerBox as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&ShulkerBoxScreenFactory(inventory), Some(pos)) .await; - args.player - .open_handled_screen(&ShulkerBoxScreenFactory(inventory), Some(*args.position)) - .await; - } + }); + } - BlockActionResult::Success - }) + BlockActionResult::Success } - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/signs.rs b/crates/pumpkin/src/block/blocks/signs.rs index 9e3f0b2e2..0deb7ce0d 100644 --- a/crates/pumpkin/src/block/blocks/signs.rs +++ b/crates/pumpkin/src/block/blocks/signs.rs @@ -17,7 +17,6 @@ use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::NormalUseArgs; @@ -294,40 +293,35 @@ impl SignBlock { //TODO: add support for click commands impl BlockBehaviour for SignBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let support = Self::detect_support(args.world, args.position); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let support = Self::detect_support(args.world, args.position); - let Some(placement) = Self::determine_placement(&args, &support) else { - return BlockStateId::AIR; // Invalid placement - }; + let Some(placement) = Self::determine_placement(&args, &support) else { + return BlockStateId::AIR; // Invalid placement + }; - let actual_block = Block::from_id(placement.block_id); - Self::apply_placement_properties(actual_block, &placement) - }) + let actual_block = Block::from_id(placement.block_id); + Self::apply_placement_properties(actual_block, &placement) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if args.block.name.contains("hanging") { - args.world - .add_block_entity(Arc::new(HangingSignBlockEntity::empty(*args.position))); - } else { - args.world - .add_block_entity(Arc::new(SignBlockEntity::empty(*args.position))); - } - }) + fn placed(&self, args: PlacedArgs<'_>) { + if args.block.name.contains("hanging") { + args.world + .add_block_entity(Arc::new(HangingSignBlockEntity::empty(*args.position))); + } else { + args.world + .add_block_entity(Arc::new(SignBlockEntity::empty(*args.position))); + } } - fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - match args.player.client.as_ref() { - crate::net::ClientPlatform::Java(java) => { - java.send_sign_packet(*args.position, true).await; - } - crate::net::ClientPlatform::Bedrock(_bedrock) => {} + fn player_placed(&self, args: PlayerPlacedArgs<'_>) { + let client = args.player.client.clone(); + let pos = *args.position; + tokio::spawn(async move { + if let crate::net::ClientPlatform::Java(java) = client.as_ref() { + java.send_sign_packet(pos, true).await; } - }) + }); } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -373,16 +367,14 @@ impl BlockBehaviour for SignBlock { } } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - args.world.remove_block_entity(args.position); - }) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + args.world.remove_block_entity(args.position); } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { let is_hanging = args.block.name.contains("hanging"); let is_wall_sign = args.block.name.contains("wall"); @@ -398,177 +390,174 @@ impl BlockBehaviour for SignBlock { Some(BlockDirection::Down) }; - Box::pin(async move { - if let Some(dir) = support_dir { - // Only check if the neighbor that changed is our support neighbor - if args.direction == dir { - let support_pos = args.position.offset(dir.to_offset()); - let (support_block, support_state) = - args.world.get_block_and_state(&support_pos); + if let Some(dir) = support_dir { + let support_pos = args.position.offset(dir.to_offset()); + let support_state = args.world.get_block_state(&support_pos); - // Permissive support check - let is_leaf = - support_block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_LEAVES); - let is_sign = support_block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_SIGNS); + let is_leaf = args + .world + .get_block(&support_pos) + .has_tag(&pumpkin_data::tag::Block::MINECRAFT_LEAVES); - let is_valid = match dir { - BlockDirection::Up => { - support_state.is_side_solid(BlockDirection::Down) || is_leaf || is_sign - } - BlockDirection::Down => { - support_state.is_center_solid(BlockDirection::Up) || is_leaf || is_sign - } - _ => support_state.is_side_solid(dir.opposite()) || is_leaf || is_sign, - }; + let is_sign = args + .world + .get_block(&support_pos) + .has_tag(&pumpkin_data::tag::Block::MINECRAFT_ALL_SIGNS); - if !is_valid { - return BlockStateId::AIR; // Return AIR to break the block - } + let is_valid = match dir { + BlockDirection::Up => { + support_state.is_center_solid(BlockDirection::Down) || is_leaf || is_sign } + BlockDirection::Down => { + support_state.is_center_solid(BlockDirection::Up) || is_leaf || is_sign + } + _ => support_state.is_side_solid(dir.opposite()) || is_leaf || is_sign, + }; + + if !is_valid { + return BlockStateId::AIR; } - args.state_id - }) + } + args.state_id } /// Handles normal use (right-click) on the sign block. - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let Some(block_entity) = args.world.get_block_entity(args.position) else { - return BlockActionResult::Pass; - }; - let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { - return BlockActionResult::Pass; - }; + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let Some(block_entity) = args.world.get_block_entity(args.position) else { + return BlockActionResult::Pass; + }; + let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { + return BlockActionResult::Pass; + }; - if sign_entity.is_waxed.load(Ordering::Relaxed) { - args.world.play_block_sound( - pumpkin_data::sound::Sound::BlockSignWaxedInteractFail, - pumpkin_data::sound::SoundCategory::Blocks, - *args.position, - ); - return BlockActionResult::SuccessServer; + if sign_entity.is_waxed.load(Ordering::Relaxed) { + args.world.play_block_sound( + pumpkin_data::sound::Sound::BlockSignWaxedInteractFail, + pumpkin_data::sound::SoundCategory::Blocks, + *args.position, + ); + return BlockActionResult::SuccessServer; + } + + let mut currently_editing = sign_entity + .currently_editing_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !try_claim_sign( + &mut currently_editing, + &args.player.gameprofile.id, + args.world, + args.position, + ) { + return BlockActionResult::Pass; + } + + let is_facing_front_text = + is_facing_front_text(args.world, args.position, args.block, args.player); + let client = args.player.client.clone(); + let pos = *args.position; + tokio::spawn(async move { + if let ClientPlatform::Java(java) = client.as_ref() { + java.send_sign_packet(pos, is_facing_front_text).await; } + }); - let mut currently_editing = sign_entity.currently_editing_player.lock().await; - if !try_claim_sign( - &mut currently_editing, - &args.player.gameprofile.id, - args.world, - args.position, - ) { - return BlockActionResult::Pass; - } - - let is_facing_front_text = - is_facing_front_text(args.world, args.position, args.block, args.player); - match args.player.client.as_ref() { - ClientPlatform::Java(java) => { - java.send_sign_packet(*args.position, is_facing_front_text) - .await; - } - ClientPlatform::Bedrock(_bedrock) => {} - } - - BlockActionResult::SuccessServer - }) + BlockActionResult::SuccessServer } /// Handles use with an item on the sign block. - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let Some(block_entity) = args.world.get_block_entity(args.position) else { - return BlockActionResult::Pass; - }; - let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { - return BlockActionResult::Pass; - }; + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let Some(block_entity) = args.world.get_block_entity(args.position) else { + return BlockActionResult::Pass; + }; + let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { + return BlockActionResult::Pass; + }; - if sign_entity.is_waxed.load(Ordering::Relaxed) { - return BlockActionResult::PassToDefaultBlockAction; - } + if sign_entity.is_waxed.load(Ordering::Relaxed) { + return BlockActionResult::PassToDefaultBlockAction; + } - let mut currently_editing = sign_entity.currently_editing_player.lock().await; - if !try_claim_sign( - &mut currently_editing, - &args.player.gameprofile.id, - args.world, - args.position, - ) { - // I don't think that makes sense, since it will also just return in normal_use, but vanilla does it like this - return BlockActionResult::PassToDefaultBlockAction; - } + let mut currently_editing = sign_entity + .currently_editing_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !try_claim_sign( + &mut currently_editing, + &args.player.gameprofile.id, + args.world, + args.position, + ) { + return BlockActionResult::PassToDefaultBlockAction; + } - let text = if is_facing_front_text(args.world, args.position, args.block, args.player) { - &sign_entity.front_text - } else { - &sign_entity.back_text - }; + let text = if is_facing_front_text(args.world, args.position, args.block, args.player) { + &sign_entity.front_text + } else { + &sign_entity.back_text + }; - let Some(pumpkin_item) = args - .server - .item_registry - .get_pumpkin_item(args.item_stack.item.id) - else { - return BlockActionResult::PassToDefaultBlockAction; - }; + let Some(pumpkin_item) = args + .server + .item_registry + .get_pumpkin_item(args.item_stack.item.id) + else { + return BlockActionResult::PassToDefaultBlockAction; + }; - let result = pumpkin_item + let result = pumpkin_item + .as_any() + .downcast_ref::() + .map_or_else( + || { + pumpkin_item + .as_any() + .downcast_ref::() + .map_or_else( + || { + if let Some(ink_sac_item) = + pumpkin_item.as_any().downcast_ref::() + { + ink_sac_item.apply_to_sign(&args, &block_entity, text) + } else if let Some(dye) = + pumpkin_item.as_any().downcast_ref::() + { + let color_name = args + .item_stack + .item + .registry_key + .strip_suffix("_dye") + .unwrap_or(args.item_stack.item.registry_key); + dye.apply_to_sign(&args, &block_entity, text, color_name) + } else { + BlockActionResult::PassToDefaultBlockAction + } + }, + |g_ink_sac_item| { + g_ink_sac_item.apply_to_sign(&args, &block_entity, text) + }, + ) + }, + |honeycomb_item| honeycomb_item.apply_to_sign(&args, &block_entity, sign_entity), + ); + + if result == BlockActionResult::Success { + if pumpkin_item .as_any() - .downcast_ref::() - .map_or_else( - || { - pumpkin_item - .as_any() - .downcast_ref::() - .map_or_else( - || { - if let Some(ink_sac_item) = - pumpkin_item.as_any().downcast_ref::() - { - ink_sac_item.apply_to_sign(&args, &block_entity, text) - } else if let Some(dye) = - pumpkin_item.as_any().downcast_ref::() - { - let color_name = args - .item_stack - .item - .registry_key - .strip_suffix("_dye") - .unwrap_or(args.item_stack.item.registry_key); - dye.apply_to_sign(&args, &block_entity, text, color_name) - } else { - BlockActionResult::PassToDefaultBlockAction - } - }, - |g_ink_sac_item| { - g_ink_sac_item.apply_to_sign(&args, &block_entity, text) - }, - ) - }, - |honeycomb_item| { - honeycomb_item.apply_to_sign(&args, &block_entity, sign_entity) - }, + .downcast_ref::() + .is_some() + { + args.player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::GlowedSign, ); - - if result == BlockActionResult::Success { - if pumpkin_item - .as_any() - .downcast_ref::() - .is_some() - { - args.player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::GlowedSign).await; - } - if !args.player.has_infinite_materials() { - args.item_stack.decrement(1); - } - *currently_editing = None; } + if !args.player.has_infinite_materials() { + args.item_stack.decrement(1); + } + *currently_editing = None; + } - result - }) + result } } diff --git a/crates/pumpkin/src/block/blocks/skull_block.rs b/crates/pumpkin/src/block/blocks/skull_block.rs index 9f03ed976..c9ad3124a 100644 --- a/crates/pumpkin/src/block/blocks/skull_block.rs +++ b/crates/pumpkin/src/block/blocks/skull_block.rs @@ -1,5 +1,5 @@ use crate::block::blocks::redstone::block_receives_redstone_power; -use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; use crate::entity::EntityBase; use pumpkin_data::BlockId; use pumpkin_data::BlockStateId; @@ -29,37 +29,33 @@ use crate::block::entities::skull::SkullBlockEntity; use std::sync::Arc; impl BlockBehaviour for SkullBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = SkullBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = SkeletonSkullLikeProperties::default(args.block); - props.rotation = args.player.get_entity().get_rotation_16(); - props.powered = block_receives_redstone_power(args.world, args.position).await; - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = SkeletonSkullLikeProperties::default(args.block); + props.rotation = args.player.get_entity().get_rotation_16(); + props.powered = block_receives_redstone_power(args.world, args.position); + props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state = args.world.get_block_state(args.position); let mut props = SkeletonSkullLikeProperties::from_state_id(state.id, args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + let is_receiving_power = block_receives_redstone_power(args.world, args.position); if props.powered != is_receiving_power { props.powered = is_receiving_power; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/slabs.rs b/crates/pumpkin/src/block/blocks/slabs.rs index 9224466cb..8a689abbf 100644 --- a/crates/pumpkin/src/block/blocks/slabs.rs +++ b/crates/pumpkin/src/block/blocks/slabs.rs @@ -5,7 +5,6 @@ use pumpkin_data::block_properties::SlabType; use pumpkin_macros::pumpkin_block_from_tag; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::BlockIsReplacing; use crate::block::CanUpdateAtArgs; use crate::block::OnPlaceArgs; @@ -16,28 +15,26 @@ type SlabProperties = pumpkin_data::block_properties::ResinBrickSlabLikeProperti pub struct SlabBlock; impl BlockBehaviour for SlabBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if let BlockIsReplacing::Itself(state_id) = args.replacing { - let mut slab_props = SlabProperties::from_state_id(state_id, args.block); - slab_props.r#type = SlabType::Double; - slab_props.waterlogged = false; - return slab_props.to_state_id(args.block); - } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if let BlockIsReplacing::Itself(state_id) = args.replacing { + let mut slab_props = SlabProperties::from_state_id(state_id, args.block); + slab_props.r#type = SlabType::Double; + slab_props.waterlogged = false; + return slab_props.to_state_id(args.block); + } - let mut slab_props = SlabProperties::default(args.block); - slab_props.waterlogged = args.replacing.water_source(); - slab_props.r#type = match args.direction { - BlockDirection::Up => SlabType::Top, - BlockDirection::Down => SlabType::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0..0.5 => SlabType::Bottom, - _ => SlabType::Top, - }, - }; + let mut slab_props = SlabProperties::default(args.block); + slab_props.waterlogged = args.replacing.water_source(); + slab_props.r#type = match args.direction { + BlockDirection::Up => SlabType::Top, + BlockDirection::Down => SlabType::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => SlabType::Bottom, + _ => SlabType::Top, + }, + }; - slab_props.to_state_id(args.block) - }) + slab_props.to_state_id(args.block) } fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { diff --git a/crates/pumpkin/src/block/blocks/slime.rs b/crates/pumpkin/src/block/blocks/slime.rs index 52a237668..e9b3253d2 100644 --- a/crates/pumpkin/src/block/blocks/slime.rs +++ b/crates/pumpkin/src/block/blocks/slime.rs @@ -1,28 +1,20 @@ use pumpkin_macros::pumpkin_block; use crate::block::{ - BlockBehaviour, BlockFuture, OnLandedUponArgs, UpdateEntityMovementAfterFallOnArgs, - bounce_entity_after_fall, + BlockBehaviour, OnLandedUponArgs, UpdateEntityMovementAfterFallOnArgs, bounce_entity_after_fall, }; #[pumpkin_block("minecraft:slime_block")] pub struct SlimeBlock; impl BlockBehaviour for SlimeBlock { - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() { - living - .handle_fall_damage(args.entity, args.fall_distance, 0.0) - .await; - } - }) + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() { + living.handle_fall_damage(args.entity, args.fall_distance, 0.0); + } } - fn update_entity_movement_after_fall_on<'a>( - &'a self, - args: UpdateEntityMovementAfterFallOnArgs<'a>, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { bounce_entity_after_fall(args.entity, 1.0) }) + fn update_entity_movement_after_fall_on(&self, args: UpdateEntityMovementAfterFallOnArgs<'_>) { + bounce_entity_after_fall(args.entity, 1.0); } } diff --git a/crates/pumpkin/src/block/blocks/smithing_table.rs b/crates/pumpkin/src/block/blocks/smithing_table.rs index 8b1ebde3e..66153e777 100644 --- a/crates/pumpkin/src/block/blocks/smithing_table.rs +++ b/crates/pumpkin/src/block/blocks/smithing_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; @@ -16,21 +16,21 @@ use tokio::sync::Mutex; pub struct SmithingTableBlock; impl BlockBehaviour for SmithingTableBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithSmithingTable as i32, - 1, - ) - .await; - args.player - .open_handled_screen(&SmithingTableScreenFactory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithSmithingTable as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&SmithingTableScreenFactory, Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/smoker.rs b/crates/pumpkin/src/block/blocks/smoker.rs index 670868ef9..3e5b35926 100644 --- a/crates/pumpkin/src/block/blocks/smoker.rs +++ b/crates/pumpkin/src/block/blocks/smoker.rs @@ -20,8 +20,8 @@ use tokio::sync::Mutex; use crate::{ block::{ - BlockBehaviour, BlockFuture, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, - OnPlaceArgs, PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -81,79 +81,70 @@ impl ScreenHandlerFactory for SmokerScreenFactory { pub struct SmokerBlock; impl BlockBehaviour for SmokerBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithSmoker as i32, - 1, - ) - .await; - let smoker_screen_factory = - SmokerScreenFactory::new(inventory, property_delegate, experience_container); - args.player - .open_handled_screen(&smoker_screen_factory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.clone().get_inventory() + && let Some(property_delegate) = block_entity.clone().to_property_delegate() + && let Some(experience_container) = block_entity.to_experience_container() + { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithSmoker as i32, + 1, + ); + let smoker_screen_factory = + SmokerScreenFactory::new(inventory, property_delegate, experience_container); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&smoker_screen_factory, Some(pos)) .await; + }); + } + crate::block::registry::BlockActionResult::Consume + } + + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = FurnaceLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + + props.to_state_id(args.block) + } + + fn placed(&self, args: PlacedArgs<'_>) { + let smoker_block_entity = SmokerBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(smoker_block_entity)); + } + + fn broken(&self, args: BrokenArgs<'_>) { + // Extract and drop accumulated XP as orbs before removing the block entity + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(experience_container) = block_entity.to_experience_container() + { + let xp = experience_container.extract_experience(); + if xp > 0 { + let pos = args.position.to_f64(); + ExperienceOrbEntity::spawn(args.world, pos, xp as u32); } - crate::block::registry::BlockActionResult::Consume - }) + } + args.world.remove_block_entity(args.position); } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = FurnaceLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - - props.to_state_id(args.block) - }) - } - - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let smoker_block_entity = SmokerBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(smoker_block_entity)); - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Extract and drop accumulated XP as orbs before removing the block entity - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(experience_container) = block_entity.to_experience_container() - { - let xp = experience_container.extract_experience(); - if xp > 0 { - let pos = args.position.to_f64(); - ExperienceOrbEntity::spawn(args.world, pos, xp as u32).await; - } - } - args.world.remove_block_entity(args.position); - }) - } - - fn get_comparator_output<'a>( - &'a self, - args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { - Some(crate::block::calculate_comparator_output(inventory.as_ref()).await) - } else { - None - } - }) + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(inventory) = block_entity.get_inventory() + { + Some(crate::block::calculate_comparator_output( + inventory.as_ref(), + )) + } else { + None + } } } diff --git a/crates/pumpkin/src/block/blocks/sniffer_egg.rs b/crates/pumpkin/src/block/blocks/sniffer_egg.rs index 34653c9ff..9c1a98890 100644 --- a/crates/pumpkin/src/block/blocks/sniffer_egg.rs +++ b/crates/pumpkin/src/block/blocks/sniffer_egg.rs @@ -7,9 +7,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; -use crate::block::{ - BlockBehaviour, BlockFuture, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, -}; +use crate::block::{BlockBehaviour, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs}; #[pumpkin_block("minecraft:sniffer_egg")] pub struct SnifferEggBlock; @@ -31,15 +29,13 @@ impl SnifferEggBlock { } impl BlockBehaviour for SnifferEggBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = SnifferEggLikeProperties::default(args.block); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let props = SnifferEggLikeProperties::default(args.block); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { args.world.play_sound( Sound::BlockSnifferEggPlop, SoundCategory::Blocks, @@ -50,62 +46,52 @@ impl BlockBehaviour for SnifferEggBlock { let delay = Self::get_hatch_delay(on_moss); args.world .schedule_block_tick(args.block, *args.position, delay, TickPriority::Normal); - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let mut props = SnifferEggLikeProperties::from_state_id(state_id, args.block); + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let mut props = SnifferEggLikeProperties::from_state_id(state_id, args.block); - if props.hatch < 2 { - props.hatch += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + if props.hatch < 2 { + props.hatch += 1; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); - args.world.play_sound( - Sound::BlockSnifferEggCrack, - SoundCategory::Blocks, - &args.position.to_f64(), - ); + args.world.play_sound( + Sound::BlockSnifferEggCrack, + SoundCategory::Blocks, + &args.position.to_f64(), + ); - let on_moss = Self::is_on_moss(args.world.as_ref(), args.position); - let delay = Self::get_hatch_delay(on_moss); - args.world.schedule_block_tick( - args.block, - *args.position, - delay, - TickPriority::Normal, - ); - } else { - args.world - .break_block(args.position, None, BlockFlags::SKIP_DROPS) - .await; + let on_moss = Self::is_on_moss(args.world.as_ref(), args.position); + let delay = Self::get_hatch_delay(on_moss); + args.world + .schedule_block_tick(args.block, *args.position, delay, TickPriority::Normal); + } else { + args.world + .break_block(args.position, None, BlockFlags::SKIP_DROPS); - args.world.play_sound( - Sound::BlockSnifferEggHatch, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } - }) + args.world.play_sound( + Sound::BlockSnifferEggHatch, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { args.world.play_sound( Sound::BlockSnifferEggCrack, SoundCategory::Blocks, &args.position.to_f64(), ); args.world - .drop_stack(args.position, ItemStack::new(1, &Item::SNIFFER_EGG)) - .await; - }) + .drop_stack(args.position, ItemStack::new(1, &Item::SNIFFER_EGG)); + } } } diff --git a/crates/pumpkin/src/block/blocks/snow.rs b/crates/pumpkin/src/block/blocks/snow.rs index 108a67c9c..42f930c38 100644 --- a/crates/pumpkin/src/block/blocks/snow.rs +++ b/crates/pumpkin/src/block/blocks/snow.rs @@ -9,7 +9,7 @@ use pumpkin_world::{ }; use crate::block::{ - BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, + BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs, UseWithItemArgs, registry::BlockActionResult, }; @@ -17,22 +17,17 @@ use crate::block::{ pub struct LayeredSnowBlock; impl BlockBehaviour for LayeredSnowBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - return Block::AIR.default_state.id; - } - let mut props = SnowLikeProperties::default(args.block); - props.layers = 1; - props.to_state_id(&Block::SNOW) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if !can_place_at(args.world, args.position) { + return Block::AIR.default_state.id; + } + let mut props = SnowLikeProperties::default(args.block); + props.layers = 1; + props.to_state_id(&Block::SNOW) } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { let item = args.item_stack.item; if item == &Item::SNOW { @@ -52,60 +47,49 @@ impl BlockBehaviour for LayeredSnowBlock { let mut props = SnowLikeProperties::from_state_id(state_id, &Block::SNOW); if props.layers >= 8 { - args.world - .set_block_state( - pos, - Block::SNOW_BLOCK.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + pos, + Block::SNOW_BLOCK.default_state.id, + BlockFlags::NOTIFY_ALL, + ); return BlockActionResult::Success; } props.layers += 1; let state_id = props.to_state_id(&Block::SNOW); args.world - .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL); return BlockActionResult::Success; } BlockActionResult::Pass - }) + } } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Snow layers melt when lit by block light above level 11, - // e.g. from a nearby torch. - if args.world.get_block_light_level(args.position).unwrap_or(0) > 11 { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + // Snow layers melt when lit by block light above level 11, + // e.g. from a nearby torch. + if args.world.get_block_light_level(args.position).unwrap_or(0) > 11 { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/spawner.rs b/crates/pumpkin/src/block/blocks/spawner.rs index 169678d0b..407ce7fc5 100644 --- a/crates/pumpkin/src/block/blocks/spawner.rs +++ b/crates/pumpkin/src/block/blocks/spawner.rs @@ -5,33 +5,29 @@ use crate::entity::experience_orb::ExperienceOrbEntity; use pumpkin_macros::pumpkin_block; use pumpkin_util::GameMode; -use crate::block::{BlockBehaviour, BlockFuture, BrokenArgs, OnSyncedBlockEventArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, BrokenArgs, OnSyncedBlockEventArgs, PlacedArgs}; #[pumpkin_block("minecraft:spawner")] pub struct SpawnerBlock; impl BlockBehaviour for SpawnerBlock { - fn on_synced_block_event<'a>( - &'a self, - _args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { true }) + fn on_synced_block_event(&self, _args: OnSyncedBlockEventArgs<'_>) -> bool { + true } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let spawner_block_entity = MobSpawnerBlockEntity::new(*args.position, None); args.world.add_block_entity(Arc::new(spawner_block_entity)); - }) + } } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { if args.player.gamemode.load() != GameMode::Creative { let xp_count = 15 + rand::random_range(0..15) + rand::random_range(0..15); - ExperienceOrbEntity::spawn(args.world, args.position.to_centered_f64(), xp_count) - .await; + ExperienceOrbEntity::spawn(args.world, args.position.to_centered_f64(), xp_count); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/sponge.rs b/crates/pumpkin/src/block/blocks/sponge.rs index fb92053a3..8d7f6012a 100644 --- a/crates/pumpkin/src/block/blocks/sponge.rs +++ b/crates/pumpkin/src/block/blocks/sponge.rs @@ -3,7 +3,7 @@ use pumpkin_util::math::vector3::Vector3; use std::collections::{HashSet, VecDeque}; use std::sync::Arc; -use crate::block::{BlockBehaviour, BlockFuture, OnNeighborUpdateArgs, PlacedArgs}; +use crate::block::{BlockBehaviour, OnNeighborUpdateArgs, PlacedArgs}; use pumpkin_data::dimension::Dimension; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; @@ -15,7 +15,7 @@ use pumpkin_world::world::BlockFlags; pub struct SpongeBlock; impl SpongeBlock { - pub async fn absorb_water(world: &Arc, position: &BlockPos) -> bool { + pub fn absorb_water(world: &Arc, position: &BlockPos) -> bool { let mut water_blocks = Vec::new(); let mut visited = HashSet::new(); let mut queue = VecDeque::new(); @@ -70,24 +70,20 @@ impl SpongeBlock { let mut event = crate::plugin::api::events::block::sponge_absorb::SpongeAbsorbEvent::new(*position); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return false; } for water_pos in &water_blocks { - world - .set_block_state(water_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(water_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); } - world - .set_block_state( - position, - Block::WET_SPONGE.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + position, + Block::WET_SPONGE.default_state.id, + BlockFlags::NOTIFY_ALL, + ); world.play_block_sound(Sound::BlockSpongeAbsorb, SoundCategory::Blocks, *position); @@ -97,20 +93,16 @@ impl SpongeBlock { } impl BlockBehaviour for SpongeBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Attempt to absorb water on placement - Self::absorb_water(args.world, args.position).await; - }) + fn placed(&self, args: PlacedArgs<'_>) { + // Attempt to absorb water on placement + Self::absorb_water(args.world, args.position); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - // If a neighboring block changed and it's water, attempt to absorb. - if args.source_block.id == Block::WATER.id { - Self::absorb_water(args.world, args.position).await; - } - }) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // If a neighboring block changed and it's water, attempt to absorb. + if args.source_block.id == Block::WATER.id { + Self::absorb_water(args.world, args.position); + } } } @@ -118,17 +110,15 @@ impl BlockBehaviour for SpongeBlock { pub struct WetSpongeBlock; impl BlockBehaviour for WetSpongeBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { // Check if placed in Nether, if so, dry out if args.world.dimension == Dimension::THE_NETHER { - args.world - .set_block_state( - args.position, - Block::SPONGE.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + Block::SPONGE.default_state.id, + BlockFlags::NOTIFY_ALL, + ); // Play dry sound and spawn smoke particles args.world.play_block_sound( @@ -149,6 +139,6 @@ impl BlockBehaviour for WetSpongeBlock { Particle::Cloud, ); } - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/stairs.rs b/crates/pumpkin/src/block/blocks/stairs.rs index 2165d205f..46f209e2b 100644 --- a/crates/pumpkin/src/block/blocks/stairs.rs +++ b/crates/pumpkin/src/block/blocks/stairs.rs @@ -10,7 +10,6 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; -use crate::block::BlockFuture; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; use crate::world::World; @@ -21,37 +20,35 @@ type StairsProperties = pumpkin_data::block_properties::OakStairsLikeProperties; pub struct StairBlock; impl BlockBehaviour for StairBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut stair_props = StairsProperties::default(args.block); - stair_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut stair_props = StairsProperties::default(args.block); + stair_props.waterlogged = args.replacing.water_source(); - stair_props.facing = args.player.get_entity().get_horizontal_facing(); - stair_props.half = match args.direction { - BlockDirection::Up => Half::Top, - BlockDirection::Down => Half::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0..0.5 => Half::Bottom, - 0.5..1.0 => Half::Top, + stair_props.facing = args.player.get_entity().get_horizontal_facing(); + stair_props.half = match args.direction { + BlockDirection::Up => Half::Top, + BlockDirection::Down => Half::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => Half::Bottom, + 0.5..1.0 => Half::Top, - // This cannot happen normally - _ => Half::Bottom, - }, - }; + // This cannot happen normally + _ => Half::Bottom, + }, + }; - stair_props.shape = compute_stair_shape( - args.world, - args.position, - stair_props.facing, - stair_props.half, - ); + stair_props.shape = compute_stair_shape( + args.world, + args.position, + stair_props.facing, + stair_props.half, + ); - stair_props.to_state_id(args.block) - }) + stair_props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let state_id = args.world.get_block_state_id(args.position); let mut stair_props = StairsProperties::from_state_id(state_id, args.block); @@ -64,15 +61,13 @@ impl BlockBehaviour for StairBlock { if stair_props.shape != new_shape { stair_props.shape = new_shape; - args.world - .set_block_state( - args.position, - stair_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + stair_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } - }) + } } fn rotate( diff --git a/crates/pumpkin/src/block/blocks/stonecutter.rs b/crates/pumpkin/src/block/blocks/stonecutter.rs index 61c2ecdb7..01f6ba246 100644 --- a/crates/pumpkin/src/block/blocks/stonecutter.rs +++ b/crates/pumpkin/src/block/blocks/stonecutter.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; @@ -17,21 +17,21 @@ use pumpkin_inventory::stonecutter_screen_handler::StonecutterScreenHandler; pub struct StonecutterBlock; impl BlockBehaviour for StonecutterBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - args.player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithStonecutter as i32, - 1, - ) - .await; - args.player - .open_handled_screen(&StonecutterScreenFactory, Some(*args.position)) + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithStonecutter as i32, + 1, + ); + let player = Arc::clone(args.player); + let pos = *args.position; + tokio::spawn(async move { + player + .open_handled_screen(&StonecutterScreenFactory, Some(pos)) .await; + }); - BlockActionResult::Success - }) + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/structure_block.rs b/crates/pumpkin/src/block/blocks/structure_block.rs index e30fa607d..e06c0c2cb 100644 --- a/crates/pumpkin/src/block/blocks/structure_block.rs +++ b/crates/pumpkin/src/block/blocks/structure_block.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs}; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, StructureBlockLikeProperties}; @@ -10,15 +10,13 @@ use pumpkin_util::PermissionLvl; pub struct StructureBlock; impl BlockBehaviour for StructureBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let props = StructureBlockLikeProperties::default(args.block); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let props = StructureBlockLikeProperties::default(args.block); + props.to_state_id(args.block) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { if args.player.permission_lvl.load() < PermissionLvl::Two { return BlockActionResult::Pass; } @@ -28,6 +26,6 @@ impl BlockBehaviour for StructureBlock { args.world.update_block_entity(&block_entity); BlockActionResult::Success - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/tnt.rs b/crates/pumpkin/src/block/blocks/tnt.rs index e06b6b642..7e699d5fa 100644 --- a/crates/pumpkin/src/block/blocks/tnt.rs +++ b/crates/pumpkin/src/block/blocks/tnt.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, ExplodeArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, + BlockBehaviour, ExplodeArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, }; use crate::entity::Entity; use crate::entity::tnt::TNTEntity; @@ -23,13 +23,13 @@ use super::redstone::block_receives_redstone_power; pub struct TNTBlock; impl TNTBlock { - pub async fn prime(world: &Arc, location: &BlockPos) { + pub fn prime(world: &Arc, location: &BlockPos) { let mut event = crate::plugin::api::events::block::tnt_prime::TNTPrimeEvent::new( *location, "REDSTONE".to_string(), ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; @@ -43,7 +43,9 @@ impl TNTBlock { false, ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut prime_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut prime_event); } if prime_event.cancelled { return; @@ -51,15 +53,13 @@ impl TNTBlock { let pos = entity.pos.load(); let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, DEFAULT_FUSE)); - world.spawn_entity(tnt).await; + world.spawn_entity(tnt); world.play_sound( pumpkin_data::sound::Sound::EntityTntPrimed, SoundCategory::Blocks, &pos, ); - world - .set_block_state(location, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(location, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); } } @@ -67,47 +67,44 @@ const DEFAULT_FUSE: u32 = 80; const DEFAULT_POWER: f32 = 4.0; impl BlockBehaviour for TNTBlock { - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { let item = args.item_stack.item; if item != &Item::FLINT_AND_STEEL || item == &Item::FIRE_CHARGE { return BlockActionResult::Pass; } let world = args.player.world(); - Self::prime(&world, args.position).await; + Self::prime(&world, args.position); BlockActionResult::Consume - }) + } } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if block_receives_redstone_power(args.world, args.position).await { - Self::prime(args.world, args.position).await; + fn placed(&self, args: PlacedArgs<'_>) { + { + if block_receives_redstone_power(args.world, args.position) { + Self::prime(args.world, args.position); } - }) + } } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if block_receives_redstone_power(args.world, args.position).await { - Self::prime(args.world, args.position).await; + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { + if block_receives_redstone_power(args.world, args.position) { + Self::prime(args.world, args.position); } - }) + } } - fn explode<'a>(&'a self, args: ExplodeArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn explode(&self, args: ExplodeArgs<'_>) { + { let entity = Entity::new(args.world.clone(), args.position.to_f64(), &EntityType::TNT); let angle = rand::random::() * std::f64::consts::TAU; entity.set_velocity(Vector3::new(-angle.sin() * 0.02, 0.2, -angle.cos() * 0.02)); let fuse = rand::rng().random_range(0..DEFAULT_FUSE / 4) + DEFAULT_FUSE / 8; let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, fuse)); - args.world.spawn_entity(tnt).await; - }) + args.world.spawn_entity(tnt); + } } fn should_drop_items_on_explosion(&self) -> bool { diff --git a/crates/pumpkin/src/block/blocks/torches.rs b/crates/pumpkin/src/block/blocks/torches.rs index 846241176..130445575 100644 --- a/crates/pumpkin/src/block/blocks/torches.rs +++ b/crates/pumpkin/src/block/blocks/torches.rs @@ -1,4 +1,4 @@ -use crate::block::{BlockFuture, BlockIsReplacing}; +use crate::block::BlockIsReplacing; use crate::entity::EntityBase; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, Facing}; @@ -31,63 +31,61 @@ impl BlockMetadata for TorchBlock { } impl BlockBehaviour for TorchBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return args.block.default_state.id; - } - } - let mut directions = args.player.get_entity().get_entity_facing_order(); - - if args.replacing == BlockIsReplacing::None { - let face = args.direction.to_facing(); - let mut i = 0; - while i < directions.len() && directions[i] != face { - i += 1; - } - - if i > 0 { - directions.copy_within(0..i, 1); - directions[0] = face; - } - } else if directions[0] == Facing::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if support_block.is_center_solid(BlockDirection::Up) { - return args.block.default_state.id; - } - } - - for dir in directions { - if dir != Facing::Up - && dir != Facing::Down - && can_place_at(args.world, args.position, dir.to_block_direction()) - { - let wall_block = { - if args.block == &Block::TORCH { - Block::WALL_TORCH - } else if args.block == &Block::SOUL_TORCH { - Block::SOUL_WALL_TORCH - } else { - Block::COPPER_WALL_TORCH - } - }; - let mut torch_props = WallTorchProps::default(&wall_block); - if let Some(facing) = dir.opposite().to_horizontal_facing() { - torch_props.facing = facing; - return torch_props.to_state_id(&wall_block); - } - } - } - + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.direction == BlockDirection::Down { let support_block = args.world.get_block_state(&args.position.down()); if support_block.is_center_solid(BlockDirection::Up) { - args.block.default_state.id - } else { - BlockStateId::AIR + return args.block.default_state.id; } - }) + } + let mut directions = args.player.get_entity().get_entity_facing_order(); + + if args.replacing == BlockIsReplacing::None { + let face = args.direction.to_facing(); + let mut i = 0; + while i < directions.len() && directions[i] != face { + i += 1; + } + + if i > 0 { + directions.copy_within(0..i, 1); + directions[0] = face; + } + } else if directions[0] == Facing::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if support_block.is_center_solid(BlockDirection::Up) { + return args.block.default_state.id; + } + } + + for dir in directions { + if dir != Facing::Up + && dir != Facing::Down + && can_place_at(args.world, args.position, dir.to_block_direction()) + { + let wall_block = { + if args.block == &Block::TORCH { + Block::WALL_TORCH + } else if args.block == &Block::SOUL_TORCH { + Block::SOUL_WALL_TORCH + } else { + Block::COPPER_WALL_TORCH + } + }; + let mut torch_props = WallTorchProps::default(&wall_block); + if let Some(facing) = dir.opposite().to_horizontal_facing() { + torch_props.facing = facing; + return torch_props.to_state_id(&wall_block); + } + } + } + + let support_block = args.world.get_block_state(&args.position.down()); + if support_block.is_center_solid(BlockDirection::Up) { + args.block.default_state.id + } else { + BlockStateId::AIR + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -103,33 +101,31 @@ impl BlockBehaviour for TorchBlock { false } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.block == &Block::WALL_TORCH - || args.block == &Block::SOUL_WALL_TORCH - || args.block == &Block::COPPER_WALL_TORCH + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.block == &Block::WALL_TORCH + || args.block == &Block::SOUL_WALL_TORCH + || args.block == &Block::COPPER_WALL_TORCH + { + let props = WallTorchProps::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_at( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) { - let props = WallTorchProps::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) - { - return BlockStateId::AIR; - } - } else if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()); - if !support_block.is_center_solid(BlockDirection::Up) { - return BlockStateId::AIR; - } + return BlockStateId::AIR; } - args.state_id - }) + } else if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()); + if !support_block.is_center_solid(BlockDirection::Up) { + return BlockStateId::AIR; + } + } + args.state_id } } diff --git a/crates/pumpkin/src/block/blocks/trapdoor.rs b/crates/pumpkin/src/block/blocks/trapdoor.rs index d408c5de1..caab25739 100644 --- a/crates/pumpkin/src/block/blocks/trapdoor.rs +++ b/crates/pumpkin/src/block/blocks/trapdoor.rs @@ -1,6 +1,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs}; use crate::entity::EntityBase; use crate::entity::player::Player; use crate::world::World; @@ -17,7 +17,7 @@ use std::sync::Arc; type TrapDoorProperties = pumpkin_data::block_properties::OakTrapdoorLikeProperties; -async fn toggle_trapdoor(player: &Player, world: &Arc, block_pos: &BlockPos) { +fn toggle_trapdoor(player: &Player, world: &Arc, block_pos: &BlockPos) { let (block, block_state) = world.get_block_and_state_id(block_pos); let mut trapdoor_props = TrapDoorProperties::from_state_id(block_state, block); trapdoor_props.open = !trapdoor_props.open; @@ -29,13 +29,11 @@ async fn toggle_trapdoor(player: &Player, world: &Arc, block_pos: &BlockP *block_pos, ); - world - .set_block_state( - block_pos, - trapdoor_props.to_state_id(block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + block_pos, + trapdoor_props.to_state_id(block), + BlockFlags::NOTIFY_LISTENERS, + ); } fn can_open_trapdoor(block: &Block) -> bool { @@ -67,56 +65,54 @@ fn get_sound(block: &Block, open: bool) -> Sound { pub struct TrapDoorBlock; impl BlockBehaviour for TrapDoorBlock { - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + { if !can_open_trapdoor(args.block) { return BlockActionResult::Pass; } - toggle_trapdoor(args.player, args.world, args.position).await; + toggle_trapdoor(args.player, args.world, args.position); BlockActionResult::Success - }) + } } - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut trapdoor_props = TrapDoorProperties::default(args.block); - trapdoor_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut trapdoor_props = TrapDoorProperties::default(args.block); + trapdoor_props.waterlogged = args.replacing.water_source(); - let powered = block_receives_redstone_power(args.world, args.position).await; + let powered = block_receives_redstone_power(args.world, args.position); - let player_facing = args.player.get_entity().get_horizontal_facing(); + let player_facing = args.player.get_entity().get_horizontal_facing(); - // Correct facing logic using Option unwrap - let facing = args - .direction - .to_horizontal_facing() - .unwrap_or(player_facing); + // Correct facing logic using Option unwrap + let facing = args + .direction + .to_horizontal_facing() + .unwrap_or(player_facing); - trapdoor_props.facing = facing; + trapdoor_props.facing = facing; - trapdoor_props.half = match args.direction { - BlockDirection::Up => Half::Top, - BlockDirection::Down => Half::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0..0.5 => Half::Bottom, - _ => Half::Top, - }, - }; + trapdoor_props.half = match args.direction { + BlockDirection::Up => Half::Top, + BlockDirection::Down => Half::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => Half::Bottom, + _ => Half::Top, + }, + }; - trapdoor_props.powered = powered; - trapdoor_props.open = powered; + trapdoor_props.powered = powered; + trapdoor_props.open = powered; - trapdoor_props.to_state_id(args.block) - }) + trapdoor_props.to_state_id(args.block) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + { let block_state = args.world.get_block_state(args.position); let mut trapdoor_props = TrapDoorProperties::from_state_id(block_state.id, args.block); - let powered = block_receives_redstone_power(args.world, args.position).await; + let powered = block_receives_redstone_power(args.world, args.position); if powered != trapdoor_props.powered { trapdoor_props.powered = !trapdoor_props.powered; @@ -132,13 +128,11 @@ impl BlockBehaviour for TrapDoorBlock { } } - args.world - .set_block_state( - args.position, - trapdoor_props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - }) + args.world.set_block_state( + args.position, + trapdoor_props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ); + } } } diff --git a/crates/pumpkin/src/block/blocks/trial_spawner.rs b/crates/pumpkin/src/block/blocks/trial_spawner.rs index 66ade631b..89e288a3c 100644 --- a/crates/pumpkin/src/block/blocks/trial_spawner.rs +++ b/crates/pumpkin/src/block/blocks/trial_spawner.rs @@ -11,123 +11,105 @@ use pumpkin_world::world::BlockFlags; use crate::block::entities::trial_spawner::TrialSpawnerBlockEntity; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, PlacedArgs, UseWithItemArgs}; +use crate::block::{BlockBehaviour, NormalUseArgs, PlacedArgs, UseWithItemArgs}; #[pumpkin_block("minecraft:trial_spawner")] pub struct TrialSpawnerBlock; impl BlockBehaviour for TrialSpawnerBlock { - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let entity = TrialSpawnerBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(entity)); - }) + fn placed(&self, args: PlacedArgs<'_>) { + let entity = TrialSpawnerBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let mut props = TrialSpawnerLikeProperties::from_state_id(state_id, args.block); + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state_id(args.position); + let mut props = TrialSpawnerLikeProperties::from_state_id(state_id, args.block); - match props.trial_spawner_state { - TrialSpawnerState::Inactive | TrialSpawnerState::WaitingForPlayers => { - props.trial_spawner_state = TrialSpawnerState::Active; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + match props.trial_spawner_state { + TrialSpawnerState::Inactive | TrialSpawnerState::WaitingForPlayers => { + props.trial_spawner_state = TrialSpawnerState::Active; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); - args.world.play_sound( - Sound::BlockTrialSpawnerDetectPlayer, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.world.play_sound( - Sound::BlockTrialSpawnerOpenShutter, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } - TrialSpawnerState::Active => { - // Eject trial rewards & key drop upon trial wave completion - props.trial_spawner_state = TrialSpawnerState::WaitingForRewardEjection; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - args.world.play_sound( - Sound::BlockTrialSpawnerSpawnItemBegin, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.world.play_sound( - Sound::BlockTrialSpawnerEjectItem, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - - let key_stack = ItemStack::new(1, &Item::TRIAL_KEY); - args.world.drop_stack(args.position, key_stack).await; - - props.trial_spawner_state = TrialSpawnerState::Cooldown; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - args.world.play_sound( - Sound::BlockTrialSpawnerCloseShutter, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } - TrialSpawnerState::Cooldown => { - args.world.play_sound( - Sound::BlockTrialSpawnerAmbient, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } - TrialSpawnerState::WaitingForRewardEjection | TrialSpawnerState::EjectingReward => { - props.trial_spawner_state = TrialSpawnerState::Cooldown; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } + args.world.play_sound( + Sound::BlockTrialSpawnerDetectPlayer, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world.play_sound( + Sound::BlockTrialSpawnerOpenShutter, + SoundCategory::Blocks, + &args.position.to_f64(), + ); } + TrialSpawnerState::Active => { + // Eject trial rewards & key drop upon trial wave completion + props.trial_spawner_state = TrialSpawnerState::WaitingForRewardEjection; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); - BlockActionResult::Success - }) + args.world.play_sound( + Sound::BlockTrialSpawnerSpawnItemBegin, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world.play_sound( + Sound::BlockTrialSpawnerEjectItem, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + + let key_stack = ItemStack::new(1, &Item::TRIAL_KEY); + args.world.drop_stack(args.position, key_stack); + + props.trial_spawner_state = TrialSpawnerState::Cooldown; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + + args.world.play_sound( + Sound::BlockTrialSpawnerCloseShutter, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } + TrialSpawnerState::Cooldown => { + args.world.play_sound( + Sound::BlockTrialSpawnerAmbient, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } + TrialSpawnerState::WaitingForRewardEjection | TrialSpawnerState::EjectingReward => { + props.trial_spawner_state = TrialSpawnerState::Cooldown; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + } + + BlockActionResult::Success } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - self.normal_use(NormalUseArgs { - server: args.server, - world: args.world, - block: args.block, - position: args.position, - player: args.player, - hit: args.hit, - }) - .await + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + self.normal_use(NormalUseArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + hit: args.hit, }) } } diff --git a/crates/pumpkin/src/block/blocks/turtle_egg.rs b/crates/pumpkin/src/block/blocks/turtle_egg.rs index df86b7708..e4a955626 100644 --- a/crates/pumpkin/src/block/blocks/turtle_egg.rs +++ b/crates/pumpkin/src/block/blocks/turtle_egg.rs @@ -13,7 +13,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use uuid::Uuid; use crate::block::{ - BlockBehaviour, BlockFuture, BlockIsReplacing, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, + BlockBehaviour, BlockIsReplacing, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnEntityStepArgs, OnLandedUponArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs, }; @@ -52,7 +52,7 @@ impl TurtleEggBlock { world.level_info.load().game_rules.mob_griefing } - pub async fn decrease_eggs( + pub fn decrease_eggs( world: &Arc, pos: &BlockPos, state_id: BlockStateId, @@ -66,17 +66,15 @@ impl TurtleEggBlock { let props = TurtleEggProperties::from_state_id(state_id, block); if props.eggs <= 1 { - world.break_block(pos, None, BlockFlags::empty()).await; + world.break_block(pos, None, BlockFlags::empty()); } else { let mut new_props = props; new_props.eggs -= 1; - world - .set_block_state(pos, new_props.to_state_id(block), BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, new_props.to_state_id(block), BlockFlags::NOTIFY_ALL); } } - pub async fn destroy_egg( + pub fn destroy_egg( world: &Arc, pos: &BlockPos, state_id: BlockStateId, @@ -87,27 +85,25 @@ impl TurtleEggBlock { if Self::can_destroy_egg(world, entity) && (randomness <= 1 || rand::random::().is_multiple_of(randomness)) { - Self::decrease_eggs(world, pos, state_id, block).await; + Self::decrease_eggs(world, pos, state_id, block); } } } impl BlockBehaviour for TurtleEggBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.player.get_entity().pose.load() != EntityPose::Crouching - && let BlockIsReplacing::Itself(state_id) = args.replacing - { - let mut properties = TurtleEggProperties::from_state_id(state_id, args.block); - if properties.eggs < 4 { - properties.eggs += 1; - } - return properties.to_state_id(args.block); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + if args.player.get_entity().pose.load() != EntityPose::Crouching + && let BlockIsReplacing::Itself(state_id) = args.replacing + { + let mut properties = TurtleEggProperties::from_state_id(state_id, args.block); + if properties.eggs < 4 { + properties.eggs += 1; } + return properties.to_state_id(args.block); + } - let properties = TurtleEggProperties::default(args.block); - properties.to_state_id(args.block) - }) + let properties = TurtleEggProperties::default(args.block); + properties.to_state_id(args.block) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -121,128 +117,111 @@ impl BlockBehaviour for TurtleEggBlock { && args.block.id == b.id } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if !can_place_at(args.world, args.position) { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); - } - args.state_id - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if !can_place_at(args.world, args.position) { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); + } + args.state_id } - fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !can_place_at(args.world.as_ref(), args.position) { - args.world - .break_block(args.position, None, BlockFlags::empty()) - .await; - } - }) + fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + if !can_place_at(args.world.as_ref(), args.position) { + args.world + .break_block(args.position, None, BlockFlags::empty()); + } } - fn on_entity_step<'a>(&'a self, args: OnEntityStepArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !args.entity.get_entity().is_sneaking() { - Self::destroy_egg( - args.world, - args.position, - args.state.id, - args.block, - args.entity, - 100, - ) - .await; - } - }) + fn on_entity_step(&self, args: OnEntityStepArgs<'_>) { + if !args.entity.get_entity().is_sneaking() { + Self::destroy_egg( + args.world, + args.position, + args.state.id, + args.block, + args.entity, + 100, + ); + } } - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() { - living - .handle_fall_damage(args.entity, args.fall_distance, 1.0) - .await; - } + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() { + living.handle_fall_damage(args.entity, args.fall_distance, 1.0); + } - if args.entity.get_entity().entity_type.id != EntityType::ZOMBIE.id { - let entity_pos = args.entity.get_entity().pos.load(); - let pos = BlockPos(Vector3::new( - entity_pos.x.floor() as i32, - entity_pos.y.floor() as i32, - entity_pos.z.floor() as i32, - )); - let (block, state) = args.world.get_block_and_state(&pos); - if block == &Block::TURTLE_EGG { - Self::destroy_egg(args.world, &pos, state.id, block, args.entity, 3).await; + if args.entity.get_entity().entity_type.id != EntityType::ZOMBIE.id { + let entity_pos = args.entity.get_entity().pos.load(); + let pos = BlockPos(Vector3::new( + entity_pos.x.floor() as i32, + entity_pos.y.floor() as i32, + entity_pos.z.floor() as i32, + )); + let (block, state) = args.world.get_block_and_state(&pos); + if block == &Block::TURTLE_EGG { + Self::destroy_egg(args.world, &pos, state.id, block, args.entity, 3); + } + } + } + + fn random_tick(&self, args: RandomTickArgs<'_>) { + if !Self::on_sand(args.world.as_ref(), args.position) { + return; + } + + let state_id = args.world.get_block_state_id(args.position); + let mut props = TurtleEggProperties::from_state_id(state_id, args.block); + + if props.hatch < 2 { + props.hatch += 1; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + + args.world.play_sound( + Sound::EntityTurtleEggCrack, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } else { + args.world + .break_block(args.position, None, BlockFlags::SKIP_DROPS); + + args.world.play_sound( + Sound::EntityTurtleEggHatch, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + + if args.world.level_info.load().game_rules.spawn_mobs { + for i in 0..props.eggs { + let spawn_pos = Vector3::new( + args.position.0.x as f64 + 0.3 + f64::from(i) * 0.2, + args.position.0.y as f64, + args.position.0.z as f64 + 0.3, + ); + let turtle = + from_type(&EntityType::TURTLE, spawn_pos, args.world, Uuid::new_v4()); + turtle.get_entity().set_age(-24000); + args.world.spawn_entity_non_save(turtle); } } - }) + } } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !Self::on_sand(args.world.as_ref(), args.position) { - return; - } - - let state_id = args.world.get_block_state_id(args.position); - let mut props = TurtleEggProperties::from_state_id(state_id, args.block); - - if props.hatch < 2 { - props.hatch += 1; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - args.world.play_sound( - Sound::EntityTurtleEggCrack, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - } else { - args.world - .break_block(args.position, None, BlockFlags::SKIP_DROPS) - .await; - - args.world.play_sound( - Sound::EntityTurtleEggHatch, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - - if args.world.level_info.load().game_rules.spawn_mobs { - for i in 0..props.eggs { - let spawn_pos = Vector3::new( - args.position.0.x as f64 + 0.3 + f64::from(i) * 0.2, - args.position.0.y as f64, - args.position.0.z as f64 + 0.3, - ); - let turtle = - from_type(&EntityType::TURTLE, spawn_pos, args.world, Uuid::new_v4()); - turtle.get_entity().set_age(-24000); - args.world.spawn_entity(turtle).await; - } - } - } - }) - } - - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn broken(&self, args: BrokenArgs<'_>) { + { args.world.play_sound( Sound::EntityTurtleEggBreak, SoundCategory::Blocks, &args.position.to_f64(), ); - }) + } } } diff --git a/crates/pumpkin/src/block/blocks/vault.rs b/crates/pumpkin/src/block/blocks/vault.rs index 293a600a8..cc5f705f1 100644 --- a/crates/pumpkin/src/block/blocks/vault.rs +++ b/crates/pumpkin/src/block/blocks/vault.rs @@ -12,134 +12,118 @@ use pumpkin_world::world::BlockFlags; use crate::block::entities::vault::VaultBlockEntity; use crate::block::registry::BlockActionResult; -use crate::block::{ - BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, PlacedArgs, UseWithItemArgs, -}; +use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs, PlacedArgs, UseWithItemArgs}; #[pumpkin_block("minecraft:vault")] pub struct VaultBlock; impl BlockBehaviour for VaultBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = LadderLikeProperties::default(args.block); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = LadderLikeProperties::default(args.block); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.to_state_id(args.block) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = VaultBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(entity)); - }) + } } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { - let item_id = args.item_stack.item.id; - let is_trial_key = - item_id == Item::TRIAL_KEY.id || item_id == Item::OMINOUS_TRIAL_KEY.id; + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + let item_id = args.item_stack.item.id; + let is_trial_key = item_id == Item::TRIAL_KEY.id || item_id == Item::OMINOUS_TRIAL_KEY.id; - if !is_trial_key { - args.world.play_sound( - Sound::BlockVaultInsertItemFail, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - return BlockActionResult::Success; - } - - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(vault_entity) = block_entity.as_any().downcast_ref::() - { - let player_uuid = args.player.gameprofile.id; - - if vault_entity.has_rewarded(&player_uuid).await { - args.world.play_sound( - Sound::BlockVaultRejectRewardedPlayer, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - return BlockActionResult::Success; - } - - vault_entity.mark_rewarded(player_uuid).await; - - args.item_stack - .decrement_unless_creative(args.player.gamemode.load(), 1); - - args.world.play_sound( - Sound::BlockVaultInsertItem, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - args.world.play_sound( - Sound::BlockVaultOpenShutter, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - - let state_id = args.world.get_block_state_id(args.position); - let mut props = VaultLikeProperties::from_state_id(state_id, args.block); - props.vault_state = VaultState::Ejecting; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - args.world.play_sound( - Sound::BlockVaultEjectItem, - SoundCategory::Blocks, - &args.position.to_f64(), - ); - - // Spawn trial vault loot (emeralds, diamond, iron) - let loot_stacks = vec![ - ItemStack::new(4, &Item::EMERALD), - ItemStack::new(1, &Item::DIAMOND), - ItemStack::new(2, &Item::IRON_INGOT), - ]; - - for stack in loot_stacks { - args.world.drop_stack(args.position, stack).await; - } - - props.vault_state = VaultState::Active; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - - return BlockActionResult::Success; - } - - BlockActionResult::Pass - }) - } - - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + if !is_trial_key { args.world.play_sound( Sound::BlockVaultInsertItemFail, SoundCategory::Blocks, &args.position.to_f64(), ); - BlockActionResult::Success - }) + return BlockActionResult::Success; + } + + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(vault_entity) = block_entity.as_any().downcast_ref::() + { + let player_uuid = args.player.gameprofile.id; + + if vault_entity.has_rewarded(&player_uuid) { + args.world.play_sound( + Sound::BlockVaultRejectRewardedPlayer, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + return BlockActionResult::Success; + } + + vault_entity.mark_rewarded(player_uuid); + + args.item_stack + .decrement_unless_creative(args.player.gamemode.load(), 1); + + args.world.play_sound( + Sound::BlockVaultInsertItem, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world.play_sound( + Sound::BlockVaultOpenShutter, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + + let state_id = args.world.get_block_state_id(args.position); + let mut props = VaultLikeProperties::from_state_id(state_id, args.block); + props.vault_state = VaultState::Ejecting; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + + args.world.play_sound( + Sound::BlockVaultEjectItem, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + + // Spawn trial vault loot (emeralds, diamond, iron) + let loot_stacks = vec![ + ItemStack::new(4, &Item::EMERALD), + ItemStack::new(1, &Item::DIAMOND), + ItemStack::new(2, &Item::IRON_INGOT), + ]; + + for stack in loot_stacks { + args.world.drop_stack(args.position, stack); + } + + props.vault_state = VaultState::Active; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + + return BlockActionResult::Success; + } + + BlockActionResult::Pass + } + + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + args.world.play_sound( + Sound::BlockVaultInsertItemFail, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + BlockActionResult::Success } } diff --git a/crates/pumpkin/src/block/blocks/vine.rs b/crates/pumpkin/src/block/blocks/vine.rs index af71cf237..b111f2442 100644 --- a/crates/pumpkin/src/block/blocks/vine.rs +++ b/crates/pumpkin/src/block/blocks/vine.rs @@ -1,8 +1,7 @@ use crate::{ block::{ - BlockBehaviour, BlockFuture, CanPlaceAtArgs, CanUpdateAtArgs, - GetStateForNeighborUpdateArgs, OnPlaceArgs, RandomTickArgs, UseWithItemArgs, - registry::BlockActionResult, + BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, + OnPlaceArgs, RandomTickArgs, UseWithItemArgs, registry::BlockActionResult, }, entity::{EntityBase, player::Player}, world::World, @@ -228,36 +227,33 @@ pub fn get_nearest_looking_directions( } impl BlockBehaviour for VineBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let (clicked_block, clicked_state_id) = - args.world.get_block_and_state_id(args.position); - let clicked_is_vine = clicked_block == &Block::VINE; - let mut result = if clicked_is_vine { - VineLikeProperties::from_state_id(clicked_state_id, args.block) - } else { - VineLikeProperties::default(args.block) - }; + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let (clicked_block, clicked_state_id) = args.world.get_block_and_state_id(args.position); + let clicked_is_vine = clicked_block == &Block::VINE; + let mut result = if clicked_is_vine { + VineLikeProperties::from_state_id(clicked_state_id, args.block) + } else { + VineLikeProperties::default(args.block) + }; - let nearest_directions = - get_nearest_looking_directions(args.player, clicked_is_vine, args.direction); + let nearest_directions = + get_nearest_looking_directions(args.player, clicked_is_vine, args.direction); - for direction in nearest_directions { - if direction != BlockDirection::Down { - let face_occupied = clicked_is_vine && has_face_property(&result, direction); - if !face_occupied && can_support_at_face(args.world, args.position, direction) { - set_face_property(&mut result, direction, true); - return result.to_state_id(args.block); - } + for direction in nearest_directions { + if direction != BlockDirection::Down { + let face_occupied = clicked_is_vine && has_face_property(&result, direction); + if !face_occupied && can_support_at_face(args.world, args.position, direction) { + set_face_property(&mut result, direction, true); + return result.to_state_id(args.block); } } + } - if clicked_is_vine && count_faces(&result) > 0 { - result.to_state_id(args.block) - } else { - Block::AIR.default_state.id - } - }) + if clicked_is_vine && count_faces(&result) > 0 { + result.to_state_id(args.block) + } else { + Block::AIR.default_state.id + } } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { @@ -316,27 +312,25 @@ impl BlockBehaviour for VineBlock { clicked_is_vine && count_faces(&result) > 0 } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - if args.direction == BlockDirection::Down { - return args.state_id; - } + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + if args.direction == BlockDirection::Down { + return args.state_id; + } - let updated_props = get_updated_state( - VineLikeProperties::from_state_id(args.state_id, args.block), - args.world, - args.position, - args.block, - ); - if count_faces(&updated_props) == 0 { - Block::AIR.default_state.id - } else { - updated_props.to_state_id(args.block) - } - }) + let updated_props = get_updated_state( + VineLikeProperties::from_state_id(args.state_id, args.block), + args.world, + args.position, + args.block, + ); + if count_faces(&updated_props) == 0 { + Block::AIR.default_state.id + } else { + updated_props.to_state_id(args.block) + } } fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { @@ -348,11 +342,8 @@ impl BlockBehaviour for VineBlock { count_faces(&props) < 5 } - fn use_with_item<'a>( - &'a self, - args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { + fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { + { if args.item_stack.item.id != Item::VINE.id { return BlockActionResult::Pass; } @@ -373,250 +364,217 @@ impl BlockBehaviour for VineBlock { && can_support_at_face(&**args.world, args.position, direction) { set_face_property(&mut props, direction, true); - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + args.world.set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); return BlockActionResult::Consume; } } } BlockActionResult::Pass - }) + } } #[expect(clippy::too_many_lines)] - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let do_spread = matches!( - args.world - .level_info - .load() - .game_rules - .get(&GameRule::SpreadVines), - GameRuleValue::Bool(true) - ); - if !do_spread { - return; - } + fn random_tick(&self, args: RandomTickArgs<'_>) { + let do_spread = matches!( + args.world + .level_info + .load() + .game_rules + .get(&GameRule::SpreadVines), + GameRuleValue::Bool(true) + ); + if !do_spread { + return; + } - if rand::rng().random_range(0..4) != 0 { - return; - } + if rand::rng().random_range(0..4) != 0 { + return; + } - let test_direction = BlockDirection::all()[rand::rng().random_range(0..6)]; - let above_pos = args.position.up(); - let state_id = args.world.get_block_state_id(args.position); - let state_props = VineLikeProperties::from_state_id(state_id, args.block); + let test_direction = BlockDirection::all()[rand::rng().random_range(0..6)]; + let above_pos = args.position.up(); + let state_id = args.world.get_block_state_id(args.position); + let state_props = VineLikeProperties::from_state_id(state_id, args.block); - if test_direction.is_horizontal() && !has_face_property(&state_props, test_direction) { - if can_spread(args.world, args.position) { - let test_pos = args.position.offset(test_direction.to_offset()); - let (edge_block, edge_state) = args.world.get_block_and_state(&test_pos); - if edge_block.default_state.is_air() { - let cw_direction = test_direction.rotate_clockwise(); - let ccw_direction = test_direction.rotate_counter_clockwise(); - let cw_has_connecting_face = has_face_property(&state_props, cw_direction); - let ccw_has_connecting_face = - has_face_property(&state_props, ccw_direction); - let cw_test_pos = test_pos.offset(cw_direction.to_offset()); - let ccw_test_pos = test_pos.offset(ccw_direction.to_offset()); + if test_direction.is_horizontal() && !has_face_property(&state_props, test_direction) { + if can_spread(args.world, args.position) { + let test_pos = args.position.offset(test_direction.to_offset()); + let (edge_block, edge_state) = args.world.get_block_and_state(&test_pos); + if edge_block.default_state.is_air() { + let cw_direction = test_direction.rotate_clockwise(); + let ccw_direction = test_direction.rotate_counter_clockwise(); + let cw_has_connecting_face = has_face_property(&state_props, cw_direction); + let ccw_has_connecting_face = has_face_property(&state_props, ccw_direction); + let cw_test_pos = test_pos.offset(cw_direction.to_offset()); + let ccw_test_pos = test_pos.offset(ccw_direction.to_offset()); - let (cw_test_block, cw_test_state) = - args.world.get_block_and_state(&cw_test_pos); - let (ccw_test_block, ccw_test_state) = - args.world.get_block_and_state(&ccw_test_pos); + let (cw_test_block, cw_test_state) = + args.world.get_block_and_state(&cw_test_pos); + let (ccw_test_block, ccw_test_state) = + args.world.get_block_and_state(&ccw_test_pos); - if cw_has_connecting_face - && is_acceptable_neighbour(cw_test_block, cw_test_state, cw_direction) - { - let mut new_props = VineLikeProperties::default(args.block); - set_face_property(&mut new_props, cw_direction, true); - args.world - .set_block_state( - &test_pos, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } else if ccw_has_connecting_face - && is_acceptable_neighbour( - ccw_test_block, - ccw_test_state, - ccw_direction, - ) - { - let mut new_props = VineLikeProperties::default(args.block); - set_face_property(&mut new_props, ccw_direction, true); - args.world - .set_block_state( - &test_pos, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } else { - let opposite = test_direction.opposite(); - let (cw_support_block, cw_support_state) = - args.world.get_block_and_state( - &args.position.offset(cw_direction.to_offset()), - ); - let (ccw_support_block, ccw_support_state) = - args.world.get_block_and_state( - &args.position.offset(ccw_direction.to_offset()), - ); - - if cw_has_connecting_face - && cw_test_block.default_state.is_air() - && is_acceptable_neighbour( - cw_support_block, - cw_support_state, - opposite, - ) - { - let mut new_props = VineLikeProperties::default(args.block); - set_face_property(&mut new_props, opposite, true); - args.world - .set_block_state( - &cw_test_pos, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } else if ccw_has_connecting_face - && ccw_test_block.default_state.is_air() - && is_acceptable_neighbour( - ccw_support_block, - ccw_support_state, - opposite, - ) - { - let mut new_props = VineLikeProperties::default(args.block); - set_face_property(&mut new_props, opposite, true); - args.world - .set_block_state( - &ccw_test_pos, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } else if rand::rng().random_range(0.0..1.0f32) < 0.05 - && is_acceptable_neighbour( - args.world.get_block(&test_pos.up()), - args.world.get_block_state(&test_pos.up()), - BlockDirection::Up, - ) - { - let mut new_props = VineLikeProperties::default(args.block); - new_props.up = true; - args.world - .set_block_state( - &test_pos, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } - } - } else if is_acceptable_neighbour(edge_block, edge_state, test_direction) { - let mut new_props = state_props; - set_face_property(&mut new_props, test_direction, true); - args.world - .set_block_state( - args.position, - new_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } - } - } else if test_direction == BlockDirection::Up - && args.position.0.y < args.world.dimension.min_y + args.world.dimension.height - 1 - { - if can_support_at_face(&**args.world, args.position, test_direction) { - let mut new_props = state_props; - new_props.up = true; - args.world - .set_block_state( - args.position, + if cw_has_connecting_face + && is_acceptable_neighbour(cw_test_block, cw_test_state, cw_direction) + { + let mut new_props = VineLikeProperties::default(args.block); + set_face_property(&mut new_props, cw_direction, true); + args.world.set_block_state( + &test_pos, new_props.to_state_id(args.block), BlockFlags::NOTIFY_ALL, - ) - .await; + ); + } else if ccw_has_connecting_face + && is_acceptable_neighbour(ccw_test_block, ccw_test_state, ccw_direction) + { + let mut new_props = VineLikeProperties::default(args.block); + set_face_property(&mut new_props, ccw_direction, true); + args.world.set_block_state( + &test_pos, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } else { + let opposite = test_direction.opposite(); + let (cw_support_block, cw_support_state) = args + .world + .get_block_and_state(&args.position.offset(cw_direction.to_offset())); + let (ccw_support_block, ccw_support_state) = args + .world + .get_block_and_state(&args.position.offset(ccw_direction.to_offset())); + + if cw_has_connecting_face + && cw_test_block.default_state.is_air() + && is_acceptable_neighbour(cw_support_block, cw_support_state, opposite) + { + let mut new_props = VineLikeProperties::default(args.block); + set_face_property(&mut new_props, opposite, true); + args.world.set_block_state( + &cw_test_pos, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } else if ccw_has_connecting_face + && ccw_test_block.default_state.is_air() + && is_acceptable_neighbour( + ccw_support_block, + ccw_support_state, + opposite, + ) + { + let mut new_props = VineLikeProperties::default(args.block); + set_face_property(&mut new_props, opposite, true); + args.world.set_block_state( + &ccw_test_pos, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } else if rand::rng().random_range(0.0..1.0f32) < 0.05 + && is_acceptable_neighbour( + args.world.get_block(&test_pos.up()), + args.world.get_block_state(&test_pos.up()), + BlockDirection::Up, + ) + { + let mut new_props = VineLikeProperties::default(args.block); + new_props.up = true; + args.world.set_block_state( + &test_pos, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + } + } else if is_acceptable_neighbour(edge_block, edge_state, test_direction) { + let mut new_props = state_props; + set_face_property(&mut new_props, test_direction, true); + args.world.set_block_state( + args.position, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + } + } else if test_direction == BlockDirection::Up + && args.position.0.y < args.world.dimension.min_y + args.world.dimension.height - 1 + { + if can_support_at_face(&**args.world, args.position, test_direction) { + let mut new_props = state_props; + new_props.up = true; + args.world.set_block_state( + args.position, + new_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + return; + } + + if args.world.get_block(&above_pos).default_state.is_air() { + if !can_spread(args.world, args.position) { return; } - if args.world.get_block(&above_pos).default_state.is_air() { - if !can_spread(args.world, args.position) { - return; - } - - let mut above_props = state_props; - for direction in [ - BlockDirection::North, - BlockDirection::South, - BlockDirection::West, - BlockDirection::East, - ] { - let rel_pos = above_pos.offset(direction.to_offset()); - let (rel_block, rel_state) = args.world.get_block_and_state(&rel_pos); - if rand::rng().random_range(0..2) == 0 - || !is_acceptable_neighbour(rel_block, rel_state, direction) - { - set_face_property(&mut above_props, direction, false); - } - } - - if has_horizontal_connection(&above_props) { - args.world - .set_block_state( - &above_pos, - above_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; + let mut above_props = state_props; + for direction in [ + BlockDirection::North, + BlockDirection::South, + BlockDirection::West, + BlockDirection::East, + ] { + let rel_pos = above_pos.offset(direction.to_offset()); + let (rel_block, rel_state) = args.world.get_block_and_state(&rel_pos); + if rand::rng().random_range(0..2) == 0 + || !is_acceptable_neighbour(rel_block, rel_state, direction) + { + set_face_property(&mut above_props, direction, false); } } - } else if args.position.0.y > args.world.dimension.min_y { - let below_pos = args.position.down(); - let (below_block, below_state) = args.world.get_block_and_state(&below_pos); - if below_block.default_state.is_air() || below_block == &Block::VINE { - let before_props = if below_block.default_state.is_air() { - VineLikeProperties::default(args.block) - } else { - VineLikeProperties::from_state_id(below_state.id, below_block) - }; - let mut after_props = before_props; - for direction in [ - BlockDirection::North, - BlockDirection::South, - BlockDirection::West, - BlockDirection::East, - ] { - if rand::rng().random_range(0..2) == 0 - && has_face_property(&state_props, direction) - { - set_face_property(&mut after_props, direction, true); - } - } - - if before_props != after_props && has_horizontal_connection(&after_props) { - args.world - .set_block_state( - &below_pos, - after_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } + if has_horizontal_connection(&above_props) { + args.world.set_block_state( + &above_pos, + above_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); } } - }) + } else if args.position.0.y > args.world.dimension.min_y { + let below_pos = args.position.down(); + let (below_block, below_state) = args.world.get_block_and_state(&below_pos); + if below_block.default_state.is_air() || below_block == &Block::VINE { + let before_props = if below_block.default_state.is_air() { + VineLikeProperties::default(args.block) + } else { + VineLikeProperties::from_state_id(below_state.id, below_block) + }; + + let mut after_props = before_props; + for direction in [ + BlockDirection::North, + BlockDirection::South, + BlockDirection::West, + BlockDirection::East, + ] { + if rand::rng().random_range(0..2) == 0 + && has_face_property(&state_props, direction) + { + set_face_property(&mut after_props, direction, true); + } + } + + if before_props != after_props && has_horizontal_connection(&after_props) { + args.world.set_block_state( + &below_pos, + after_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ); + } + } + } } fn rotate( diff --git a/crates/pumpkin/src/block/blocks/walls.rs b/crates/pumpkin/src/block/blocks/walls.rs index 91b4ebb35..e3eac2c76 100644 --- a/crates/pumpkin/src/block/blocks/walls.rs +++ b/crates/pumpkin/src/block/blocks/walls.rs @@ -1,4 +1,3 @@ -use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; @@ -25,23 +24,19 @@ type WallProperties = pumpkin_data::block_properties::ResinBrickWallLikeProperti pub struct WallBlock; impl BlockBehaviour for WallBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut wall_props = WallProperties::default(args.block); - wall_props.waterlogged = args.replacing.water_source(); + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut wall_props = WallProperties::default(args.block); + wall_props.waterlogged = args.replacing.water_source(); - compute_wall_state(wall_props, args.world, args.block, args.position) - }) + compute_wall_state(wall_props, args.world, args.block, args.position) } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let wall_props = WallProperties::from_state_id(args.state_id, args.block); - compute_wall_state(wall_props, args.world, args.block, args.position) - }) + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let wall_props = WallProperties::from_state_id(args.state_id, args.block); + compute_wall_state(wall_props, args.world, args.block, args.position) } } diff --git a/crates/pumpkin/src/block/blocks/weathering_copper.rs b/crates/pumpkin/src/block/blocks/weathering_copper.rs index 54da43176..ef9730687 100644 --- a/crates/pumpkin/src/block/blocks/weathering_copper.rs +++ b/crates/pumpkin/src/block/blocks/weathering_copper.rs @@ -19,7 +19,7 @@ use crate::block::blocks::stairs::StairBlock; use crate::block::blocks::trapdoor::TrapDoorBlock; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, + BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, PlacedArgs, RandomTickArgs, }; @@ -503,7 +503,7 @@ pub fn scan_neighbor_oxidation_levels( } /// Executes a random tick change-over-time attempt on a weathering copper block using vanilla's probability formula. -pub async fn change_over_time(world: &Arc, position: &BlockPos, block: &Block) { +pub fn change_over_time(world: &Arc, position: &BlockPos, block: &Block) { use rand::RngExt; // 1. Roll base degradation chance (~5.69%) @@ -538,9 +538,7 @@ pub async fn change_over_time(world: &Arc, position: &BlockPos, block: &B let current_state_id = world.get_block_state_id(position); let new_state_id = with_properties_of(block, current_state_id, next_block); - world - .set_block_state(position, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(position, new_state_id, BlockFlags::NOTIFY_ALL); // Special handling for multi-block structures: // Door: update upper half if present @@ -551,9 +549,7 @@ pub async fn change_over_time(world: &Arc, position: &BlockPos, block: &B let (top_block, top_state_id) = world.get_block_and_state_id(&top_pos); if top_block == block { let top_new_state_id = with_properties_of(top_block, top_state_id, next_block); - world - .set_block_state(&top_pos, top_new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&top_pos, top_new_state_id, BlockFlags::NOTIFY_ALL); } } } @@ -570,9 +566,7 @@ pub async fn change_over_time(world: &Arc, position: &BlockPos, block: &B if right_block == block { let right_new_state_id = with_properties_of(right_block, right_state_id, next_block); - world - .set_block_state(&right_pos, right_new_state_id, BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(&right_pos, right_new_state_id, BlockFlags::NOTIFY_LISTENERS); } } } @@ -647,10 +641,8 @@ impl BlockMetadata for WeatheringCopperBlock { } impl BlockBehaviour for WeatheringCopperBlock { - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } @@ -695,12 +687,12 @@ impl BlockMetadata for WeatheringCopperStairBlock { } impl BlockBehaviour for WeatheringCopperStairBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { StairBlock.on_place(args) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - StairBlock.on_neighbor_update(args) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + StairBlock.on_neighbor_update(args); } fn rotate( @@ -716,10 +708,8 @@ impl BlockBehaviour for WeatheringCopperStairBlock { StairBlock.mirror(block, state_id, mirror) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } @@ -764,22 +754,20 @@ impl BlockMetadata for WeatheringCopperTrapDoorBlock { } impl BlockBehaviour for WeatheringCopperTrapDoorBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { TrapDoorBlock.on_place(args) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { TrapDoorBlock.normal_use(args) } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - TrapDoorBlock.on_neighbor_update(args) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + TrapDoorBlock.on_neighbor_update(args); } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } @@ -824,7 +812,7 @@ impl BlockMetadata for WeatheringCopperSlabBlock { } impl BlockBehaviour for WeatheringCopperSlabBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { SlabBlock.on_place(args) } @@ -832,10 +820,8 @@ impl BlockBehaviour for WeatheringCopperSlabBlock { SlabBlock.can_update_at(args) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } @@ -880,11 +866,11 @@ impl BlockMetadata for WeatheringCopperDoorBlock { } impl BlockBehaviour for WeatheringCopperDoorBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { DoorBlock.on_place(args) } - fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { DoorBlock.normal_use(args) } @@ -892,37 +878,35 @@ impl BlockBehaviour for WeatheringCopperDoorBlock { DoorBlock.can_place_at(args) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - DoorBlock.placed(args) + fn placed(&self, args: PlacedArgs<'_>) { + DoorBlock.placed(args); } - fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - DoorBlock.broken(args) + fn broken(&self, args: BrokenArgs<'_>) { + DoorBlock.broken(args); } - fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - DoorBlock.on_neighbor_update(args) + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + DoorBlock.on_neighbor_update(args); } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { DoorBlock.get_state_for_neighbor_update(args) } - fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - DoorBlock.on_state_replaced(args) + fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { + DoorBlock.on_state_replaced(args); } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - let state_id = args.world.get_block_state_id(args.position); - let door_props = OakDoorLikeProperties::from_state_id(state_id, args.block); - if door_props.half == DoubleBlockHalf::Lower { - change_over_time(args.world, args.position, args.block).await; - } - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + let state_id = args.world.get_block_state_id(args.position); + let door_props = OakDoorLikeProperties::from_state_id(state_id, args.block); + if door_props.half == DoubleBlockHalf::Lower { + change_over_time(args.world, args.position, args.block); + } } } @@ -967,17 +951,13 @@ impl BlockMetadata for WeatheringCopperGrateBlock { } impl BlockBehaviour for WeatheringCopperGrateBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { - let mut props = MangroveRootsLikeProperties::default(args.block); - props.waterlogged = args.replacing.water_source(); - props.to_state_id(args.block) - }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + let mut props = MangroveRootsLikeProperties::default(args.block); + props.waterlogged = args.replacing.water_source(); + props.to_state_id(args.block) } - fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - change_over_time(args.world, args.position, args.block).await; - }) + fn random_tick(&self, args: RandomTickArgs<'_>) { + change_over_time(args.world, args.position, args.block); } } diff --git a/crates/pumpkin/src/block/blocks/wither_skull.rs b/crates/pumpkin/src/block/blocks/wither_skull.rs index c9b9e5125..342635a6a 100644 --- a/crates/pumpkin/src/block/blocks/wither_skull.rs +++ b/crates/pumpkin/src/block/blocks/wither_skull.rs @@ -3,9 +3,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_world::world::BlockFlags; use crate::{ - block::{ - BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs, blocks::skull_block::SkullBlock, - }, + block::{BlockBehaviour, OnPlaceArgs, PlacedArgs, blocks::skull_block::SkullBlock}, entity::{Entity, boss::wither::WitherEntity}, }; @@ -13,12 +11,12 @@ use crate::{ pub struct WitherSkeletonSkullBlock; impl BlockBehaviour for WitherSkeletonSkullBlock { - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { SkullBlock::on_place(&SkullBlock, args) } - fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { + fn placed(&self, args: PlacedArgs<'_>) { + { let entity = crate::block::entities::skull::SkullBlockEntity::new(*args.position); args.world.add_block_entity(std::sync::Arc::new(entity)); @@ -68,13 +66,11 @@ impl BlockBehaviour for WitherSkeletonSkullBlock { ]; for p in pattern { - world - .set_block_state( - &p, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &p, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); world.sync_world_event( WorldEvent::ParticlesDestroyBlock, p, @@ -89,11 +85,11 @@ impl BlockBehaviour for WitherSkeletonSkullBlock { ); let wither = WitherEntity::new(entity); wither.make_invulnerable(); - world.spawn_entity(wither).await; + world.spawn_entity(wither); return; } } } - }) + } } } diff --git a/crates/pumpkin/src/block/entities/barrel.rs b/crates/pumpkin/src/block/entities/barrel.rs index 9f11dba69..818dc669a 100644 --- a/crates/pumpkin/src/block/entities/barrel.rs +++ b/crates/pumpkin/src/block/entities/barrel.rs @@ -16,9 +16,7 @@ use std::{ }, }; -use crate::block::viewer::{ - ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, -}; +use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt}; use crate::world::{BlockFlags, World}; use pumpkin_world::inventory::InventoryFuture; use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; @@ -66,12 +64,9 @@ impl BlockEntity for BarrelBlockEntity { self.write_inventory_nbt(nbt, true) } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; - }) + fn tick(&self, world: &Arc) { + self.viewers + .update_viewer_count::(self, world, &self.position); } fn get_inventory(self: Arc) -> Option> { @@ -100,26 +95,14 @@ impl BlockEntity for BarrelBlockEntity { } impl ViewerCountListener for BarrelBlockEntity { - fn on_container_open<'a>( - &'a self, - world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, Sound::BlockBarrelOpen); - self.set_open(world, true).await; - }) + fn on_container_open(&self, world: &Arc, _position: &BlockPos) { + self.play_sound(world, Sound::BlockBarrelOpen); + self.set_open(world, true); } - fn on_container_close<'a>( - &'a self, - world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, Sound::BlockBarrelClose); - self.set_open(world, false).await; - }) + fn on_container_close(&self, world: &Arc, _position: &BlockPos) { + self.play_sound(world, Sound::BlockBarrelClose); + self.set_open(world, false); } } @@ -137,20 +120,17 @@ impl BarrelBlockEntity { } } - async fn set_open(&self, world: &Arc, open: bool) { + fn set_open(&self, world: &Arc, open: bool) { let state = world.get_block_state(&self.position); let mut properties = BarrelLikeProperties::from_state_id(state.id, &Block::BARREL); properties.open = open; - world - .clone() - .set_block_state( - &self.position, - properties.to_state_id(&Block::BARREL), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &self.position, + properties.to_state_id(&Block::BARREL), + BlockFlags::NOTIFY_ALL, + ); } fn play_sound(&self, world: &Arc, sound: Sound) { diff --git a/crates/pumpkin/src/block/entities/beacon.rs b/crates/pumpkin/src/block/entities/beacon.rs index bb55b220c..ad44e6173 100644 --- a/crates/pumpkin/src/block/entities/beacon.rs +++ b/crates/pumpkin/src/block/entities/beacon.rs @@ -157,7 +157,7 @@ impl BeaconBlockEntity { } /// Replicates Java's `applyEffects` bounding box mapping and duration mapping - async fn apply_effects(&self, world: &Arc, levels: i32) { + fn apply_effects(&self, world: &Arc, levels: i32) { if levels <= 0 { return; } @@ -194,34 +194,30 @@ impl BeaconBlockEntity { for player in players { if let Some(effect) = primary_effect { - player - .add_effect(pumpkin_data::potion::Effect { - effect_type: effect, - duration: duration_ticks, - amplifier: base_amp as u8, - ambient: true, - show_particles: true, - show_icon: true, - blend: false, - }) - .await; + player.add_effect(pumpkin_data::potion::Effect { + effect_type: effect, + duration: duration_ticks, + amplifier: base_amp as u8, + ambient: true, + show_particles: true, + show_icon: true, + blend: false, + }); } if levels >= 4 && primary_id != secondary_id && let Some(effect) = secondary_effect { - player - .add_effect(pumpkin_data::potion::Effect { - effect_type: effect, - duration: duration_ticks, - amplifier: 0, - ambient: true, - show_particles: true, - show_icon: true, - blend: false, - }) - .await; + player.add_effect(pumpkin_data::potion::Effect { + effect_type: effect, + duration: duration_ticks, + amplifier: 0, + ambient: true, + show_particles: true, + show_icon: true, + blend: false, + }); } } } @@ -286,21 +282,19 @@ impl BlockEntity for BeaconBlockEntity { }) } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - // Check properties every 80 ticks matching Java - if world.get_time_of_day().await % 80 == 0 { - let levels = self.update_base(world); - self.levels.store(levels, Ordering::Relaxed); + fn tick(&self, world: &Arc) { + // Check properties every 80 ticks matching Java + if world.get_time_of_day() % 80 == 0 { + let levels = self.update_base(world); + self.levels.store(levels, Ordering::Relaxed); - // TODO: Beam Section validation (scanning upward to heightmap to check for sky visibility) - // is typically checked here before applying effects in Vanilla. + // TODO: Beam Section validation (scanning upward to heightmap to check for sky visibility) + // is typically checked here before applying effects in Vanilla. - if levels > 0 { - self.apply_effects(world, levels).await; - } + if levels > 0 { + self.apply_effects(world, levels); } - }) + } } fn chunk_data_nbt(&self) -> Option { diff --git a/crates/pumpkin/src/block/entities/bell.rs b/crates/pumpkin/src/block/entities/bell.rs index e34947914..cb8a6cc81 100644 --- a/crates/pumpkin/src/block/entities/bell.rs +++ b/crates/pumpkin/src/block/entities/bell.rs @@ -61,37 +61,33 @@ impl BlockEntity for BellBlockEntity { Self::new(position) } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - if self.ringing.load() { - self.ring_ticks.fetch_add(1); - } - if self.ring_ticks.load() >= 50 { - self.ringing.store(false); - self.ring_ticks.store(0); - } - if self.ring_ticks.load() >= 5 - && self.resonate_time.load() == 0 - && self.raiders_hear_bell() - { - self.resonating.store(true); - world.play_sound_fine( - Sound::BlockBellResonate, - SoundCategory::Blocks, - &self.position.to_f64(), - 1.0, - 1.0, - ); - } + fn tick(&self, world: &Arc) { + if self.ringing.load() { + self.ring_ticks.fetch_add(1); + } + if self.ring_ticks.load() >= 50 { + self.ringing.store(false); + self.ring_ticks.store(0); + } + if self.ring_ticks.load() >= 5 && self.resonate_time.load() == 0 && self.raiders_hear_bell() + { + self.resonating.store(true); + world.play_sound_fine( + Sound::BlockBellResonate, + SoundCategory::Blocks, + &self.position.to_f64(), + 1.0, + 1.0, + ); + } - if self.resonating.load() { - if self.resonate_time.load() < 40 { - self.resonate_time.fetch_add(1); - } else { - self.resonating.store(false); - } + if self.resonating.load() { + if self.resonate_time.load() < 40 { + self.resonate_time.fetch_add(1); + } else { + self.resonating.store(false); } - }) + } } fn resource_location(&self) -> &'static str { diff --git a/crates/pumpkin/src/block/entities/brewing_stand.rs b/crates/pumpkin/src/block/entities/brewing_stand.rs index b5d4256bc..17af78ae8 100644 --- a/crates/pumpkin/src/block/entities/brewing_stand.rs +++ b/crates/pumpkin/src/block/entities/brewing_stand.rs @@ -58,7 +58,7 @@ impl BrewingStandBlockEntity { } /// Check if any potion slot has a valid recipe with the ingredient - async fn is_brewable(&self, ingredient: &ItemStack) -> bool { + fn is_brewable(&self, ingredient: &ItemStack) -> bool { if ingredient.is_empty() { return false; } @@ -66,7 +66,9 @@ impl BrewingStandBlockEntity { let ingredient_id = ingredient.get_item().id; // Check potion recipes (water bottle -> potions, potion upgrades, etc.) - let items = self.items.read().await; + let Ok(items) = self.items.try_read() else { + return false; + }; for slot_idx in 0..3usize { let slot = &items[slot_idx]; if slot.is_empty() { @@ -101,12 +103,14 @@ impl BrewingStandBlockEntity { } /// Perform brewing on all valid potion slots - async fn do_brew(&self, world: &Arc, ingredient: &ItemStack) { + fn do_brew(&self, world: &Arc, ingredient: &ItemStack) { let ingredient_id = ingredient.get_item().id; // Apply recipes to each slot for slot_idx in 0..3usize { - let items = self.items.read().await; + let Ok(items) = self.items.try_read() else { + continue; + }; let slot = &items[slot_idx]; if slot.is_empty() { continue; @@ -171,28 +175,28 @@ impl BrewingStandBlockEntity { drop(items); - // Update the slot using set_stack if a recipe was applied - if let Some(new_stack) = new_stack_opt { - self.set_stack(slot_idx, new_stack).await; + // Update the slot if a recipe was applied + if let Some(new_stack) = new_stack_opt + && let Ok(mut items) = self.items.try_write() + { + items[slot_idx] = new_stack; + self.mark_dirty(); } } - let mut event = crate::plugin::api::events::inventory::brew::BrewEvent::new( - self.position, - self.fuel.load(std::sync::atomic::Ordering::Relaxed) as u8, - ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return; + let mut event = crate::plugin::api::events::inventory::brew::BrewEvent::new( + self.position, + self.fuel.load(std::sync::atomic::Ordering::Relaxed) as u8, + ); + server.plugin_manager.fire_blocking(&server, &mut event); } // Consume ingredient - let mut items = self.items.write().await; - items[3].decrement(1); - self.mark_dirty(); - drop(items); + if let Ok(mut items) = self.items.try_write() { + items[3].decrement(1); + self.mark_dirty(); + } // Play sound at the center of the block let pos = Vector3::new( @@ -407,7 +411,9 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { let mut nbt = NbtCompound::new(); nbt.put_int("BrewTime", self.brew_time.load(Ordering::Relaxed)); nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed)); - sync_write_items_to_nbt(&*futures::executor::block_on(self.items.read()), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(&*items, &mut nbt); + } Some(nbt) } @@ -423,127 +429,116 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { self } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - // Refill fuel counter from fuel item if needed - let fuel_refilled = if self.fuel.load(Ordering::Relaxed) <= 0 { - let mut items = self.items.write().await; - if !items[4].is_empty() - && items[4] - .get_item() - .has_tag(&tag::Item::MINECRAFT_BREWING_FUEL) - { + fn tick(&self, world: &Arc) { + // Refill fuel counter from fuel item if needed + let fuel_refilled = self.fuel.load(Ordering::Relaxed) <= 0 + && if let Ok(mut items) = self.items.try_write() + && !items[4].is_empty() + && items[4] + .get_item() + .has_tag(&tag::Item::MINECRAFT_BREWING_FUEL) + { + if let Some(server) = world.server.upgrade() { let mut fuel_event = crate::plugin::api::events::inventory::brewing_stand_fuel::BrewingStandFuelEvent::new( - self.position, - 20, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut fuel_event).await; - } - if fuel_event.cancelled { - false - } else { - self.fuel - .store(fuel_event.fuel_power as i32, Ordering::Relaxed); - items[4].decrement(1); - true - } - } else { - false + self.position, + 20, + ); + server + .plugin_manager + .fire_blocking(&server, &mut fuel_event); } + self.fuel.store(20, Ordering::Relaxed); + items[4].decrement(1); + true } else { false }; - // Get current ingredient and check brewing state - let ingredient = self.items.read().await[3].clone(); - let brewable = self.is_brewable(&ingredient).await; - let is_brewing = self.brew_time.load(Ordering::Relaxed) > 0; + // Get current ingredient and check brewing state + let Ok(items) = self.items.try_read() else { + return; + }; + let ingredient = items[3].clone(); + drop(items); + let brewable = self.is_brewable(&ingredient); + let is_brewing = self.brew_time.load(Ordering::Relaxed) > 0; - // Handle brewing state machine - if is_brewing { - // Decrement brew time - let new_brew_time = self.brew_time.fetch_sub(1, Ordering::Relaxed) - 1; - let is_done_brewing = new_brew_time == 0; + // Handle brewing state machine + if is_brewing { + // Decrement brew time + let new_brew_time = self.brew_time.fetch_sub(1, Ordering::Relaxed) - 1; + let is_done_brewing = new_brew_time == 0; - if is_done_brewing && brewable { - // Brewing complete - self.do_brew(world, &ingredient).await; - } else if !brewable || !self.ingredient_matches(&ingredient) { - // Cancel brewing - self.brew_time.store(0, Ordering::Relaxed); - self.mark_dirty(); - } else { - // Continue brewing - self.mark_dirty(); - } - } else if brewable && self.fuel.load(Ordering::Relaxed) > 0 { - // Start new brewing cycle - self.fuel.fetch_sub(1, Ordering::Relaxed); - self.brew_time.store(400, Ordering::Relaxed); - *self - .ingredient_item - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - Some(ingredient.get_item()); + if is_done_brewing && brewable { + // Brewing complete + self.do_brew(world, &ingredient); + } else if !brewable || !self.ingredient_matches(&ingredient) { + // Cancel brewing + self.brew_time.store(0, Ordering::Relaxed); self.mark_dirty(); - } else if fuel_refilled { - // Mark dirty if fuel was refilled to update fuel indicator + } else { + // Continue brewing self.mark_dirty(); } + } else if brewable && self.fuel.load(Ordering::Relaxed) > 0 { + // Start new brewing cycle + self.fuel.fetch_sub(1, Ordering::Relaxed); + self.brew_time.store(400, Ordering::Relaxed); + *self + .ingredient_item + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(ingredient.get_item()); + self.mark_dirty(); + } else if fuel_refilled { + // Mark dirty if fuel was refilled to update fuel indicator + self.mark_dirty(); + } - // Ensure clients are notified when potion slot contents (and their data) change. - // Compute current presence bits for the three bottle slots - let mut current: [bool; 3] = [false; 3]; - let items_guard = self.items.read().await; + // Ensure clients are notified when potion slot contents (and their data) change. + // Compute current presence bits for the three bottle slots + let mut current: [bool; 3] = [false; 3]; + if let Ok(items_guard) = self.items.try_read() { for (i, slot) in items_guard.iter().take(3).enumerate() { // Consider a potion slot "present" when it has an item and a PotionContents component or is a glass bottle current[i] = !slot.is_empty() && (slot.get_data_component::().is_some() || slot.get_item().id == Item::GLASS_BOTTLE.id); } - drop(items_guard); + } - // If potion presence changed, update last_potion_count and update block state so clients - let mut needs_update = false; - { - let mut last_guard = self - .last_potion_count - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if last_guard.as_ref() != Some(¤t) { - *last_guard = Some(current); - needs_update = true; - } + // If potion presence changed, update last_potion_count and update block state so clients + let mut needs_update = false; + { + let mut last_guard = self + .last_potion_count + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if last_guard.as_ref() != Some(¤t) { + *last_guard = Some(current); + needs_update = true; } + } - if needs_update { - // Update the block state properties for the brewing stand to reflect bottle presence - let world = world.clone(); - let (block, state) = world.get_block_and_state(&self.position); - // Use generated block properties helper to produce a new state id with the bits set - let mut props = - pumpkin_data::block_properties::BrewingStandLikeProperties::from_state_id( - state.id, block, - ); - // Generated field names use raw identifiers for clarity - props.r#has_bottle_0 = current[0]; - props.r#has_bottle_1 = current[1]; - props.r#has_bottle_2 = current[2]; + if needs_update { + // Update the block state properties for the brewing stand to reflect bottle presence + let (block, state) = world.get_block_and_state(&self.position); + // Use generated block properties helper to produce a new state id with the bits set + let mut props = + pumpkin_data::block_properties::BrewingStandLikeProperties::from_state_id( + state.id, block, + ); + // Generated field names use raw identifiers for clarity + props.r#has_bottle_0 = current[0]; + props.r#has_bottle_1 = current[1]; + props.r#has_bottle_2 = current[2]; - world - .set_block_state( - &self.position, - props.to_state_id(block), - crate::world::BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &self.position, + props.to_state_id(block), + crate::world::BlockFlags::NOTIFY_ALL, + ); - // Also mark dirty so inventory/container updates are sent to open screens - self.mark_dirty(); - } - }) + // Also mark dirty so inventory/container updates are sent to open screens + self.mark_dirty(); + } } fn to_property_delegate(self: Arc) -> Option> { diff --git a/crates/pumpkin/src/block/entities/chest_like_block_entity.rs b/crates/pumpkin/src/block/entities/chest_like_block_entity.rs index 76060ac73..42885ff87 100644 --- a/crates/pumpkin/src/block/entities/chest_like_block_entity.rs +++ b/crates/pumpkin/src/block/entities/chest_like_block_entity.rs @@ -24,7 +24,9 @@ macro_rules! impl_block_entity_for_chest { let mut chest = Self { position, - items: tokio::sync::RwLock::new(std::array::from_fn(|_| ItemStack::EMPTY.clone())), + items: tokio::sync::RwLock::new(std::array::from_fn(|_| { + ItemStack::EMPTY.clone() + })), dirty: std::sync::atomic::AtomicBool::new(false), viewers: $crate::block::viewer::ViewerCountTracker::new(), loot_table: StdMutex::new(loot_table_key), @@ -53,7 +55,10 @@ macro_rules! impl_block_entity_for_chest { Box::pin(async move { // Clone the loot table key without holding the lock across an await. let loot_table_key = { - let guard = self.loot_table.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + let guard = self + .loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); guard.clone() }; @@ -70,22 +75,18 @@ macro_rules! impl_block_entity_for_chest { }) } - fn tick<'a>( - &'a self, - world: &'a Arc<$crate::world::World>, - ) -> std::pin::Pin + Send + 'a>> { - Box::pin(async move { - $crate::block::viewer::ViewerCountTrackerExt::update_viewer_count::<$struct_name>( - &self.viewers, - self, - world, - &self.position, - ) - .await; - }) + fn tick(&self, world: &Arc<$crate::world::World>) { + $crate::block::viewer::ViewerCountTrackerExt::update_viewer_count::<$struct_name>( + &self.viewers, + self, + world, + &self.position, + ); } - fn get_inventory(self: Arc) -> Option> { + fn get_inventory( + self: Arc, + ) -> Option> { Some(self) } @@ -100,13 +101,14 @@ macro_rules! impl_block_entity_for_chest { fn chunk_data_nbt(&self) -> Option { let mut nbt = pumpkin_nbt::compound::NbtCompound::new(); - let has_loot_table = self.loot_table.lock().unwrap_or_else(std::sync::PoisonError::into_inner).is_some(); + let has_loot_table = self + .loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some(); if !has_loot_table { if let Ok(items) = self.items.try_read() { pumpkin_world::inventory::sync_write_items_to_nbt(&*items, &mut nbt); - } else { - let items = futures::executor::block_on(self.items.read()); - pumpkin_world::inventory::sync_write_items_to_nbt(&*items, &mut nbt); } } Some(nbt) @@ -117,12 +119,18 @@ macro_rules! impl_block_entity_for_chest { } fn take_loot_table(&self) -> Option<(String, i64)> { - let mut guard = self.loot_table.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + let mut guard = self + .loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); guard.take().map(|key| (key, self.loot_table_seed)) } fn has_loot_table(&self) -> bool { - self.loot_table.lock().unwrap_or_else(std::sync::PoisonError::into_inner).is_some() + self.loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() } } }; @@ -244,57 +252,43 @@ macro_rules! impl_clearable_for_chest { macro_rules! impl_viewer_count_listener_for_chest { ($struct_name:ty) => { impl $crate::block::viewer::ViewerCountListener for $struct_name { - fn on_container_open<'a>( - &'a self, - world: &'a Arc<$crate::world::World>, - _position: &'a pumpkin_util::math::position::BlockPos, - ) -> $crate::block::viewer::ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, pumpkin_data::sound::Sound::BlockChestOpen) - .await; - }) + fn on_container_open( + &self, + world: &Arc<$crate::world::World>, + _position: &pumpkin_util::math::position::BlockPos, + ) { + self.play_sound(world, pumpkin_data::sound::Sound::BlockChestOpen); } - fn on_container_close<'a>( - &'a self, - world: &'a Arc<$crate::world::World>, - _position: &'a pumpkin_util::math::position::BlockPos, - ) -> $crate::block::viewer::ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, pumpkin_data::sound::Sound::BlockChestClose) - .await; - }) + fn on_container_close( + &self, + world: &Arc<$crate::world::World>, + _position: &pumpkin_util::math::position::BlockPos, + ) { + self.play_sound(world, pumpkin_data::sound::Sound::BlockChestClose); } - fn on_viewer_count_update<'a>( - &'a self, - world: &'a Arc<$crate::world::World>, - position: &'a pumpkin_util::math::position::BlockPos, + fn on_viewer_count_update( + &self, + world: &Arc<$crate::world::World>, + position: &pumpkin_util::math::position::BlockPos, old: u16, new: u16, - ) -> $crate::block::viewer::ViewerFuture<'a, ()> { - Box::pin(async move { - // Trigger block animation - world - .add_synced_block_event( - *position, - Self::LID_ANIMATION_EVENT_TYPE, - new as u8, - ) - .await; + ) { + // Trigger block animation + world.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8); - // Update neighbors for redstone signal when viewer count changes - // This is controlled by the EMITS_REDSTONE constant on the struct - if Self::EMITS_REDSTONE && old != new { - // Update direct neighbors - world.clone().update_neighbors(position, None).await; + // Update neighbors for redstone signal when viewer count changes + // This is controlled by the EMITS_REDSTONE constant on the struct + if Self::EMITS_REDSTONE && old != new { + // Update direct neighbors + world.update_neighbors(position, None); - // Also update neighbors of the block below (strongly powered block) - // This ensures redstone components adjacent to the block below are notified - let below_pos = position.down(); - world.clone().update_neighbors(&below_pos, None).await; - } - }) + // Also update neighbors of the block below (strongly powered block) + // This ensures redstone components adjacent to the block below are notified + let below_pos = position.down(); + world.update_neighbors(&below_pos, None); + } } } }; @@ -329,7 +323,7 @@ macro_rules! impl_chest_helper_methods { } } - async fn play_sound( + fn play_sound( &self, world: &Arc<$crate::world::World>, sound: pumpkin_data::sound::Sound, diff --git a/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs b/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs index 3133a4686..a822b1eee 100644 --- a/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs +++ b/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs @@ -105,10 +105,10 @@ impl ChiseledBookshelfBlockEntity { } } - pub async fn update_state( + pub fn update_state( &self, mut properties: ChiseledBookshelfLikeProperties, - world: Arc, + world: &Arc, slot: usize, ) { if (0..Self::INVENTORY_SIZE).contains(&slot) { @@ -116,7 +116,7 @@ impl ChiseledBookshelfBlockEntity { .store(slot as i8, Ordering::Relaxed); self.mark_dirty(); - let occupied = !self.get_stack(slot).await.is_empty(); + let occupied = !self.items.blocking_read()[slot].is_empty(); match slot { 0 => properties.slot_0_occupied = occupied, 1 => properties.slot_1_occupied = occupied, @@ -127,13 +127,11 @@ impl ChiseledBookshelfBlockEntity { _ => {} } - world - .set_block_state( - &self.position, - properties.to_state_id(&Block::CHISELED_BOOKSHELF), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + &self.position, + properties.to_state_id(&Block::CHISELED_BOOKSHELF), + BlockFlags::NOTIFY_LISTENERS, + ); } else { warn!( "Invalid interacted slot: {} for chiseled bookshelf at position {:?}", @@ -141,6 +139,23 @@ impl ChiseledBookshelfBlockEntity { ); } } + + pub fn set_book(&self, slot: usize, stack: ItemStack) { + let mut items = self.items.blocking_write(); + items[slot] = stack; + self.mark_dirty(); + } + + pub fn remove_book(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self.items.blocking_write(); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res + } } impl Inventory for ChiseledBookshelfBlockEntity { diff --git a/crates/pumpkin/src/block/entities/command_block.rs b/crates/pumpkin/src/block/entities/command_block.rs index e12aea917..2a126b70d 100644 --- a/crates/pumpkin/src/block/entities/command_block.rs +++ b/crates/pumpkin/src/block/entities/command_block.rs @@ -1,13 +1,14 @@ use std::{ pin::Pin, - sync::atomic::{AtomicBool, AtomicU32, Ordering}, + sync::{ + Mutex as StdMutex, + atomic::{AtomicBool, AtomicU32, Ordering}, + }, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; - use super::BlockEntity; // todo: CustomName, LastExecution, UpdateLastExecution @@ -17,8 +18,8 @@ pub struct CommandBlockEntity { pub condition_met: AtomicBool, pub auto: AtomicBool, pub dirty: AtomicBool, - pub command: Mutex, - pub last_output: Mutex, + pub command: StdMutex, + pub last_output: StdMutex, pub track_output: AtomicBool, pub success_count: AtomicU32, } @@ -26,19 +27,45 @@ pub struct CommandBlockEntity { impl CommandBlockEntity { pub const ID: &'static str = "minecraft:command_block"; #[must_use] - pub fn new(position: BlockPos, track_output: bool, is_chain: bool) -> Self { + pub const fn new(position: BlockPos, track_output: bool, is_chain: bool) -> Self { Self { position, powered: AtomicBool::new(false), condition_met: AtomicBool::new(false), auto: AtomicBool::new(is_chain), dirty: AtomicBool::new(false), - command: Mutex::new(String::new()), - last_output: Mutex::new(String::new()), + command: StdMutex::new(String::new()), + last_output: StdMutex::new(String::new()), track_output: AtomicBool::new(track_output), success_count: AtomicU32::new(0), } } + + fn write_sync_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("auto", self.auto.load(Ordering::SeqCst)); + nbt.put_string( + "Command", + self.command + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .to_string(), + ); + nbt.put_bool("conditionMet", self.condition_met.load(Ordering::SeqCst)); + nbt.put_string( + "LastOutput", + self.last_output + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .to_string(), + ); + nbt.put_bool("powered", self.powered.load(Ordering::SeqCst)); + nbt.put_bool("TrackOutput", self.track_output.load(Ordering::SeqCst)); + nbt.put_bool("UpdateLastExecution", false); + nbt.put_int( + "SuccessCount", + self.success_count.load(Ordering::SeqCst).cast_signed(), + ); + } } impl BlockEntity for CommandBlockEntity { @@ -56,8 +83,8 @@ impl BlockEntity for CommandBlockEntity { let condition_met = AtomicBool::new(nbt.get_bool("conditionMet").unwrap_or(false)); let auto = AtomicBool::new(nbt.get_bool("auto").unwrap_or(false)); let powered = AtomicBool::new(nbt.get_bool("powered").unwrap_or(false)); - let command = Mutex::new(nbt.get_string("Command").unwrap_or("").to_string()); - let last_output = Mutex::new(nbt.get_string("LastOutput").unwrap_or("").to_string()); + let command = StdMutex::new(nbt.get_string("Command").unwrap_or("").to_string()); + let last_output = StdMutex::new(nbt.get_string("LastOutput").unwrap_or("").to_string()); let track_output = AtomicBool::new(nbt.get_bool("TrackOutput").unwrap_or(false)); let success_count = AtomicU32::new(nbt.get_int("SuccessCount").unwrap_or(0).cast_unsigned()); @@ -78,27 +105,15 @@ impl BlockEntity for CommandBlockEntity { fn write_nbt<'a>( &'a self, nbt: &'a mut NbtCompound, - ) -> Pin + Send + 'a>> { + ) -> Pin + Send + 'a>> { Box::pin(async { - nbt.put_bool("auto", self.auto.load(Ordering::SeqCst)); - nbt.put_string("Command", self.command.lock().await.to_string()); - nbt.put_bool("conditionMet", self.condition_met.load(Ordering::SeqCst)); - nbt.put_string("LastOutput", self.last_output.lock().await.to_string()); - nbt.put_bool("powered", self.powered.load(Ordering::SeqCst)); - nbt.put_bool("TrackOutput", self.track_output.load(Ordering::SeqCst)); - nbt.put_bool("UpdateLastExecution", false); - nbt.put_int( - "SuccessCount", - self.success_count.load(Ordering::SeqCst).cast_signed(), - ); + self.write_sync_nbt(nbt); }) } fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - futures::executor::block_on(async { - self.write_nbt(&mut nbt).await; - }); + self.write_sync_nbt(&mut nbt); Some(nbt) } diff --git a/crates/pumpkin/src/block/entities/crafter.rs b/crates/pumpkin/src/block/entities/crafter.rs index 35f6b5ff4..fcb1b28c0 100644 --- a/crates/pumpkin/src/block/entities/crafter.rs +++ b/crates/pumpkin/src/block/entities/crafter.rs @@ -73,8 +73,9 @@ impl BlockEntity for CrafterBlockEntity { fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } nbt.put_int( "crafting_ticks_remaining", self.crafting_ticks_remaining.load(Ordering::Relaxed), diff --git a/crates/pumpkin/src/block/entities/daylight_detector.rs b/crates/pumpkin/src/block/entities/daylight_detector.rs index 9585c95ca..903333458 100644 --- a/crates/pumpkin/src/block/entities/daylight_detector.rs +++ b/crates/pumpkin/src/block/entities/daylight_detector.rs @@ -42,12 +42,10 @@ impl BlockEntity for DaylightDetectorBlockEntity { self } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async { - if world.get_world_age().await % 20 == 0 && world.dimension.has_skylight { - Self::update_power(world, &self.position).await; - } - }) + fn tick(&self, world: &Arc) { + if world.get_world_age() % 20 == 0 && world.dimension.has_skylight { + Self::update_power(world, &self.position); + } } } @@ -59,7 +57,7 @@ impl DaylightDetectorBlockEntity { Self { position } } - pub async fn update_power(world: &Arc, block_pos: &BlockPos) { + pub fn update_power(world: &Arc, block_pos: &BlockPos) { use std::f32::consts::PI; let (block, state) = world.get_block_and_state(block_pos); @@ -82,7 +80,7 @@ impl DaylightDetectorBlockEntity { * is not as accurate as vanilla and doesnt consider weather */ - let time_of_day = world.get_time_of_day().await; + let time_of_day = world.get_time_of_day(); // Sun Angle let sun_angle_fraction = (time_of_day as f32 / 24000.0) - 0.25; @@ -118,10 +116,7 @@ impl DaylightDetectorBlockEntity { if power != props.power { props.power = power; let state = props.to_state_id(block); - world - .clone() - .set_block_state(block_pos, state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(block_pos, state, BlockFlags::NOTIFY_ALL); } } } diff --git a/crates/pumpkin/src/block/entities/decorated_pot.rs b/crates/pumpkin/src/block/entities/decorated_pot.rs index 04ad6c689..7db38731d 100644 --- a/crates/pumpkin/src/block/entities/decorated_pot.rs +++ b/crates/pumpkin/src/block/entities/decorated_pot.rs @@ -86,16 +86,16 @@ impl DecoratedPotBlockEntity { } } - pub async fn get_item(&self) -> Option { - self.item.lock().await.clone() + pub fn get_item(&self) -> Option { + self.item.blocking_lock().clone() } - pub async fn take_item(&self) -> Option { - self.item.lock().await.take() + pub fn take_item(&self) -> Option { + self.item.blocking_lock().take() } - pub async fn try_insert_item(&self, stack: &mut ItemStack, count: u8) -> bool { - let mut item_guard = self.item.lock().await; + pub fn try_insert_item(&self, stack: &mut ItemStack, count: u8) -> bool { + let mut item_guard = self.item.blocking_lock(); if let Some(existing) = item_guard.as_mut() { if existing.item.id == stack.item.id { let add = count.min(64 - existing.item_count); @@ -116,8 +116,8 @@ impl DecoratedPotBlockEntity { } } - pub async fn get_comparator_output(&self) -> u8 { - self.item.lock().await.as_ref().map_or(0, |item| { + pub fn get_comparator_output(&self) -> u8 { + self.item.blocking_lock().as_ref().map_or(0, |item| { if item.item_count == 0 { 0 } else { diff --git a/crates/pumpkin/src/block/entities/dispenser.rs b/crates/pumpkin/src/block/entities/dispenser.rs index c301cec1f..7bcf9f1fa 100644 --- a/crates/pumpkin/src/block/entities/dispenser.rs +++ b/crates/pumpkin/src/block/entities/dispenser.rs @@ -61,8 +61,9 @@ impl BlockEntity for DispenserBlockEntity { fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } Some(nbt) } diff --git a/crates/pumpkin/src/block/entities/dropper.rs b/crates/pumpkin/src/block/entities/dropper.rs index 3cdf52bb8..d978dfc7d 100644 --- a/crates/pumpkin/src/block/entities/dropper.rs +++ b/crates/pumpkin/src/block/entities/dropper.rs @@ -61,8 +61,9 @@ impl BlockEntity for DropperBlockEntity { fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } Some(nbt) } diff --git a/crates/pumpkin/src/block/entities/ender_chest.rs b/crates/pumpkin/src/block/entities/ender_chest.rs index 9269b5927..f040b8531 100644 --- a/crates/pumpkin/src/block/entities/ender_chest.rs +++ b/crates/pumpkin/src/block/entities/ender_chest.rs @@ -7,9 +7,7 @@ use std::any::Any; use std::pin::Pin; use std::sync::Arc; -use crate::block::viewer::{ - ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, -}; +use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt}; use crate::world::World; use super::BlockEntity; @@ -51,12 +49,9 @@ impl BlockEntity for EnderChestBlockEntity { Some(NbtCompound::new()) } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; - }) + fn tick(&self, world: &Arc) { + self.viewers + .update_viewer_count::(self, world, &self.position); } fn as_any(&self) -> &dyn Any { @@ -65,38 +60,16 @@ impl BlockEntity for EnderChestBlockEntity { } impl ViewerCountListener for EnderChestBlockEntity { - fn on_container_open<'a>( - &'a self, - world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, Sound::BlockEnderChestOpen); - }) + fn on_container_open(&self, world: &Arc, _position: &BlockPos) { + self.play_sound(world, Sound::BlockEnderChestOpen); } - fn on_container_close<'a>( - &'a self, - world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - self.play_sound(world, Sound::BlockEnderChestClose); - }) + fn on_container_close(&self, world: &Arc, _position: &BlockPos) { + self.play_sound(world, Sound::BlockEnderChestClose); } - fn on_viewer_count_update<'a>( - &'a self, - world: &'a Arc, - position: &'a BlockPos, - _old: u16, - new: u16, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - world - .add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8) - .await; - }) + fn on_viewer_count_update(&self, world: &Arc, position: &BlockPos, _old: u16, new: u16) { + world.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8); } } diff --git a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs index 99627dee3..b6632fbc4 100644 --- a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs +++ b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs @@ -30,12 +30,8 @@ pub trait CookingBlockEntityBase: fn set_lit_total_time(&self, total_time: u16); fn is_burning(&self) -> bool; - fn can_accept_recipe_output( - &self, - recipe: Option<&CookingRecipe>, - max_count: u8, - ) -> impl Future; - fn craft_recipe(&self, recipe: Option<&CookingRecipe>) -> impl Future; + fn can_accept_recipe_output(&self, recipe: Option<&CookingRecipe>, max_count: u8) -> bool; + fn craft_recipe(&self, recipe: Option<&CookingRecipe>) -> bool; } #[macro_export] @@ -127,13 +123,15 @@ macro_rules! impl_cooking_block_entity_base { total_xp.floor() as i32 } - async fn can_accept_recipe_output( + fn can_accept_recipe_output( &self, recipe: Option<&pumpkin_data::recipes::CookingRecipe>, max_count: u8, ) -> bool { let Some(recipe) = recipe else { return false }; - let items = self.items.read().await; + let Ok(items) = self.items.try_read() else { + return false; + }; let is_top_items_empty = items[0].is_empty(); let side_item_stack = &items[2]; @@ -157,16 +155,14 @@ macro_rules! impl_cooking_block_entity_base { } false } - async fn craft_recipe( - &self, - recipe: Option<&pumpkin_data::recipes::CookingRecipe>, - ) -> bool { - let can_accept_output = self - .can_accept_recipe_output(recipe, self.get_max_count_per_stack()) - .await; + fn craft_recipe(&self, recipe: Option<&pumpkin_data::recipes::CookingRecipe>) -> bool { + let can_accept_output = + self.can_accept_recipe_output(recipe, self.get_max_count_per_stack()); if let Some(recipe) = recipe { if can_accept_output { - let mut items = self.items.write().await; + let Ok(mut items) = self.items.try_write() else { + return false; + }; let Some(output_item) = pumpkin_data::item::Item::from_registry_key( recipe .result @@ -186,18 +182,17 @@ macro_rules! impl_cooking_block_entity_base { // Track recipe usage for XP calculation (vanilla RecipesUsed format) self.add_recipe_used(recipe); - } - let mut items = self.items.write().await; - if items[0].item.id == pumpkin_data::item::Item::WET_SPONGE.id - && !items[1].is_empty() - && items[1].item.id == pumpkin_data::item::Item::BUCKET.id - { - items[1] = ItemStack::new(1, &pumpkin_data::item::Item::WATER_BUCKET); - } + if items[0].item.id == pumpkin_data::item::Item::WET_SPONGE.id + && !items[1].is_empty() + && items[1].item.id == pumpkin_data::item::Item::BUCKET.id + { + items[1] = ItemStack::new(1, &pumpkin_data::item::Item::WATER_BUCKET); + } - items[0].decrement(1); - return true; + items[0].decrement(1); + return true; + } } false @@ -368,169 +363,148 @@ macro_rules! impl_block_entity_for_cooking { ($struct_name:ty,$recipe_kind:expr) => { impl $crate::block::entities::BlockEntity for $struct_name { #[expect(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - world: &'a Arc<$crate::world::World>, - ) -> std::pin::Pin + Send + 'a>> { - Box::pin(async move { - let is_burning = self.is_burning(); - let mut is_dirty = false; - if self.is_burning() { - self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed); - } + fn tick( + &self, + world: &Arc<$crate::world::World>, + ) { + let is_burning = self.is_burning(); + let mut is_dirty = false; + if self.is_burning() { + self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed); + } - let items_guard = self.items.read().await; - let top_item = items_guard[0].clone(); - let bottom_item = items_guard[1].clone(); - drop(items_guard); + let (top_item, bottom_item) = if let Ok(items_guard) = self.items.try_read() { + (items_guard[0].clone(), items_guard[1].clone()) + } else { + return; + }; - let is_top_items_empty = top_item.is_empty(); + let is_top_items_empty = top_item.is_empty(); - let furnace_recipe = pumpkin_data::recipes::get_cooking_recipe_with_ingredient( - top_item.item, - $recipe_kind, - ); + let furnace_recipe = pumpkin_data::recipes::get_cooking_recipe_with_ingredient( + top_item.item, + $recipe_kind, + ); - let can_accept_output = self - .can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack()) - .await; + let can_accept_output = self + .can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack()); - let bottom_items_is_empty = bottom_item.is_empty(); - if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty { - if !self.is_burning() && can_accept_output { - let base_fuel_ticks = - pumpkin_data::fuels::get_item_burn_ticks(bottom_item.item.id) - .unwrap_or(0); + let bottom_items_is_empty = bottom_item.is_empty(); + if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty { + if !self.is_burning() && can_accept_output { + let base_fuel_ticks = + pumpkin_data::fuels::get_item_burn_ticks(bottom_item.item.id) + .unwrap_or(0); - let adjusted_fuel_ticks = if matches!( - $recipe_kind, - CookingRecipeKind::Blasting | CookingRecipeKind::Smoking - ) { - base_fuel_ticks / 2 - } else { - base_fuel_ticks - }; - - let mut burn_event = $crate::plugin::api::events::inventory::furnace_burn::FurnaceBurnEvent::new( - self.position, - bottom_item.item.registry_key.to_string(), - adjusted_fuel_ticks as u32, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut burn_event).await; - } - if burn_event.cancelled { - self.set_lit_time_remaining(0); - } else { - self.set_lit_time_remaining(adjusted_fuel_ticks); - self.set_lit_total_time(adjusted_fuel_ticks); - } - - if self.is_burning() { - is_dirty = true; - let mut items_guard = self.items.write().await; - if !items_guard[1].is_empty() { - items_guard[1].decrement(1); - if let Some(remainder_id) = - pumpkin_data::recipe_remainder::get_recipe_remainder_id( - items_guard[1].item.id, - ) - && items_guard[1].is_empty() - && let Some(remainder_item) = - pumpkin_data::item::Item::from_id(remainder_id) - { - items_guard[1] = ItemStack::new(1, remainder_item); - } - } - } - } - - if self.is_burning() && can_accept_output { - if self.get_cooking_time_spent() == 0 { - let mut start_event = $crate::plugin::api::events::inventory::furnace_start_smelt::FurnaceStartSmeltEvent::new( - self.position, - top_item.item.registry_key.to_string(), - self.get_cooking_total_time() as u32, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut start_event).await; - } - } - self.cooking_time_spent.fetch_add(1, Ordering::Relaxed); - - if self.get_cooking_time_spent() == self.get_cooking_total_time() { - self.set_cooking_time_spent(0); - if let Some(cooking_recipe) = furnace_recipe { - let cooking_total_time = cooking_recipe.cookingtime; - self.set_cooking_total_time(cooking_total_time as u16); - - let mut smelt_event = $crate::plugin::api::events::inventory::furnace_smelt::FurnaceSmeltEvent::new( - self.position, - top_item.item.registry_key.to_string(), - cooking_recipe.result.id.to_string(), - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut smelt_event).await; - } - if !smelt_event.cancelled { - self.craft_recipe(Some(cooking_recipe)).await; - is_dirty = true; - } - } - } + let adjusted_fuel_ticks = if matches!( + $recipe_kind, + CookingRecipeKind::Blasting | CookingRecipeKind::Smoking + ) { + base_fuel_ticks / 2 } else { - self.set_cooking_time_spent(0); - } - } else if !self.is_burning() && self.get_cooking_time_spent() > 0 { - let _ = self.cooking_time_spent.try_update( - Ordering::Acquire, - Ordering::Acquire, - |v| { - Some( - v.saturating_sub(2) - .min(self.cooking_total_time.load(Ordering::Acquire)), - ) - }, - ); - } + base_fuel_ticks + }; - if is_burning != self.is_burning() { - is_dirty = true; - let world = world.clone(); - - let (furnace_block, furnace_block_state) = - world.get_block_and_state(&self.position); - let mut props = - pumpkin_data::block_properties::FurnaceLikeProperties::from_state_id( - furnace_block_state.id, - furnace_block, + if let Some(server) = world.server.upgrade() { + let mut burn_event = $crate::plugin::api::events::inventory::furnace_burn::FurnaceBurnEvent::new( + self.position, + bottom_item.item.registry_key.to_string(), + adjusted_fuel_ticks as u32, ); + server.plugin_manager.fire_blocking(&server, &mut burn_event); + } + self.set_lit_time_remaining(adjusted_fuel_ticks); + self.set_lit_total_time(adjusted_fuel_ticks); if self.is_burning() { - props.lit = true; - world - .set_block_state( - &self.position, - props.to_state_id(furnace_block), - $crate::world::BlockFlags::NOTIFY_ALL, - ) - .await; - } else { - props.lit = false; - world - .set_block_state( - &self.position, - props.to_state_id(furnace_block), - $crate::world::BlockFlags::NOTIFY_ALL, - ) - .await; + is_dirty = true; + if let Ok(mut items_guard) = self.items.try_write() { + if !items_guard[1].is_empty() { + items_guard[1].decrement(1); + if let Some(remainder_id) = + pumpkin_data::recipe_remainder::get_recipe_remainder_id( + items_guard[1].item.id, + ) + && items_guard[1].is_empty() + && let Some(remainder_item) = + pumpkin_data::item::Item::from_id(remainder_id) + { + items_guard[1] = ItemStack::new(1, remainder_item); + } + } + } } } - if is_dirty { - self.mark_dirty(); + if self.is_burning() && can_accept_output { + if self.get_cooking_time_spent() == 0 { + if let Some(server) = world.server.upgrade() { + let mut start_event = $crate::plugin::api::events::inventory::furnace_start_smelt::FurnaceStartSmeltEvent::new( + self.position, + top_item.item.registry_key.to_string(), + self.get_cooking_total_time() as u32, + ); + server.plugin_manager.fire_blocking(&server, &mut start_event); + } + } + self.cooking_time_spent.fetch_add(1, Ordering::Relaxed); + + if self.get_cooking_time_spent() == self.get_cooking_total_time() { + self.set_cooking_time_spent(0); + if let Some(cooking_recipe) = furnace_recipe { + let cooking_total_time = cooking_recipe.cookingtime; + self.set_cooking_total_time(cooking_total_time as u16); + + if let Some(server) = world.server.upgrade() { + let mut smelt_event = $crate::plugin::api::events::inventory::furnace_smelt::FurnaceSmeltEvent::new( + self.position, + top_item.item.registry_key.to_string(), + cooking_recipe.result.id.to_string(), + ); + server.plugin_manager.fire_blocking(&server, &mut smelt_event); + } + self.craft_recipe(Some(cooking_recipe)); + is_dirty = true; + } + } + } else { + self.set_cooking_time_spent(0); } - }) + } else if !self.is_burning() && self.get_cooking_time_spent() > 0 { + let _ = self.cooking_time_spent.try_update( + Ordering::Acquire, + Ordering::Acquire, + |v| { + Some( + v.saturating_sub(2) + .min(self.cooking_total_time.load(Ordering::Acquire)), + ) + }, + ); + } + + if is_burning != self.is_burning() { + is_dirty = true; + + let (furnace_block, furnace_block_state) = + world.get_block_and_state(&self.position); + let mut props = + pumpkin_data::block_properties::FurnaceLikeProperties::from_state_id( + furnace_block_state.id, + furnace_block, + ); + + props.lit = self.is_burning(); + world.set_block_state( + &self.position, + props.to_state_id(furnace_block), + $crate::world::BlockFlags::NOTIFY_ALL, + ); + } + + if is_dirty { + self.mark_dirty(); + } } fn resource_location(&self) -> &'static str { diff --git a/crates/pumpkin/src/block/entities/hanging_sign.rs b/crates/pumpkin/src/block/entities/hanging_sign.rs index 31830a26d..428098ffd 100644 --- a/crates/pumpkin/src/block/entities/hanging_sign.rs +++ b/crates/pumpkin/src/block/entities/hanging_sign.rs @@ -10,7 +10,7 @@ use super::BlockEntity; use crate::block::entities::sign::Text; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; +use std::sync::Mutex; pub struct HangingSignBlockEntity { pub front_text: Text, diff --git a/crates/pumpkin/src/block/entities/hopper.rs b/crates/pumpkin/src/block/entities/hopper.rs index 24abebf9a..dd19426f6 100644 --- a/crates/pumpkin/src/block/entities/hopper.rs +++ b/crates/pumpkin/src/block/entities/hopper.rs @@ -71,19 +71,26 @@ impl BlockEntity for HopperBlockEntity { hopper } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - self.ticked_game_time - .store(world.get_world_age().await, Ordering::Relaxed); - if self.cooldown_time.fetch_sub(1, Ordering::Relaxed) <= 0 { - self.cooldown_time.store(0, Ordering::Relaxed); - let state = HopperLikeProperties::from_state_id( - world.get_block_state(&self.position).id, - &Block::HOPPER, - ); - self.try_move_items(&state, world).await; + fn tick(&self, world: &Arc) { + self.ticked_game_time + .store(world.get_world_age(), Ordering::Relaxed); + if self.cooldown_time.fetch_sub(1, Ordering::Relaxed) <= 0 { + self.cooldown_time.store(0, Ordering::Relaxed); + let state = HopperLikeProperties::from_state_id( + world.get_block_state(&self.position).id, + &Block::HOPPER, + ); + if state.enabled + && let Some(entity) = world.get_block_entity(&self.position) + { + let world = world.clone(); + tokio::spawn(async move { + if let Some(hopper) = entity.as_any().downcast_ref::() { + hopper.try_move_items(&state, &world).await; + } + }); } - }) + } } fn resource_location(&self) -> &'static str { @@ -117,8 +124,9 @@ impl BlockEntity for HopperBlockEntity { "TransferCooldown", NbtTag::Int(self.cooldown_time.load(Ordering::Relaxed)), ); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } Some(nbt) } @@ -204,7 +212,7 @@ impl HopperBlockEntity { let xp = experience_container.extract_experience(); if xp > 0 { let pos = self.position.to_f64(); - ExperienceOrbEntity::spawn(world, pos, xp as u32).await; + ExperienceOrbEntity::spawn(world, pos, xp as u32); } } return true; @@ -223,27 +231,49 @@ impl HopperBlockEntity { let entities = world.get_entities_at_box(&search_box); for entity_base in entities { if let Some(item_entity) = entity_base.clone().get_item_entity() { - let mut stack = item_entity.get_item_stack().lock().await; - if !stack.is_empty() { - let mut pickup_event = crate::plugin::api::events::inventory::inventory_pickup_item::InventoryPickupItemEvent::new( - self.position, - item_entity.get_entity().entity_id, - stack.item.registry_key.to_string(), - ); + let (is_empty, registry_key) = { + let stack = item_entity + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (stack.is_empty(), stack.item.registry_key.to_string()) + }; + if !is_empty { + let mut pickup_event = + crate::plugin::api::events::inventory::inventory_pickup_item::InventoryPickupItemEvent::new( + self.position, + item_entity.get_entity().entity_id, + registry_key, + ); if let Some(server) = world.server.upgrade() { server.plugin_manager.fire(&server, &mut pickup_event).await; } if pickup_event.cancelled { continue; } - let backup = stack.clone(); - let one_item = stack.split(1); - if Self::add_one_item(self, self, one_item).await { + let (backup, one_item, is_empty) = { + let mut stack = item_entity + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if stack.is_empty() { - item_entity.get_entity().remove().await; + continue; + } + let backup = stack.clone(); + let one_item = stack.split(1); + let is_empty = stack.is_empty(); + (backup, one_item, is_empty) + }; + if Self::add_one_item(self, self, one_item).await { + if is_empty { + item_entity.get_entity().remove(); } return true; } + let mut stack = item_entity + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); *stack = backup; } } diff --git a/crates/pumpkin/src/block/entities/jigsaw_block.rs b/crates/pumpkin/src/block/entities/jigsaw_block.rs index b0bd18259..2fabfda69 100644 --- a/crates/pumpkin/src/block/entities/jigsaw_block.rs +++ b/crates/pumpkin/src/block/entities/jigsaw_block.rs @@ -119,16 +119,11 @@ impl JigsawBlockEntity { }; if let Some(structure) = structure { - self.place_structure(world, structure, keep_jigsaws).await; + Self::place_structure(world, &structure, keep_jigsaws); } } - async fn place_structure( - &self, - world: &Arc, - structure: StructurePosition, - keep_jigsaws: bool, - ) { + fn place_structure(world: &Arc, structure: &StructurePosition, keep_jigsaws: bool) { let mut pieces = std::mem::take( &mut structure .collector @@ -148,8 +143,8 @@ impl JigsawBlockEntity { } } placer.finalize(); - world.queue_block_updates(&placer.changed_positions).await; - world.flush_block_updates().await; + world.queue_block_updates(&placer.changed_positions); + world.flush_block_updates(); } } diff --git a/crates/pumpkin/src/block/entities/jukebox.rs b/crates/pumpkin/src/block/entities/jukebox.rs index fbe76dbea..93430b9a5 100644 --- a/crates/pumpkin/src/block/entities/jukebox.rs +++ b/crates/pumpkin/src/block/entities/jukebox.rs @@ -78,22 +78,20 @@ impl BlockEntity for JukeboxBlockEntity { }) } - fn tick<'a>(&'a self, _world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - // Increment ticks if we're playing - let song_length = self.song_length_ticks.load(Ordering::Relaxed); - if song_length > 0 { - let ticks = self - .ticks_since_song_started - .fetch_add(1, Ordering::Relaxed); - // Check if song has finished - if ticks >= song_length { - self.stop_playing(); - // TODO: Update block state to has_record = false? Or just stop redstone? - // In vanilla, the disc stays but music stops and redstone turns off - } + fn tick(&self, _world: &Arc) { + // Increment ticks if we're playing + let song_length = self.song_length_ticks.load(Ordering::Relaxed); + if song_length > 0 { + let ticks = self + .ticks_since_song_started + .fetch_add(1, Ordering::Relaxed); + // Check if song has finished + if ticks >= song_length { + self.stop_playing(); + // TODO: Update block state to has_record = false? Or just stop redstone? + // In vanilla, the disc stays but music stops and redstone turns off } - }) + } } fn is_dirty(&self) -> bool { @@ -140,21 +138,21 @@ impl JukeboxBlockEntity { } /// Get the current record stack - pub async fn get_record(&self) -> ItemStack { - self.record_stack.lock().await.clone() + pub fn get_record(&self) -> ItemStack { + self.record_stack.blocking_lock().clone() } /// Set the record stack - matches vanilla's `setStack()` /// Note: The caller is responsible for updating block state and playing music - pub async fn set_record(&self, stack: ItemStack) { - *self.record_stack.lock().await = stack; + pub fn set_record(&self, stack: ItemStack) { + *self.record_stack.blocking_lock() = stack; self.mark_dirty(); } /// Clear the stack and return what was there - used for dropping - pub async fn clear_record(&self) -> ItemStack { + pub fn clear_record(&self) -> ItemStack { self.stop_playing(); - let mut record = self.record_stack.lock().await; + let mut record = self.record_stack.blocking_lock(); let taken = record.clone(); *record = ItemStack::EMPTY.clone(); self.mark_dirty(); diff --git a/crates/pumpkin/src/block/entities/mob_spawner.rs b/crates/pumpkin/src/block/entities/mob_spawner.rs index e584dee02..8f67aed78 100644 --- a/crates/pumpkin/src/block/entities/mob_spawner.rs +++ b/crates/pumpkin/src/block/entities/mob_spawner.rs @@ -83,7 +83,7 @@ impl MobSpawnerBlockEntity { } impl MobSpawnerBlockEntity { - async fn update_spawns(&self, world: &Arc) { + fn update_spawns(&self, world: &Arc) { let min_delay = self.min_delay; let max_delay = self.max_delay; @@ -95,7 +95,7 @@ impl MobSpawnerBlockEntity { }, Ordering::Relaxed, ); - world.add_synced_block_event(self.position, 1, 0).await; + world.add_synced_block_event(self.position, 1, 0); } pub fn set_entity_type(&self, entity_type: &'static EntityType) { @@ -112,95 +112,93 @@ impl BlockEntity for MobSpawnerBlockEntity { self.position } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(entity_type) = &self.entity_type.load() { - let center = self.position.to_centered_f64(); - let max_player_dist_sq = (self.required_player_range as f64).powi(2); - let player_nearby = world.players.load().iter().any(|p| { - p.get_entity().pos.load().squared_distance_to_vec(¢er) <= max_player_dist_sq - }); + fn tick(&self, world: &Arc) { + if let Some(entity_type) = &self.entity_type.load() { + let center = self.position.to_centered_f64(); + let max_player_dist_sq = (self.required_player_range as f64).powi(2); + let player_nearby = world.players.load().iter().any(|p| { + p.get_entity().pos.load().squared_distance_to_vec(¢er) <= max_player_dist_sq + }); - if !player_nearby { - return; - } - - if self.delay.load(Ordering::Relaxed) < 0 { - self.update_spawns(world).await; - return; - } - if self.delay.load(Ordering::Relaxed) > 0 { - self.delay.fetch_sub(1, Ordering::Relaxed); - return; - } - - let search_radius_horiz = (self.spawn_range * 2) as f64; - let search_radius_vert = 4.0; - let nearby_count = world - .entities - .load() - .iter() - .filter(|e| { - let ent = e.get_entity(); - if ent.entity_type.id != entity_type.id { - return false; - } - let pos = ent.pos.load(); - (pos.x - center.x).abs() <= search_radius_horiz - && (pos.z - center.z).abs() <= search_radius_horiz - && (pos.y - center.y).abs() <= search_radius_vert - }) - .count(); - - if nearby_count as i32 >= self.max_nearby_entities { - self.update_spawns(world).await; - return; - } - - let spawn_range = self.spawn_range; - let mut spawned_any = false; - for _ in 0..self.spawn_count { - let pos = self.position.0; - - let spawn_pos = Vector3::new( - pos.x as f64 - + (rand::random::() - rand::random::()) * spawn_range as f64 - + 0.5, - (pos.y + rand::random_range(0..3) - 1) as f64, - pos.z as f64 - + (rand::random::() - rand::random::()) * spawn_range as f64 - + 0.5, - ); - // TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes - if !world.is_space_empty(BoundingBox::new_from_pos( - spawn_pos.x, - spawn_pos.y, - spawn_pos.z, - &EntityDimensions { - width: entity_type.dimension[0], - height: entity_type.dimension[1], - eye_height: entity_type.eye_height, - }, - )) { - continue; - } - let entity = crate::entity::r#type::from_type( - entity_type, - spawn_pos, - world, - uuid::Uuid::new_v4(), - ); - let yaw = rand::random::() * 360.0; - entity.get_entity().set_rotation(yaw, 0.0); - world.spawn_entity(entity).await; - world.sync_world_event(WorldEvent::ParticlesMobblockSpawn, self.position, 0); - spawned_any = true; - } - if spawned_any { - self.update_spawns(world).await; - } + if !player_nearby { + return; } - }) + + if self.delay.load(Ordering::Relaxed) < 0 { + self.update_spawns(world); + return; + } + if self.delay.load(Ordering::Relaxed) > 0 { + self.delay.fetch_sub(1, Ordering::Relaxed); + return; + } + + let search_radius_horiz = (self.spawn_range * 2) as f64; + let search_radius_vert = 4.0; + let nearby_count = world + .entities + .load() + .iter() + .filter(|e| { + let ent = e.get_entity(); + if ent.entity_type.id != entity_type.id { + return false; + } + let pos = ent.pos.load(); + (pos.x - center.x).abs() <= search_radius_horiz + && (pos.z - center.z).abs() <= search_radius_horiz + && (pos.y - center.y).abs() <= search_radius_vert + }) + .count(); + + if nearby_count as i32 >= self.max_nearby_entities { + self.update_spawns(world); + return; + } + + let spawn_range = self.spawn_range; + let mut spawned_any = false; + for _ in 0..self.spawn_count { + let pos = self.position.0; + + let spawn_pos = Vector3::new( + pos.x as f64 + + (rand::random::() - rand::random::()) * spawn_range as f64 + + 0.5, + (pos.y + rand::random_range(0..3) - 1) as f64, + pos.z as f64 + + (rand::random::() - rand::random::()) * spawn_range as f64 + + 0.5, + ); + // TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes + if !world.is_space_empty(BoundingBox::new_from_pos( + spawn_pos.x, + spawn_pos.y, + spawn_pos.z, + &EntityDimensions { + width: entity_type.dimension[0], + height: entity_type.dimension[1], + eye_height: entity_type.eye_height, + }, + )) { + continue; + } + let entity = crate::entity::r#type::from_type( + entity_type, + spawn_pos, + world, + uuid::Uuid::new_v4(), + ); + let yaw = rand::random::() * 360.0; + entity.get_entity().set_rotation(yaw, 0.0); + world.spawn_entity(entity); + world.sync_world_event(WorldEvent::ParticlesMobblockSpawn, self.position, 0); + spawned_any = true; + } + if spawned_any { + self.update_spawns(world); + } + } } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self diff --git a/crates/pumpkin/src/block/entities/mod.rs b/crates/pumpkin/src/block/entities/mod.rs index 921944b52..00e2409aa 100644 --- a/crates/pumpkin/src/block/entities/mod.rs +++ b/crates/pumpkin/src/block/entities/mod.rs @@ -76,9 +76,7 @@ pub trait BlockEntity: Any + Send + Sync { fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self where Self: Sized; - fn tick<'a>(&'a self, _world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async {}) - } + fn tick(&self, _world: &Arc) {} fn resource_location(&self) -> &'static str; fn get_position(&self) -> BlockPos; diff --git a/crates/pumpkin/src/block/entities/piston.rs b/crates/pumpkin/src/block/entities/piston.rs index d2268d067..725c7f997 100644 --- a/crates/pumpkin/src/block/entities/piston.rs +++ b/crates/pumpkin/src/block/entities/piston.rs @@ -81,7 +81,7 @@ impl PistonBlockEntity { for entity in world.get_entities_at_box(&swept) { let e = entity.get_entity(); - if e.no_clip.load(Ordering::Relaxed) { + if e.no_physics.load(Ordering::Relaxed) { continue; } // Player movement is client-authoritative; vanilla still nudges them @@ -191,7 +191,7 @@ impl PistonBlockEntity { ) } - pub async fn finish(&self, world: Arc) { + pub fn finish(&self, world: &Arc) { if self.last_progress.load() < 1.0 { let pos = self.position; world.remove_block_entity(&pos); @@ -199,16 +199,10 @@ impl PistonBlockEntity { let state = if self.source { Block::AIR.default_state.id } else { - world - .clone() - .update_from_neighbor_shapes(self.pushed_block_state.id, &pos) - .await + world.update_from_neighbor_shapes(self.pushed_block_state.id, &pos) }; - world - .clone() - .set_block_state(&pos, state, BlockFlags::NOTIFY_ALL) - .await; - world.update_neighbors(&pos, None).await; + world.set_block_state(&pos, state, BlockFlags::NOTIFY_ALL); + world.update_neighbors(&pos, None); } } } @@ -228,45 +222,35 @@ impl BlockEntity for PistonBlockEntity { self.position } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - let current_progress = self.current_progress.load(); - self.last_progress.store(current_progress); - if current_progress >= 1.0 { - let pos = self.position; - world.remove_block_entity(&pos); - if world.get_block(&pos) == &Block::MOVING_PISTON { - if self.pushed_block_state.is_air() { - world - .clone() - .set_block_state( - &pos, - self.pushed_block_state.id, - BlockFlags::FORCE_STATE | BlockFlags::MOVED, - ) - .await; - } else { - let updated_state = world - .clone() - .update_from_neighbor_shapes(self.pushed_block_state.id, &pos) - .await; - world - .clone() - .set_block_state( - &pos, - updated_state, - BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, - ) - .await; - world.clone().update_neighbors(&pos, None).await; - } + fn tick(&self, world: &Arc) { + let current_progress = self.current_progress.load(); + self.last_progress.store(current_progress); + if current_progress >= 1.0 { + let pos = self.position; + world.remove_block_entity(&pos); + if world.get_block(&pos) == &Block::MOVING_PISTON { + if self.pushed_block_state.is_air() { + world.set_block_state( + &pos, + self.pushed_block_state.id, + BlockFlags::FORCE_STATE | BlockFlags::MOVED, + ); + } else { + let updated_state = + world.update_from_neighbor_shapes(self.pushed_block_state.id, &pos); + world.set_block_state( + &pos, + updated_state, + BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, + ); + world.update_neighbors(&pos, None); } - return; } - let new_progress = (current_progress + 0.5).min(1.0); - self.push_entities(world, new_progress); - self.current_progress.store(new_progress); - }) + return; + } + let new_progress = (current_progress + 0.5).min(1.0); + self.push_entities(world, new_progress); + self.current_progress.store(new_progress); } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self diff --git a/crates/pumpkin/src/block/entities/shelf.rs b/crates/pumpkin/src/block/entities/shelf.rs index b44c9826c..d8c26fb81 100644 --- a/crates/pumpkin/src/block/entities/shelf.rs +++ b/crates/pumpkin/src/block/entities/shelf.rs @@ -60,8 +60,9 @@ impl BlockEntity for ShelfBlockEntity { fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } Some(nbt) } diff --git a/crates/pumpkin/src/block/entities/shulker_box.rs b/crates/pumpkin/src/block/entities/shulker_box.rs index 17e140e47..de5ef8306 100644 --- a/crates/pumpkin/src/block/entities/shulker_box.rs +++ b/crates/pumpkin/src/block/entities/shulker_box.rs @@ -10,9 +10,7 @@ use std::{array::from_fn, sync::Arc}; use tokio::sync::RwLock; use crate::block::entities::BlockEntity; -use crate::block::viewer::{ - ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, -}; +use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt}; use crate::world::World; use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; @@ -57,12 +55,9 @@ impl BlockEntity for ShulkerBoxBlockEntity { self.write_inventory_nbt(nbt, true) } - fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { - Box::pin(async move { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; - }) + fn tick(&self, world: &Arc) { + self.viewers + .update_viewer_count::(self, world, &self.position); } fn on_block_replaced<'a>( @@ -92,8 +87,9 @@ impl BlockEntity for ShulkerBoxBlockEntity { fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); - let items = futures::executor::block_on(self.items.read()); - sync_write_items_to_nbt(items.as_slice(), &mut nbt); + if let Ok(items) = self.items.try_read() { + sync_write_items_to_nbt(items.as_slice(), &mut nbt); + } Some(nbt) } @@ -103,40 +99,18 @@ impl BlockEntity for ShulkerBoxBlockEntity { } impl ViewerCountListener for ShulkerBoxBlockEntity { - fn on_container_open<'a>( - &'a self, - world: &'a Arc, - position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - Self::play_sound(world, position, 1); - // TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos); - }) + fn on_container_open(&self, world: &Arc, position: &BlockPos) { + Self::play_sound(world, position, 1); + // TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos); } - fn on_container_close<'a>( - &'a self, - world: &'a Arc, - position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - Self::play_sound(world, position, 0); - // TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos); - }) + fn on_container_close(&self, world: &Arc, position: &BlockPos) { + Self::play_sound(world, position, 0); + // TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos); } - fn on_viewer_count_update<'a>( - &'a self, - world: &'a Arc, - position: &'a BlockPos, - _old: u16, - new: u16, - ) -> ViewerFuture<'a, ()> { - Box::pin(async move { - world - .add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8) - .await; - }) + fn on_viewer_count_update(&self, world: &Arc, position: &BlockPos, _old: u16, new: u16) { + world.add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8); } } diff --git a/crates/pumpkin/src/block/entities/sign.rs b/crates/pumpkin/src/block/entities/sign.rs index 8d3af98ed..880c1e64c 100644 --- a/crates/pumpkin/src/block/entities/sign.rs +++ b/crates/pumpkin/src/block/entities/sign.rs @@ -9,7 +9,7 @@ use std::{ use super::BlockEntity; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; +use std::sync::Mutex; pub use pumpkin_data::dye_color::DyeColor; diff --git a/crates/pumpkin/src/block/entities/vault.rs b/crates/pumpkin/src/block/entities/vault.rs index 233ebb325..de5eb09c2 100644 --- a/crates/pumpkin/src/block/entities/vault.rs +++ b/crates/pumpkin/src/block/entities/vault.rs @@ -81,11 +81,11 @@ impl VaultBlockEntity { } } - pub async fn has_rewarded(&self, player_id: &Uuid) -> bool { - self.rewarded_players.lock().await.contains(player_id) + pub fn has_rewarded(&self, player_id: &Uuid) -> bool { + self.rewarded_players.blocking_lock().contains(player_id) } - pub async fn mark_rewarded(&self, player_id: Uuid) { - self.rewarded_players.lock().await.insert(player_id); + pub fn mark_rewarded(&self, player_id: Uuid) { + self.rewarded_players.blocking_lock().insert(player_id); } } diff --git a/crates/pumpkin/src/block/fluid/flowing_trait.rs b/crates/pumpkin/src/block/fluid/flowing_trait.rs index 3daf6bc22..445782c6d 100644 --- a/crates/pumpkin/src/block/fluid/flowing_trait.rs +++ b/crates/pumpkin/src/block/fluid/flowing_trait.rs @@ -1,5 +1,5 @@ use super::{pathfinder, physics}; -use crate::{block::BlockFuture, world::World}; +use crate::world::World; use pumpkin_data::{ Block, BlockDirection, BlockStateId, fluid::{EnumVariants, Falling, Fluid, FluidProperties, Level}, @@ -8,8 +8,8 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::{tick::TickPriority, world::BlockFlags}; use std::sync::Arc; pub type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; -pub type FluidFuture<'a, T> = BlockFuture<'a, T>; +#[allow(async_fn_in_trait)] pub trait FlowingFluid: Send + Sync { fn get_level_decrease_per_block(&self, world: &World) -> i32; fn get_flow_speed(&self, world: &World) -> u8; @@ -72,75 +72,63 @@ pub trait FlowingFluid: Send + Sync { /// 3. Triggering fluid spread to adjacent positions /// /// Sources (level 8, non-falling) always spread without state changes. - fn on_scheduled_tick_internal<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - ) -> impl std::future::Future + Send + 'a { - async move { - //let block = world.get_block(block_pos); - let current_block_state_id = world.get_block_state_id(block_pos); - let block = Block::from_state_id(current_block_state_id); + async fn on_scheduled_tick_internal( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, + ) { + let current_block_state_id = world.get_block_state_id(block_pos); + let block = Block::from_state_id(current_block_state_id); - if !self.has_fluid_at(fluid, current_block_state_id) { - return; - } - - let waterlogged = block.is_waterlogged(current_block_state_id); - let Some(current_fluid_state) = self.get_effective_props(fluid, current_block_state_id) - else { - return; - }; - let is_source = current_fluid_state.level == Level::L8 - && current_fluid_state.falling != Falling::True; - let state_for_spreading: FlowingFluidProperties; - - // Update state if non-source - if !is_source && !waterlogged { - let new_fluid_state = self.get_new_liquid(world, fluid, block_pos).await; - - if let Some(new_state) = new_fluid_state { - let new_state_id = new_state.to_state_id(fluid); - - if new_state_id != current_block_state_id { - world - .set_block_state(block_pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; - - // Schedule next tick for this position - let tick_delay = self.get_flow_speed(world); - world.schedule_fluid_tick( - fluid, - *block_pos, - tick_delay, - TickPriority::Normal, - ); - } - - // Use the new state for spreading - state_for_spreading = new_state; - } else { - if !waterlogged { - world - .set_block_state( - block_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - return; // Don't spread if fluid is gone - } - } else { - // Sources use their current state - state_for_spreading = current_fluid_state; - } - - // Then, spread using the appropriate state - self.try_flow(world, fluid, block_pos, &state_for_spreading) - .await; + if !self.has_fluid_at(fluid, current_block_state_id) { + return; } + + let waterlogged = block.is_waterlogged(current_block_state_id); + let Some(current_fluid_state) = self.get_effective_props(fluid, current_block_state_id) + else { + return; + }; + let is_source = + current_fluid_state.level == Level::L8 && current_fluid_state.falling != Falling::True; + let state_for_spreading: FlowingFluidProperties; + + // Update state if non-source + if !is_source && !waterlogged { + let new_fluid_state = self.get_new_liquid(world, fluid, block_pos).await; + + if let Some(new_state) = new_fluid_state { + let new_state_id = new_state.to_state_id(fluid); + + if new_state_id != current_block_state_id { + world.set_block_state(block_pos, new_state_id, BlockFlags::NOTIFY_ALL); + + // Schedule next tick for this position + let tick_delay = self.get_flow_speed(world); + world.schedule_fluid_tick(fluid, *block_pos, tick_delay, TickPriority::Normal); + } + + // Use the new state for spreading + state_for_spreading = new_state; + } else { + if !waterlogged { + world.set_block_state( + block_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + return; // Don't spread if fluid is gone + } + } else { + // Sources use their current state + state_for_spreading = current_fluid_state; + } + + // Then, spread using the appropriate state + self.try_flow(world, fluid, block_pos, &state_for_spreading) + .await; } /// Attempts to flow fluid from a position, prioritizing downward flow. @@ -150,65 +138,61 @@ pub trait FlowingFluid: Send + Sync { /// 2. Sides - spread horizontally using pathfinding /// /// Sources with 3+ adjacent sources also spread to sides when flowing down. - fn try_flow<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - props: &'a FlowingFluidProperties, - ) -> impl std::future::Future + Send + 'a { - async move { - let below_pos = block_pos.down(); - let below_state = world.get_block_state(&below_pos); - let below_block = Block::from_state_id(below_state.id); - let is_hole = physics::can_be_replaced(below_state, below_block, fluid); + async fn try_flow( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, + props: &FlowingFluidProperties, + ) { + let below_pos = block_pos.down(); + let below_state = world.get_block_state(&below_pos); + let below_block = Block::from_state_id(below_state.id); + let is_hole = physics::can_be_replaced(below_state, below_block, fluid); - // Try to flow down first - if is_hole { - let falling_props = self.get_flowing(fluid, Level::L8, true); - self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid)) - .await; + // Try to flow down first + if is_hole { + let falling_props = self.get_flowing(fluid, Level::L8, true); + self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid)) + .await; - // Check if we should also spread to sides - if props.level == Level::L8 && props.falling == Falling::False { - let source_count = self.count_source_neighbors(world, fluid, block_pos).await; - if source_count >= 3 { - self.flow_to_sides(world, fluid, block_pos, props).await; - } + // Check if we should also spread to sides + if props.level == Level::L8 && props.falling == Falling::False { + let source_count = self.count_source_neighbors(world, fluid, block_pos).await; + if source_count >= 3 { + self.flow_to_sides(world, fluid, block_pos, props).await; } - return; } - - // Check if fluid should flow to the side(s) - self.flow_to_sides(world, fluid, block_pos, props).await; + return; } + + // Check if fluid should flow to the side(s) + self.flow_to_sides(world, fluid, block_pos, props).await; } - fn count_source_neighbors<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - ) -> impl std::future::Future + Send + 'a { - async move { - let mut count = 0; - for direction in [ - BlockDirection::North, - BlockDirection::South, - BlockDirection::West, - BlockDirection::East, - ] { - let neighbor_pos = block_pos.offset(direction.to_offset()); - let neighbor_id = world.get_block_state_id(&neighbor_pos); - if self - .get_effective_props(fluid, neighbor_id) - .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False) - { - count += 1; - } + async fn count_source_neighbors( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, + ) -> i32 { + let mut count = 0; + for direction in [ + BlockDirection::North, + BlockDirection::South, + BlockDirection::West, + BlockDirection::East, + ] { + let neighbor_pos = block_pos.offset(direction.to_offset()); + let neighbor_id = world.get_block_state_id(&neighbor_pos); + if self + .get_effective_props(fluid, neighbor_id) + .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False) + { + count += 1; } - count } + count } /// Calculates the new fluid state for a position based on neighbors and environment. @@ -221,87 +205,84 @@ pub trait FlowingFluid: Send + Sync { /// /// # Returns /// New fluid properties, or None if fluid should drain - fn get_new_liquid<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - ) -> impl std::future::Future> + Send + 'a { - async move { - let current_state_id = world.get_block_state_id(block_pos); - let current_props = FlowingFluidProperties::from_state_id(current_state_id, fluid); + async fn get_new_liquid( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, + ) -> Option { + let current_state_id = world.get_block_state_id(block_pos); + let current_props = FlowingFluidProperties::from_state_id(current_state_id, fluid); - // Sources never change - if current_props.level == Level::L8 && current_props.falling != Falling::True { - return Some(current_props); + // Sources never change + if current_props.level == Level::L8 && current_props.falling != Falling::True { + return Some(current_props); + } + + // First: check horizontal neighbors for infinite source formation + let mut highest_neighbor = 0; + let mut neighbor_source_count = 0; + for direction in [ + BlockDirection::North, + BlockDirection::South, + BlockDirection::West, + BlockDirection::East, + ] { + let neighbor_pos = block_pos.offset(direction.to_offset()); + let neighbor_state_id = world.get_block_state_id(&neighbor_pos); + let Some(neighbor_props) = self.get_effective_props(fluid, neighbor_state_id) else { + continue; + }; + + // Count horizontal non-falling sources for infinite source formation + if neighbor_props.level == Level::L8 && neighbor_props.falling == Falling::False { + neighbor_source_count += 1; } - // First: check horizontal neighbors for infinite source formation - let mut highest_neighbor = 0; - let mut neighbor_source_count = 0; - for direction in [ - BlockDirection::North, - BlockDirection::South, - BlockDirection::West, - BlockDirection::East, - ] { - let neighbor_pos = block_pos.offset(direction.to_offset()); - let neighbor_state_id = world.get_block_state_id(&neighbor_pos); - let Some(neighbor_props) = self.get_effective_props(fluid, neighbor_state_id) - else { - continue; - }; - - // Count horizontal non-falling sources for infinite source formation - if neighbor_props.level == Level::L8 && neighbor_props.falling == Falling::False { - neighbor_source_count += 1; - } - - // Falling water from the side counts as level 8 - let neighbor_level = if neighbor_props.falling == Falling::True { - 8 - } else { - i32::from(neighbor_props.level.to_index()) + 1 - }; - - highest_neighbor = highest_neighbor.max(neighbor_level); - } - - // Attempt infinite source formation first - if self.can_convert_to_source(world) && neighbor_source_count >= 2 { - let below_pos = block_pos.down(); - let below_state = world.get_block_state(&below_pos); - let below_state_id = below_state.id; - - // Check if block below is a stable source of the same fluid - let below_is_same_source = self - .get_effective_props(fluid, below_state_id) - .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False); - - // If the block below is solid (solid block) or a source of same fluid, form a source here. - if below_is_same_source || below_state.is_solid_block() { - return Some(self.get_source(fluid, false)); - } - // Otherwise continue to standard falling/flowing logic - } - - // Then: if there's water above, this block is ALWAYS level 8, falling=true - let above_pos = block_pos.up(); - let above_state_id = world.get_block_state_id(&above_pos); - - if self.has_fluid_at(fluid, above_state_id) { - return Some(self.get_flowing(fluid, Level::L8, true)); - } - - // Standard flowing calculation - let drop_off = self.get_level_decrease_per_block(world); - let new_level = highest_neighbor - drop_off; - - if new_level <= 0 { - None + // Falling water from the side counts as level 8 + let neighbor_level = if neighbor_props.falling == Falling::True { + 8 } else { - Some(self.get_flowing(fluid, Level::from_index(new_level as u16 - 1), false)) + i32::from(neighbor_props.level.to_index()) + 1 + }; + + highest_neighbor = highest_neighbor.max(neighbor_level); + } + + // Attempt infinite source formation first + if self.can_convert_to_source(world) && neighbor_source_count >= 2 { + let below_pos = block_pos.down(); + let below_state = world.get_block_state(&below_pos); + let below_state_id = below_state.id; + + // Check if block below is a stable source of the same fluid + let below_is_same_source = self + .get_effective_props(fluid, below_state_id) + .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False); + + // If the block below is solid (solid block) or a source of same fluid, form a source here. + if below_is_same_source || below_state.is_solid_block() { + return Some(self.get_source(fluid, false)); } + // Otherwise continue to standard falling/flowing logic + } + + // Then: if there's water above, this block is ALWAYS level 8, falling=true + let above_pos = block_pos.up(); + let above_state_id = world.get_block_state_id(&above_pos); + + if self.has_fluid_at(fluid, above_state_id) { + return Some(self.get_flowing(fluid, Level::L8, true)); + } + + // Standard flowing calculation + let drop_off = self.get_level_decrease_per_block(world); + let new_level = highest_neighbor - drop_off; + + if new_level <= 0 { + None + } else { + Some(self.get_flowing(fluid, Level::from_index(new_level as u16 - 1), false)) } } @@ -314,82 +295,29 @@ pub trait FlowingFluid: Send + Sync { /// - Fluid tick scheduling for non-source blocks /// /// Called by `spread_to` implementations after fluid-specific pre-checks. - fn apply_spread<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - pos: &'a BlockPos, + async fn apply_spread( + &self, + world: &Arc, + fluid: &Fluid, + pos: &BlockPos, state_id: BlockStateId, new_props: FlowingFluidProperties, - ) -> impl std::future::Future + Send + 'a { - async move { - let current_state_id = world.get_block_state_id(pos); - if let Some(current_props) = self.get_effective_props(fluid, current_state_id) { - let current_level = i32::from(current_props.level.to_index()) + 1; - let new_level = i32::from(new_props.level.to_index()) + 1; - let current_is_source = - current_props.level == Level::L8 && current_props.falling == Falling::False; - let new_is_source = - new_props.level == Level::L8 && new_props.falling == Falling::False; + ) { + let current_state_id = world.get_block_state_id(pos); + if let Some(current_props) = self.get_effective_props(fluid, current_state_id) { + let current_level = i32::from(current_props.level.to_index()) + 1; + let new_level = i32::from(new_props.level.to_index()) + 1; + let current_is_source = + current_props.level == Level::L8 && current_props.falling == Falling::False; + let new_is_source = new_props.level == Level::L8 && new_props.falling == Falling::False; - // Never overwrite a source with anything - if current_is_source { - return; - } - - // Check for infinite source formation before quiescence checks - if !current_is_source && self.can_convert_to_source(world) { - let should_convert = self - .check_infinite_source_formation(world, fluid, pos) - .await; - - if should_convert { - let source_props = self.get_source(fluid, false); - let source_state_id = source_props.to_state_id(fluid); - world - .set_block_state(pos, source_state_id, BlockFlags::NOTIFY_ALL) - .await; - - // Sources don't need ticks - return; - } - } - - // If new is a source, always accept it - if new_is_source { - // Continue to set state below - } else if current_props.falling == new_props.falling { - // Same falling state - check level - if new_level <= current_level { - return; - } - } - } else { - // Replace non-fluid blocks - let block = world.get_block(pos); - if block.id != Block::AIR.id { - world.break_block(pos, None, BlockFlags::NOTIFY_ALL).await; - } - } - - let mut event = crate::plugin::api::events::block::block_from_to::BlockFromToEvent::new( - *pos, - *pos, - &pumpkin_data::Block::WATER, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { + // Never overwrite a source with anything + if current_is_source { return; } - world - .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) - .await; - - // Check for infinite source formation after placing new fluid - if self.can_convert_to_source(world) { + // Check for infinite source formation before quiescence checks + if !current_is_source && self.can_convert_to_source(world) { let should_convert = self .check_infinite_source_formation(world, fluid, pos) .await; @@ -397,22 +325,66 @@ pub trait FlowingFluid: Send + Sync { if should_convert { let source_props = self.get_source(fluid, false); let source_state_id = source_props.to_state_id(fluid); - world - .set_block_state(pos, source_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, source_state_id, BlockFlags::NOTIFY_ALL); // Sources don't need ticks return; } } - // Only schedule tick if not a source - let is_source = new_props.level == Level::L8 && new_props.falling == Falling::False; - - if !is_source { - let tick_delay = self.get_flow_speed(world); - world.schedule_fluid_tick(fluid, *pos, tick_delay, TickPriority::Normal); + // If new is a source, always accept it + if new_is_source { + // Continue to set state below + } else if current_props.falling == new_props.falling { + // Same falling state - check level + if new_level <= current_level { + return; + } } + } else { + // Replace non-fluid blocks + let block = world.get_block(pos); + if block.id != Block::AIR.id { + world.break_block(pos, None, BlockFlags::NOTIFY_ALL); + } + } + + let mut event = crate::plugin::api::events::block::block_from_to::BlockFromToEvent::new( + *pos, + *pos, + &pumpkin_data::Block::WATER, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + + world.set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL); + + // Check for infinite source formation after placing new fluid + if self.can_convert_to_source(world) { + let should_convert = self + .check_infinite_source_formation(world, fluid, pos) + .await; + + if should_convert { + let source_props = self.get_source(fluid, false); + let source_state_id = source_props.to_state_id(fluid); + world.set_block_state(pos, source_state_id, BlockFlags::NOTIFY_ALL); + + // Sources don't need ticks + return; + } + } + + // Only schedule tick if not a source + let is_source = new_props.level == Level::L8 && new_props.falling == Falling::False; + + if !is_source { + let tick_delay = self.get_flow_speed(world); + world.schedule_fluid_tick(fluid, *pos, tick_delay, TickPriority::Normal); } } @@ -424,101 +396,95 @@ pub trait FlowingFluid: Send + Sync { /// /// # Returns /// `true` if position should convert to a source block - fn check_infinite_source_formation<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - pos: &'a BlockPos, - ) -> impl std::future::Future + Send + 'a { - async move { - // Count adjacent horizontal source blocks - let mut source_count = 0; - for direction in [ - BlockDirection::North, - BlockDirection::South, - BlockDirection::West, - BlockDirection::East, - ] { - let neighbor_pos = pos.offset(direction.to_offset()); - let neighbor_state_id = world.get_block_state_id(&neighbor_pos); + async fn check_infinite_source_formation( + &self, + world: &Arc, + fluid: &Fluid, + pos: &BlockPos, + ) -> bool { + // Count adjacent horizontal source blocks + let mut source_count = 0; + for direction in [ + BlockDirection::North, + BlockDirection::South, + BlockDirection::West, + BlockDirection::East, + ] { + let neighbor_pos = pos.offset(direction.to_offset()); + let neighbor_state_id = world.get_block_state_id(&neighbor_pos); - if self - .get_effective_props(fluid, neighbor_state_id) - .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False) - { - source_count += 1; - } + if self + .get_effective_props(fluid, neighbor_state_id) + .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False) + { + source_count += 1; } - - // Need at least 2 source neighbors - if source_count < 2 { - return false; - } - - // Check the block below - let below_pos = pos.down(); - let below_state = world.get_block_state(&below_pos); - let below_state_id = below_state.id; - - // Check if block below is a stable source of the same fluid - let below_is_same_source = self - .get_effective_props(fluid, below_state_id) - .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False); - - // Convert to source if below is solid or a source of same fluid - below_is_same_source || below_state.is_solid_block() } + + // Need at least 2 source neighbors + if source_count < 2 { + return false; + } + + // Check the block below + let below_pos = pos.down(); + let below_state = world.get_block_state(&below_pos); + let below_state_id = below_state.id; + + // Check if block below is a stable source of the same fluid + let below_is_same_source = self + .get_effective_props(fluid, below_state_id) + .is_some_and(|p| p.level == Level::L8 && p.falling == Falling::False); + + // Convert to source if below is solid or a source of same fluid + below_is_same_source || below_state.is_solid_block() } /// Spreads fluid to a target position with the given state. /// /// Default implementation delegates to `apply_spread`. Implementations like /// lava can override to add fluid-specific logic (e.g., water -> stone conversion). - fn spread_to<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - pos: &'a BlockPos, + async fn spread_to( + &self, + world: &Arc, + fluid: &Fluid, + pos: &BlockPos, state_id: BlockStateId, - ) -> impl std::future::Future + Send + 'a { - async move { - let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); - self.apply_spread(world, fluid, pos, state_id, new_props) - .await; - } + ) { + let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); + self.apply_spread(world, fluid, pos, state_id, new_props) + .await; } /// Spreads fluid horizontally to adjacent positions using pathfinding. /// /// Uses `get_spread` to find optimal flow directions (shortest distance to holes) /// and the computed fluid state for each target position. - fn flow_to_sides<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - props: &'a FlowingFluidProperties, - ) -> impl std::future::Future + Send + 'a { - async move { - let drop_off = self.get_level_decrease_per_block(world); - let current_level = i32::from(props.level.to_index()) + 1; - let effective_level = if props.falling == Falling::True { - 7 - } else { - current_level - drop_off - }; + async fn flow_to_sides( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, + props: &FlowingFluidProperties, + ) { + let drop_off = self.get_level_decrease_per_block(world); + let current_level = i32::from(props.level.to_index()) + 1; + let effective_level = if props.falling == Falling::True { + 7 + } else { + current_level - drop_off + }; - if effective_level <= 0 { - return; - } + if effective_level <= 0 { + return; + } - let (spread_dirs, count) = pathfinder::get_spread(self, world, fluid, block_pos).await; + let (spread_dirs, count) = pathfinder::get_spread(self, world, fluid, block_pos).await; - for &(direction, state_id) in spread_dirs.iter().take(count) { - let side_pos = block_pos.offset(direction.to_offset()); + for &(direction, state_id) in spread_dirs.iter().take(count) { + let side_pos = block_pos.offset(direction.to_offset()); - self.spread_to(world, fluid, &side_pos, state_id).await; - } + self.spread_to(world, fluid, &side_pos, state_id).await; } } } diff --git a/crates/pumpkin/src/block/fluid/lava.rs b/crates/pumpkin/src/block/fluid/lava.rs index 652490609..fc17273ca 100644 --- a/crates/pumpkin/src/block/fluid/lava.rs +++ b/crates/pumpkin/src/block/fluid/lava.rs @@ -1,6 +1,6 @@ use super::flowing_trait::FlowingFluid; use crate::{ - block::{BlockFuture, FluidMetadata, blocks::fire::fire::FireBlock, fluid::FluidBehaviour}, + block::{FluidMetadata, blocks::fire::fire::FireBlock, fluid::FluidBehaviour}, entity::EntityBase, world::World, }; @@ -80,22 +80,16 @@ impl FlowingLava { .all(|dir| world.is_loaded(&pos.offset(dir.to_offset()))) } - async fn ignite_fire_if_possible(world: &Arc, pos: &BlockPos) { + fn ignite_fire_if_possible(world: &Arc, pos: &BlockPos) { if !Self::can_resolve_fire_state_without_loading(world, pos) { return; } let fire_state_id = FireBlock.get_state_for_position(world.as_ref(), &Block::FIRE, pos); - world - .set_block_state(pos, fire_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, fire_state_id, BlockFlags::NOTIFY_ALL); } - async fn receive_neighbor_fluids( - world: &Arc, - _fluid: &Fluid, - block_pos: &BlockPos, - ) -> bool { + fn receive_neighbor_fluids(world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) -> bool { // Logic to determine if we should replace the fluid with any of (cobble, obsidian, stone, etc.) let below_is_soul_soil = world .get_block(&block_pos.offset(BlockDirection::Down.to_offset())) @@ -113,13 +107,11 @@ impl FlowingLava { } else { Block::COBBLESTONE }; - world - .set_block_state( - block_pos, - block.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + block_pos, + block.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); world.sync_world_event(WorldEvent::LavaFizz, *block_pos, 0); return false; } @@ -130,18 +122,16 @@ impl FlowingLava { *block_pos, &Block::BASALT, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return false; } } - world - .set_block_state( - block_pos, - Block::BASALT.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + block_pos, + Block::BASALT.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); world.sync_world_event(WorldEvent::LavaFizz, *block_pos, 0); return false; } @@ -154,80 +144,65 @@ const LAVA_FLOW_SPEED_NETHER: u8 = 10; const LAVA_FLOW_SPEED_SLOW: u8 = 30; impl FluidBehaviour for FlowingLava { - fn placed<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, + fn placed( + &self, + world: &Arc, + fluid: &Fluid, state_id: BlockStateId, - block_pos: &'a BlockPos, + block_pos: &BlockPos, old_state_id: BlockStateId, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - if old_state_id != state_id - && Self::receive_neighbor_fluids(world, fluid, block_pos).await - { - let flow_speed = self.get_flow_speed(world); - world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); - } - }) + ) { + if old_state_id != state_id && Self::receive_neighbor_fluids(world, fluid, block_pos) { + let flow_speed = self.get_flow_speed(world); + world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); + } } - fn on_scheduled_tick<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - self.on_scheduled_tick_internal(world, fluid, block_pos) + fn on_scheduled_tick(&self, world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) { + let world = world.clone(); + let block_pos = *block_pos; + tokio::spawn(async move { + Self.on_scheduled_tick_internal(&world, &Fluid::FLOWING_LAVA, &block_pos) .await; - }) + }); } - fn on_neighbor_update<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, + fn on_neighbor_update( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - if Self::receive_neighbor_fluids(world, fluid, block_pos).await { - let flow_speed = self.get_flow_speed(world); - world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); - } - }) + ) { + if Self::receive_neighbor_fluids(world, fluid, block_pos) { + let flow_speed = self.get_flow_speed(world); + world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); + } } - fn on_entity_collision<'a>(&'a self, entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { - Box::pin(async move { - let base_entity = entity.get_entity(); - if !base_entity.entity_type.fire_immune - && !base_entity.fire_immune.load(Ordering::Relaxed) - { - entity.set_on_fire_for(15.0); + fn on_entity_collision(&self, entity: &dyn EntityBase) { + let base_entity = entity.get_entity(); + if !base_entity.entity_type.fire_immune && !base_entity.fire_immune.load(Ordering::Relaxed) + { + entity.set_on_fire_for(15.0); - // Also apply lava damage - base_entity.damage(entity, 4.0, DamageType::LAVA).await; - } - }) + // Also apply lava damage + base_entity.damage(entity, 4.0, DamageType::LAVA); + } } - fn random_tick<'a>( - &'a self, - _fluid: &'a Fluid, - world: &'a Arc, - block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - if !Self::can_spread_fire_around(world, block_pos) { + fn random_tick(&self, _fluid: &Fluid, world: &Arc, block_pos: &BlockPos) { + let world = world.clone(); + let block_pos = *block_pos; + tokio::spawn(async move { + if !Self::can_spread_fire_around(&world, &block_pos) { return; } let passes = rand::random_range(0..3); if passes > 0 { - let mut test_pos = *block_pos; + let mut test_pos = block_pos; for _ in 0..passes { test_pos = test_pos.offset(Vector3::new( @@ -245,8 +220,8 @@ impl FluidBehaviour for FlowingLava { }; if block_state.is_air() { - if Self::has_flammable_neighbours(world, &test_pos) { - Self::ignite_fire_if_possible(world, &test_pos).await; + if Self::has_flammable_neighbours(&world, &test_pos) { + Self::ignite_fire_if_possible(&world, &test_pos); return; } } else if blocks_movement(block_state, block_state.id.to_block_id()) { @@ -273,13 +248,13 @@ impl FluidBehaviour for FlowingLava { if world .get_block_state_if_loaded(&above_pos) .is_some_and(BlockState::is_air) - && Self::is_flammable(world, &test_pos) + && Self::is_flammable(&world, &test_pos) { - Self::ignite_fire_if_possible(world, &above_pos).await; + Self::ignite_fire_if_possible(&world, &above_pos); } } } - }) + }); } } @@ -330,9 +305,7 @@ impl FlowingFluid for FlowingLava { if new_props.level == Level::L8 && new_props.falling == Falling::True { // Stone creation when lava meets water if block == &Block::WATER { - world - .set_block_state(pos, Block::STONE.default_state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, Block::STONE.default_state.id, BlockFlags::NOTIFY_ALL); world.sync_world_event(WorldEvent::LavaFizz, *pos, 0); return; } diff --git a/crates/pumpkin/src/block/fluid/mod.rs b/crates/pumpkin/src/block/fluid/mod.rs index bec1716c9..056d8dfed 100644 --- a/crates/pumpkin/src/block/fluid/mod.rs +++ b/crates/pumpkin/src/block/fluid/mod.rs @@ -12,7 +12,6 @@ pub mod flowing { } use super::{BlockIsReplacing, registry::BlockActionResult}; -use crate::block::BlockFuture; use crate::entity::{EntityBase, player::Player}; use crate::{server::Server, world::World}; use pumpkin_data::BlockDirection; @@ -23,102 +22,77 @@ use pumpkin_util::math::position::BlockPos; use std::sync::Arc; pub trait FluidBehaviour: Send + Sync { - fn normal_use<'a>( - &'a self, - _fluid: &'a Fluid, - _player: &'a Player, + fn normal_use( + &self, + _fluid: &Fluid, + _player: &Player, _location: BlockPos, - _server: &'a Server, - _world: &'a Arc, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) + _server: &Server, + _world: &Arc, + ) { } - fn use_with_item<'a>( - &'a self, - _fluid: &'a Fluid, - _player: &'a Player, + fn use_with_item( + &self, + _fluid: &Fluid, + _player: &Player, _location: BlockPos, - _item: &'a Item, - _server: &'a Server, - _world: &'a Arc, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async { BlockActionResult::Pass }) + _item: &Item, + _server: &Server, + _world: &Arc, + ) -> BlockActionResult { + BlockActionResult::Pass } - fn placed<'a>( - &'a self, - _world: &'a Arc, - _fluid: &'a Fluid, + fn placed( + &self, + _world: &Arc, + _fluid: &Fluid, _state_id: BlockStateId, - _block_pos: &'a BlockPos, + _block_pos: &BlockPos, _old_state_id: BlockStateId, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) + ) { } #[expect(clippy::too_many_arguments)] - fn on_place<'a>( - &'a self, - _server: &'a Server, - _world: &'a Arc, - fluid: &'a Fluid, + fn on_place( + &self, + _server: &Server, + _world: &Arc, + fluid: &Fluid, _face: BlockDirection, - _block_pos: &'a BlockPos, - _use_item_on: &'a SUseItemOn, + _block_pos: &BlockPos, + _use_item_on: &SUseItemOn, _replacing: BlockIsReplacing, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async { fluid.states[fluid.default_state_index as usize].block_state_id }) + ) -> BlockStateId { + fluid.states[fluid.default_state_index as usize].block_state_id } - fn get_state_for_neighbour_update<'a>( - &'a self, - _world: &'a Arc, - _fluid: &'a Fluid, - _block_pos: &'a BlockPos, + fn get_state_for_neighbour_update( + &self, + _world: &Arc, + _fluid: &Fluid, + _block_pos: &BlockPos, _notify: bool, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async { BlockStateId::AIR }) + ) -> BlockStateId { + BlockStateId::AIR } - fn on_neighbor_update<'a>( - &'a self, - _world: &'a Arc, - _fluid: &'a Fluid, - _block_pos: &'a BlockPos, + fn on_neighbor_update( + &self, + _world: &Arc, + _fluid: &Fluid, + _block_pos: &BlockPos, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) + ) { } - fn on_entity_collision<'a>(&'a self, _entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_entity_collision(&self, _entity: &dyn EntityBase) {} - fn on_scheduled_tick<'a>( - &'a self, - _world: &'a Arc, - _fluid: &'a Fluid, - _block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_scheduled_tick(&self, _world: &Arc, _fluid: &Fluid, _block_pos: &BlockPos) {} - fn random_tick<'a>( - &'a self, - _fluid: &'a Fluid, - _world: &'a Arc, - _block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn random_tick(&self, _fluid: &Fluid, _world: &Arc, _block_pos: &BlockPos) {} - fn create_legacy_block<'a>( - &'a self, - _world: &'a Arc, - _block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn create_legacy_block(&self, _world: &Arc, _block_pos: &BlockPos) {} } diff --git a/crates/pumpkin/src/block/fluid/water.rs b/crates/pumpkin/src/block/fluid/water.rs index f04231c37..cc955302d 100644 --- a/crates/pumpkin/src/block/fluid/water.rs +++ b/crates/pumpkin/src/block/fluid/water.rs @@ -1,6 +1,6 @@ use super::flowing_trait::FlowingFluid; use crate::{ - block::{BlockFuture, FluidMetadata, fluid::FluidBehaviour}, + block::{FluidMetadata, fluid::FluidBehaviour}, entity::EntityBase, world::World, }; @@ -21,63 +21,44 @@ impl FluidMetadata for FlowingWater { const WATER_FLOW_SPEED: u8 = 5; impl FluidBehaviour for FlowingWater { - fn placed<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, + fn placed( + &self, + world: &Arc, + fluid: &Fluid, state_id: BlockStateId, - block_pos: &'a BlockPos, + block_pos: &BlockPos, old_state_id: BlockStateId, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - if old_state_id != state_id { - world.schedule_fluid_tick( - fluid, - *block_pos, - WATER_FLOW_SPEED, - TickPriority::Normal, - ); - } - }) + ) { + if old_state_id != state_id { + world.schedule_fluid_tick(fluid, *block_pos, WATER_FLOW_SPEED, TickPriority::Normal); + } } - fn on_scheduled_tick<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, - ) -> BlockFuture<'a, ()> { - Box::pin(async { - self.on_scheduled_tick_internal(world, fluid, block_pos) + fn on_scheduled_tick(&self, world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) { + let world = world.clone(); + let block_pos = *block_pos; + tokio::spawn(async move { + Self.on_scheduled_tick_internal(&world, &Fluid::FLOWING_WATER, &block_pos) .await; - }) + }); } - fn on_neighbor_update<'a>( - &'a self, - world: &'a Arc, - fluid: &'a Fluid, - block_pos: &'a BlockPos, + fn on_neighbor_update( + &self, + world: &Arc, + fluid: &Fluid, + block_pos: &BlockPos, _notify: bool, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - // Avoid rescheduling a fluid tick if one is already queued. - if !world.is_fluid_tick_scheduled(block_pos, fluid) { - world.schedule_fluid_tick( - fluid, - *block_pos, - WATER_FLOW_SPEED, - TickPriority::Normal, - ); - } - }) + ) { + // Avoid rescheduling a fluid tick if one is already queued. + if !world.is_fluid_tick_scheduled(block_pos, fluid) { + world.schedule_fluid_tick(fluid, *block_pos, WATER_FLOW_SPEED, TickPriority::Normal); + } } - fn on_entity_collision<'a>(&'a self, entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { - Box::pin(async { - entity.get_entity().extinguish(); - }) + fn on_entity_collision(&self, entity: &dyn EntityBase) { + entity.get_entity().extinguish(); } } diff --git a/crates/pumpkin/src/block/mod.rs b/crates/pumpkin/src/block/mod.rs index 53c46980b..cdeb7e1d4 100644 --- a/crates/pumpkin/src/block/mod.rs +++ b/crates/pumpkin/src/block/mod.rs @@ -8,7 +8,6 @@ use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::player::Player; use crate::world::World; use crate::world::loot::{LootContextParameters, LootTableExt}; -use std::pin::Pin; use std::sync::Arc; pub mod blocks; @@ -37,8 +36,6 @@ pub trait FluidMetadata { fn ids() -> Box<[u16]>; } -pub type BlockFuture<'a, T> = Pin + Send + 'a>>; - pub(crate) fn stop_vertical_movement_after_fall(entity: &dyn EntityBase) { let entity = entity.get_entity(); let mut velocity = entity.velocity.load(); @@ -73,56 +70,40 @@ pub trait BlockBehaviour: Send + Sync { true } - fn perform_bonemeal<'a>(&'a self, _args: BonemealArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) + fn perform_bonemeal(&self, _args: BonemealArgs<'_>) {} + + fn normal_use(&self, _args: NormalUseArgs<'_>) -> BlockActionResult { + BlockActionResult::Pass } - fn normal_use<'a>(&'a self, _args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { BlockActionResult::Pass }) + fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { + BlockActionResult::PassToDefaultBlockAction } - fn use_with_item<'a>( - &'a self, - _args: UseWithItemArgs<'a>, - ) -> BlockFuture<'a, BlockActionResult> { - Box::pin(async move { BlockActionResult::PassToDefaultBlockAction }) - } - - fn on_entity_collision<'a>(&'a self, _args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {} /// Called when an entity is standing on / walking over the top face of this block. - fn on_entity_step<'a>(&'a self, _args: OnEntityStepArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_entity_step(&self, _args: OnEntityStepArgs<'_>) {} fn should_drop_items_on_explosion(&self) -> bool { true } - fn explode<'a>(&'a self, _args: ExplodeArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn explode(&self, _args: ExplodeArgs<'_>) {} /// Handles the block event, which is an event specific to a block with an integer ID and data. /// /// returns whether the event was handled successfully - fn on_synced_block_event<'a>( - &'a self, - _args: OnSyncedBlockEventArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { false }) + fn on_synced_block_event(&self, _args: OnSyncedBlockEventArgs<'_>) -> bool { + false } /// getPlacementState in source code - fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { args.block.default_state.id }) + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { + args.block.default_state.id } - fn random_tick<'a>(&'a self, _args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn random_tick(&self, _args: RandomTickArgs<'_>) {} fn can_place_at(&self, _args: CanPlaceAtArgs<'_>) -> bool { true @@ -133,99 +114,61 @@ pub trait BlockBehaviour: Send + Sync { } /// onBlockAdded in source code - fn placed<'a>(&'a self, _args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) + fn placed(&self, _args: PlacedArgs<'_>) {} + + fn player_placed(&self, _args: PlayerPlacedArgs<'_>) {} + + fn on_landed_upon(&self, args: OnLandedUponArgs<'_>) { + if let Some(living) = args.entity.get_living_entity() { + living.handle_fall_damage(args.entity, args.fall_distance, 1.0); + } } - fn player_placed<'a>(&'a self, _args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) + fn update_entity_movement_after_fall_on(&self, args: UpdateEntityMovementAfterFallOnArgs<'_>) { + stop_vertical_movement_after_fall(args.entity); } - fn on_landed_upon<'a>(&'a self, args: OnLandedUponArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = args.entity.get_living_entity() { - living - .handle_fall_damage(args.entity, args.fall_distance, 1.0) - .await; - } - }) - } + fn broken(&self, _args: BrokenArgs<'_>) {} - fn update_entity_movement_after_fall_on<'a>( - &'a self, - args: UpdateEntityMovementAfterFallOnArgs<'a>, - ) -> BlockFuture<'a, ()> { - Box::pin(async move { - stop_vertical_movement_after_fall(args.entity); - }) - } - - fn broken<'a>(&'a self, _args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } - - fn on_neighbor_update<'a>(&'a self, _args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_neighbor_update(&self, _args: OnNeighborUpdateArgs<'_>) {} /// Called if a block state is replaced or it replaces another state - fn prepare<'a>(&'a self, _args: PrepareArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) + fn prepare(&self, _args: PrepareArgs<'_>) {} + + fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + args.state_id } - fn get_state_for_neighbor_update<'a>( - &'a self, - args: GetStateForNeighborUpdateArgs<'a>, - ) -> BlockFuture<'a, BlockStateId> { - Box::pin(async move { args.state_id }) - } + fn on_scheduled_tick(&self, _args: OnScheduledTickArgs<'_>) {} - fn on_scheduled_tick<'a>(&'a self, _args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } - - fn on_state_replaced<'a>(&'a self, _args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { - Box::pin(async {}) - } + fn on_state_replaced(&self, _args: OnStateReplacedArgs<'_>) {} // --- Redstone/Comparator Methods --- /// Sides where redstone connects to - fn emits_redstone_power<'a>( - &'a self, - _args: EmitsRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, bool> { - Box::pin(async move { false }) + fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { + false } /// Weak redstone power, aka. block that should be powered needs to be directly next to the source block - fn get_weak_redstone_power<'a>( - &'a self, - _args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { 0 }) + fn get_weak_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { + 0 } /// Strong redstone power. this can power a block that then gives power - fn get_strong_redstone_power<'a>( - &'a self, - _args: GetRedstonePowerArgs<'a>, - ) -> BlockFuture<'a, u8> { - Box::pin(async move { 0 }) + fn get_strong_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { + 0 } - fn get_comparator_output<'a>( - &'a self, - _args: GetComparatorOutputArgs<'a>, - ) -> BlockFuture<'a, Option> { - Box::pin(async move { None }) + fn get_comparator_output(&self, _args: GetComparatorOutputArgs<'_>) -> Option { + None } - fn get_inside_collision_shape<'a>( - &'a self, - _args: GetInsideCollisionShapeArgs<'a>, - ) -> BlockFuture<'a, BoundingBox> { - Box::pin(async move { BoundingBox::full_block() }) + fn get_inside_collision_shape(&self, _args: GetInsideCollisionShapeArgs<'_>) -> BoundingBox { + BoundingBox::full_block() } fn mirror(&self, block: &Block, state_id: BlockStateId, mirror: Mirror) -> &'static BlockState { @@ -480,7 +423,7 @@ pub async fn drop_loot( } if !event.cancelled { for stack in event.items { - world.drop_stack(pos, stack).await; + world.drop_stack(pos, stack); } } } @@ -500,30 +443,26 @@ pub async fn drop_loot( server.plugin_manager.fire(&server, &mut event).await; } if event.exp > 0 { - ExperienceOrbEntity::spawn(world, pos.to_f64(), event.exp as u32).await; + ExperienceOrbEntity::spawn(world, pos.to_f64(), event.exp as u32); } } } } -pub async fn calc_block_breaking( - player: &Player, - state: &BlockState, - block: &'static Block, -) -> f32 { +pub fn calc_block_breaking(player: &Player, state: &BlockState, block: &'static Block) -> f32 { let hardness = state.hardness; #[expect(clippy::float_cmp)] if hardness == -1.0 { // unbreakable return 0.0; } - let i = if player.can_harvest(state, block).await { + let i = if player.can_harvest(state, block) { 30.0 } else { 100.0 }; - player.get_mining_speed(block).await / hardness / i + player.get_mining_speed(block) / hardness / i } #[derive(PartialEq, Eq, Debug)] @@ -546,9 +485,7 @@ impl BlockIsReplacing { } } -pub async fn calculate_comparator_output( - inventory: &dyn pumpkin_world::inventory::Inventory, -) -> u8 { +pub fn calculate_comparator_output(inventory: &dyn pumpkin_world::inventory::Inventory) -> u8 { let size = inventory.size(); if size == 0 { return 0; @@ -556,7 +493,7 @@ pub async fn calculate_comparator_output( let mut fill_sum = 0.0; let mut non_empty_count = 0; for i in 0..size { - let stack = inventory.get_stack(i).await; + let stack = futures::executor::block_on(inventory.get_stack(i)); if !stack.is_empty() { let max_stack = stack.get_max_stack_size() as f32; let count = stack.item_count as f32; diff --git a/crates/pumpkin/src/block/registry.rs b/crates/pumpkin/src/block/registry.rs index c5ea96173..8d798b480 100644 --- a/crates/pumpkin/src/block/registry.rs +++ b/crates/pumpkin/src/block/registry.rs @@ -466,7 +466,7 @@ pub enum BlockPlacingError { } impl BlockRegistry { - pub async fn bone_meal( + pub fn bone_meal( &self, block: &Block, world: &Arc, @@ -486,7 +486,7 @@ impl BlockRegistry { return false; } if behaviour.is_bonemeal_success(args) { - behaviour.perform_bonemeal(args).await; + behaviour.perform_bonemeal(args); } true } @@ -495,7 +495,7 @@ impl BlockRegistry { let base_entity = entity.get_entity(); if base_entity.is_removed() || base_entity - .no_clip + .no_physics .load(std::sync::atomic::Ordering::Relaxed) || entity.is_spectator() { @@ -665,18 +665,16 @@ impl BlockRegistry { return Ok(None); } - let new_state = self - .on_place( - server, - &world, - player, - placed_block, - &final_block_pos, - final_face, - replacing, - use_item_on, - ) - .await; + let new_state = self.on_place( + server, + &world, + player, + placed_block, + &final_block_pos, + final_face, + replacing, + use_item_on, + ); // Mirror vanilla obstruction checks: only entities that block building should prevent // placement. (e.g. arrows/xp orbs/displays/markers should not) @@ -724,9 +722,8 @@ impl BlockRegistry { return Ok(None); } - let _replaced_id = world - .set_block_state(&final_block_pos, new_state, BlockFlags::NOTIFY_ALL) - .await; + let _replaced_id = + world.set_block_state(&final_block_pos, new_state, BlockFlags::NOTIFY_ALL); self.player_placed( &world, @@ -735,16 +732,13 @@ impl BlockRegistry { &final_block_pos, face, player, - ) - .await; + ); - player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::PlacedBlock { - block_id: format!("minecraft:{}", placed_block.name), - }, - ) - .await; + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::PlacedBlock { + block_id: format!("minecraft:{}", placed_block.name), + }, + ); Ok(Some((final_block_pos, new_state))) } @@ -766,7 +760,7 @@ impl BlockRegistry { } } - pub async fn on_synced_block_event( + pub fn on_synced_block_event( &self, block: &Block, world: &Arc, @@ -776,20 +770,18 @@ impl BlockRegistry { ) -> bool { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .on_synced_block_event(OnSyncedBlockEventArgs { - world, - block, - position, - r#type, - data, - }) - .await; + return pumpkin_block.on_synced_block_event(OnSyncedBlockEventArgs { + world, + block, + position, + r#type, + data, + }); } false } - pub async fn on_entity_collision( + pub fn on_entity_collision( &self, block: &Block, world: &Arc, @@ -800,20 +792,18 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_entity_collision(OnEntityCollisionArgs { - server, - world, - block, - state, - position, - entity, - }) - .await; + pumpkin_block.on_entity_collision(OnEntityCollisionArgs { + server, + world, + block, + state, + position, + entity, + }); } } - pub async fn on_entity_step( + pub fn on_entity_step( &self, block: &Block, world: &Arc, @@ -823,27 +813,25 @@ impl BlockRegistry { below_supporting_block: bool, ) { if let Some(pumpkin_block) = self.get_pumpkin_block(block.id) { - pumpkin_block - .on_entity_step(OnEntityStepArgs { - world, - block, - state, - position, - entity, - below_supporting_block, - }) - .await; + pumpkin_block.on_entity_step(OnEntityStepArgs { + world, + block, + state, + position, + entity, + below_supporting_block, + }); } } - pub async fn on_entity_collision_fluid(&self, fluid: &Fluid, entity: &dyn EntityBase) { + pub fn on_entity_collision_fluid(&self, fluid: &Fluid, entity: &dyn EntityBase) { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { - pumpkin_fluid.on_entity_collision(entity).await; + pumpkin_fluid.on_entity_collision(entity); } } - pub async fn on_use( + pub fn on_use( &self, block: &Block, player: &Arc, @@ -854,35 +842,31 @@ impl BlockRegistry { ) -> BlockActionResult { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .normal_use(NormalUseArgs { - server, - world, - block, - position, - player, - hit, - }) - .await; + return pumpkin_block.normal_use(NormalUseArgs { + server, + world, + block, + position, + player, + hit, + }); } BlockActionResult::Pass } - pub async fn explode(&self, block: &Block, world: &Arc, position: &BlockPos) { + pub fn explode(&self, block: &Block, world: &Arc, position: &BlockPos) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .explode(ExplodeArgs { - world, - block, - position, - }) - .await; + pumpkin_block.explode(ExplodeArgs { + world, + block, + position, + }); } } #[expect(clippy::too_many_arguments)] - pub async fn use_with_item( + pub fn use_with_item( &self, block: &Block, player: &Arc, @@ -895,23 +879,21 @@ impl BlockRegistry { ) -> BlockActionResult { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .use_with_item(UseWithItemArgs { - server, - world, - block, - position, - player, - hit, - item_stack, - equipment_slot, - }) - .await; + return pumpkin_block.use_with_item(UseWithItemArgs { + server, + world, + block, + position, + player, + hit, + item_stack, + equipment_slot, + }); } BlockActionResult::Pass } - pub async fn use_with_item_fluid( + pub fn use_with_item_fluid( &self, fluid: &Fluid, player: &Arc, @@ -922,9 +904,7 @@ impl BlockRegistry { ) -> BlockActionResult { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { - return pumpkin_fluid - .use_with_item(fluid, player, position, item, server, world) - .await; + return pumpkin_fluid.use_with_item(fluid, player, position, item, server, world); } BlockActionResult::Pass } @@ -986,7 +966,7 @@ impl BlockRegistry { } #[expect(clippy::too_many_arguments)] - pub async fn on_place( + pub fn on_place( &self, server: &Server, world: &World, @@ -999,23 +979,21 @@ impl BlockRegistry { ) -> BlockStateId { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .on_place(OnPlaceArgs { - server, - world, - block, - position, - direction, - player, - replacing, - use_item_on, - }) - .await; + return pumpkin_block.on_place(OnPlaceArgs { + server, + world, + block, + position, + direction, + player, + replacing, + use_item_on, + }); } block.default_state.id } - pub async fn player_placed( + pub fn player_placed( &self, world: &Arc, block: &Block, @@ -1026,20 +1004,18 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .player_placed(PlayerPlacedArgs { - world, - block, - state_id, - position, - direction, - player, - }) - .await; + pumpkin_block.player_placed(PlayerPlacedArgs { + world, + block, + state_id, + position, + direction, + player, + }); } } - pub async fn on_placed( + pub fn on_placed( &self, world: &Arc, block: &Block, @@ -1059,20 +1035,18 @@ impl BlockRegistry { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .placed(PlacedArgs { - world, - block, - state_id, - old_state_id, - position, - notify, - }) - .await; + pumpkin_block.placed(PlacedArgs { + world, + block, + state_id, + old_state_id, + position, + notify, + }); } } - pub async fn on_placed_fluid( + pub fn on_placed_fluid( &self, world: &Arc, fluid: &Fluid, @@ -1083,13 +1057,11 @@ impl BlockRegistry { ) { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { - pumpkin_fluid - .placed(world, fluid, state_id, position, old_state_id, notify) - .await; + pumpkin_fluid.placed(world, fluid, state_id, position, old_state_id, notify); } } - pub async fn on_landed_upon( + pub fn on_landed_upon( &self, block: &Block, world: &Arc, @@ -1098,33 +1070,25 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_landed_upon(OnLandedUponArgs { - world, - fall_distance, - entity, - }) - .await; + pumpkin_block.on_landed_upon(OnLandedUponArgs { + world, + fall_distance, + entity, + }); } } - pub async fn update_entity_movement_after_fall_on( - &self, - block: &Block, - entity: &dyn EntityBase, - ) { + pub fn update_entity_movement_after_fall_on(&self, block: &Block, entity: &dyn EntityBase) { if let Some(pumpkin_block) = self.get_pumpkin_block(block.id) { - pumpkin_block - .update_entity_movement_after_fall_on(UpdateEntityMovementAfterFallOnArgs { - entity, - }) - .await; + pumpkin_block.update_entity_movement_after_fall_on( + UpdateEntityMovementAfterFallOnArgs { entity }, + ); } else { stop_vertical_movement_after_fall(entity); } } - pub async fn broken( + pub fn broken( &self, world: &Arc, block: &Block, @@ -1135,20 +1099,18 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .broken(BrokenArgs { - block, - player, - position, - server, - world, - state, - }) - .await; + pumpkin_block.broken(BrokenArgs { + block, + player, + position, + server, + world, + state, + }); } } - pub async fn on_state_replaced( + pub fn on_state_replaced( &self, world: &Arc, block: &Block, @@ -1158,20 +1120,18 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_state_replaced(OnStateReplacedArgs { - world, - block, - old_state_id, - position, - moved, - }) - .await; + pumpkin_block.on_state_replaced(OnStateReplacedArgs { + world, + block, + old_state_id, + position, + moved, + }); } } /// Updates state of all neighbors of the block - pub async fn post_process_state( + pub fn post_process_state( &self, world: &Arc, position: &BlockPos, @@ -1184,8 +1144,8 @@ impl BlockRegistry { let neighbor_state_id = world.get_block_state_id(&neighbor_pos); let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - let new_state = pumpkin_block - .get_state_for_neighbor_update(GetStateForNeighborUpdateArgs { + let new_state = + pumpkin_block.get_state_for_neighbor_update(GetStateForNeighborUpdateArgs { world, block, state_id, @@ -1193,14 +1153,13 @@ impl BlockRegistry { direction: direction.opposite(), neighbor_position: &neighbor_pos, neighbor_state_id, - }) - .await; - world.set_block_state(&neighbor_pos, new_state, flags).await; + }); + world.set_block_state(&neighbor_pos, new_state, flags); } } } - pub async fn prepare( + pub fn prepare( &self, world: &Arc, position: &BlockPos, @@ -1210,22 +1169,20 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .prepare(PrepareArgs { - world, - block, - state_id, - position, - flags, - }) - .await; + pumpkin_block.prepare(PrepareArgs { + world, + block, + state_id, + position, + flags, + }); } } #[expect(clippy::too_many_arguments)] - pub async fn get_state_for_neighbor_update( + pub fn get_state_for_neighbor_update( &self, - world: &Arc, + world: &World, block: &Block, state_id: BlockStateId, position: &BlockPos, @@ -1235,22 +1192,20 @@ impl BlockRegistry { ) -> BlockStateId { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .get_state_for_neighbor_update(GetStateForNeighborUpdateArgs { - world, - block, - state_id, - position, - direction, - neighbor_position: neighbor_location, - neighbor_state_id, - }) - .await; + return pumpkin_block.get_state_for_neighbor_update(GetStateForNeighborUpdateArgs { + world, + block, + state_id, + position, + direction, + neighbor_position: neighbor_location, + neighbor_state_id, + }); } state_id } - pub async fn update_neighbors( + pub fn update_neighbors( &self, world: &Arc, position: &BlockPos, @@ -1260,16 +1215,11 @@ impl BlockRegistry { for direction in BlockDirection::abstract_block_update_order() { let pos = position.offset(direction.to_offset()); - Box::pin(world.replace_with_state_for_neighbor_update( - &pos, - direction.opposite(), - flags, - )) - .await; + world.replace_with_state_for_neighbor_update(&pos, direction.opposite(), flags); } } - pub async fn on_neighbor_update( + pub fn on_neighbor_update( &self, world: &Arc, block: &Block, @@ -1279,15 +1229,13 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_neighbor_update(OnNeighborUpdateArgs { - world, - block, - position, - source_block, - notify, - }) - .await; + pumpkin_block.on_neighbor_update(OnNeighborUpdateArgs { + world, + block, + position, + source_block, + notify, + }); } } @@ -1308,7 +1256,8 @@ impl BlockRegistry { }) } - pub async fn emits_redstone_power( + #[must_use] + pub fn emits_redstone_power( &self, block: &Block, state: &BlockState, @@ -1316,18 +1265,16 @@ impl BlockRegistry { ) -> bool { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .emits_redstone_power(EmitsRedstonePowerArgs { - block, - state, - direction, - }) - .await; + return pumpkin_block.emits_redstone_power(EmitsRedstonePowerArgs { + block, + state, + direction, + }); } false } - pub async fn get_weak_redstone_power( + pub fn get_weak_redstone_power( &self, block: &Block, world: &World, @@ -1337,20 +1284,18 @@ impl BlockRegistry { ) -> u8 { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .get_weak_redstone_power(GetRedstonePowerArgs { - world, - block, - state, - position, - direction, - }) - .await; + return pumpkin_block.get_weak_redstone_power(GetRedstonePowerArgs { + world, + block, + state, + position, + direction, + }); } 0 } - pub async fn get_strong_redstone_power( + pub fn get_strong_redstone_power( &self, block: &Block, world: &World, @@ -1360,20 +1305,18 @@ impl BlockRegistry { ) -> u8 { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .get_strong_redstone_power(GetRedstonePowerArgs { - world, - block, - state, - position, - direction, - }) - .await; + return pumpkin_block.get_strong_redstone_power(GetRedstonePowerArgs { + world, + block, + state, + position, + direction, + }); } 0 } - pub async fn get_inside_collision_shape( + pub fn get_inside_collision_shape( &self, block: &Block, world: &World, @@ -1382,14 +1325,12 @@ impl BlockRegistry { ) -> BoundingBox { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block - .get_inside_collision_shape(GetInsideCollisionShapeArgs { - world, - block, - state, - position, - }) - .await; + return pumpkin_block.get_inside_collision_shape(GetInsideCollisionShapeArgs { + world, + block, + state, + position, + }); } BoundingBox::full_block() } diff --git a/crates/pumpkin/src/block/viewer.rs b/crates/pumpkin/src/block/viewer.rs index 71de5b27d..d0f256f5d 100644 --- a/crates/pumpkin/src/block/viewer.rs +++ b/crates/pumpkin/src/block/viewer.rs @@ -1,7 +1,4 @@ -use std::{ - pin::Pin, - sync::{Arc, atomic::Ordering}, -}; +use std::sync::{Arc, atomic::Ordering}; use pumpkin_util::math::position::BlockPos; @@ -10,74 +7,45 @@ use crate::{block::entities::BlockEntity, world::World}; pub use pumpkin_world::block::viewer::ViewerCountTracker; pub trait ViewerCountTrackerExt { - fn update_viewer_count<'a, T>( - &'a self, - entity: &'a T, - world: &'a Arc, - position: &'a BlockPos, - ) -> Pin + Send + 'a>> + fn update_viewer_count(&self, entity: &T, world: &Arc, position: &BlockPos) where T: BlockEntity + ViewerCountListener + 'static; } impl ViewerCountTrackerExt for ViewerCountTracker { - fn update_viewer_count<'a, T>( - &'a self, - entity: &'a T, - world: &'a Arc, - position: &'a BlockPos, - ) -> Pin + Send + 'a>> + fn update_viewer_count(&self, entity: &T, world: &Arc, position: &BlockPos) where T: BlockEntity + ViewerCountListener + 'static, { - Box::pin(async move { - let current = self.current.load(Ordering::Relaxed); - let old = self.old.swap(current, Ordering::Relaxed); - if old != current { - match (old, current) { - (n, 0) if n > 0 => { - entity.on_container_close(world, position).await; - } - (0, n) if n > 0 => { - entity.on_container_open(world, position).await; - } - _ => {} // Ignore + let current = self.current.load(Ordering::Relaxed); + let old = self.old.swap(current, Ordering::Relaxed); + if old != current { + match (old, current) { + (n, 0) if n > 0 => { + entity.on_container_close(world, position); } - - entity - .on_viewer_count_update(world, position, old, current) - .await; + (0, n) if n > 0 => { + entity.on_container_open(world, position); + } + _ => {} // Ignore } - }) + + entity.on_viewer_count_update(world, position, old, current); + } } } -pub type ViewerFuture<'a, T> = Pin + Send + 'a>>; - pub trait ViewerCountListener: Send + Sync { - fn on_container_open<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) - } + fn on_container_open(&self, _world: &Arc, _position: &BlockPos) {} - fn on_container_close<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) - } + fn on_container_close(&self, _world: &Arc, _position: &BlockPos) {} - fn on_viewer_count_update<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, + fn on_viewer_count_update( + &self, + _world: &Arc, + _position: &BlockPos, _old: u16, _new: u16, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) + ) { } } diff --git a/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs b/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs index ce2fca552..fe724d47d 100644 --- a/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs +++ b/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs @@ -8,7 +8,6 @@ use crate::command::context::command_source::CommandSource; use crate::command::errors::command_syntax_error::CommandSyntaxError; use crate::entity::EntityBase; use crate::entity::player::Player; -use crate::entity::player::advancement::AdvancementProgress; use crate::world::World; use pumpkin_data::Advancement; use pumpkin_data::entity::EntityType; @@ -554,11 +553,9 @@ impl EntitySelectorPredicate { let Some(player) = entity.get_player() else { return false; }; - let adv_mgr = player.advancements.blocking_lock(); for (adv_id, expected_done) in advancements_map { if let Some(advancement) = Advancement::from_name(adv_id) { - let progress = adv_mgr.progress.map.get(advancement); - let is_done = progress.is_some_and(AdvancementProgress::is_done); + let is_done = player.has_advancement(advancement); if is_done != *expected_done { return false; } diff --git a/crates/pumpkin/src/command/commands/attribute.rs b/crates/pumpkin/src/command/commands/attribute.rs index 3a481ac41..cff6feb95 100644 --- a/crates/pumpkin/src/command/commands/attribute.rs +++ b/crates/pumpkin/src/command/commands/attribute.rs @@ -161,8 +161,7 @@ impl CommandExecutor for BaseSetExecutor { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); context .source @@ -214,8 +213,7 @@ impl CommandExecutor for BaseResetExecutor { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); context .source @@ -290,8 +288,7 @@ impl CommandExecutor for ModifierAddExecutor { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); context .source @@ -362,8 +359,7 @@ impl CommandExecutor for ModifierRemoveExecutor { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); context .source diff --git a/crates/pumpkin/src/command/commands/bossbar.rs b/crates/pumpkin/src/command/commands/bossbar.rs index ed3ea58b1..c111db426 100644 --- a/crates/pumpkin/src/command/commands/bossbar.rs +++ b/crates/pumpkin/src/command/commands/bossbar.rs @@ -330,20 +330,15 @@ impl CommandExecutor for RemoveExecutor { )) .await; - let error = { - match server - .bossbars - .lock() - .await - .remove_bossbar(server, namespace) - .await - { - Ok(()) => return Ok(server.bossbars.lock().await.get_bossbars_len() as i32), - Err(error) => error, - } - }; - - Err(handle_bossbar_error(error)) + let res = server + .bossbars + .lock() + .await + .remove_bossbar(server, namespace); + match res { + Ok(()) => Ok(server.bossbars.lock().await.get_bossbars_len() as i32), + Err(error) => Err(handle_bossbar_error(error)), + } }) } } @@ -373,18 +368,12 @@ impl CommandExecutor for SetExecutor { CommandValueSet::Color => { let color = BossbarColorArgumentConsumer.find_arg_default_name(args)?; - match server + server .bossbars .lock() .await - .update_color(server, namespace.clone(), *color) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .update_color(server, &namespace, *color) + .map_err(handle_bossbar_error)?; sender .send_message(TextComponent::translate_cross( @@ -408,18 +397,12 @@ impl CommandExecutor for SetExecutor { ))); }; - match server + server .bossbars .lock() .await .update_max(server, namespace.clone(), max_value) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .map_err(handle_bossbar_error)?; sender .send_message(TextComponent::translate_cross( @@ -432,28 +415,22 @@ impl CommandExecutor for SetExecutor { )) .await; - Ok(max_value) + Ok(0) } CommandValueSet::Name => { - let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; - match server + let name = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; + server .bossbars .lock() .await - .update_name(server, &namespace, text_component.clone()) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .update_name(server, &namespace, &name) + .map_err(handle_bossbar_error)?; sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_BOSSBAR_SET_NAME_SUCCESS, translation::java::COMMANDS_BOSSBAR_SET_NAME_SUCCESS, - [bossbar_prefix(text_component, namespace)], + [bossbar_prefix(name.clone(), namespace)], )) .await; @@ -461,18 +438,13 @@ impl CommandExecutor for SetExecutor { } CommandValueSet::Players(has_players) => { if !has_players { - match server + server .bossbars .lock() .await - .update_players(server, namespace.clone(), vec![]) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .set_players(server, namespace.clone(), vec![]) + .map_err(handle_bossbar_error)?; + sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_BOSSBAR_SET_PLAYERS_SUCCESS_NONE, @@ -492,18 +464,12 @@ impl CommandExecutor for SetExecutor { targets.iter().map(|player| player.gameprofile.id).collect(); let count = players.len(); - match server + server .bossbars .lock() .await - .update_players(server, namespace.clone(), players) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .set_players(server, namespace.clone(), players) + .map_err(handle_bossbar_error)?; let player_names = targets .iter() @@ -527,18 +493,13 @@ impl CommandExecutor for SetExecutor { } CommandValueSet::Style => { let style = BossbarStyleArgumentConsumer.find_arg_default_name(args)?; - match server + server .bossbars .lock() .await - .update_division(server, namespace.clone(), *style) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .update_style(server, &namespace, *style) + .map_err(handle_bossbar_error)?; + sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_BOSSBAR_SET_STYLE_SUCCESS, @@ -560,18 +521,12 @@ impl CommandExecutor for SetExecutor { ))); }; - match server + server .bossbars .lock() .await .update_value(server, namespace.clone(), value) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .map_err(handle_bossbar_error)?; sender .send_message(TextComponent::translate_cross( @@ -589,18 +544,12 @@ impl CommandExecutor for SetExecutor { CommandValueSet::Visible => { let visibility = BoolArgConsumer::find_arg(args, ARG_VISIBLE)?; - match server + server .bossbars .lock() .await .update_visibility(server, namespace.clone(), visibility) - .await - { - Ok(()) => {} - Err(err) => { - return Err(handle_bossbar_error(err)); - } - } + .map_err(handle_bossbar_error)?; let state = if visibility { translation::java::COMMANDS_BOSSBAR_SET_VISIBLE_SUCCESS_VISIBLE diff --git a/crates/pumpkin/src/command/commands/clear.rs b/crates/pumpkin/src/command/commands/clear.rs index b04e1072b..d36c772b6 100644 --- a/crates/pumpkin/src/command/commands/clear.rs +++ b/crates/pumpkin/src/command/commands/clear.rs @@ -33,14 +33,17 @@ const MAX_NO_CLEAR_BUT_SIMULATE: i32 = 0; /// If `max` provided is [`MAX_NO_UPPER_LIMIT`] (`-1`), then there is no limit in clearing. /// /// Otherwise, at most `max` items are cleared. -async fn clear_player(target: &Player, item: &ItemPredicate, max: i32) -> i32 { +fn clear_player(target: &Player, item: &ItemPredicate, max: i32) -> i32 { let inventory = target.inventory(); let mut count: i32 = 0; let mut max: i32 = max; let mut is_done: bool = false; { - let mut main_inv = inventory.main_inventory.write().await; + let mut main_inv = inventory + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); for slot in main_inv.iter_mut() { test_and_clear(&mut count, &mut max, item, slot, &mut is_done); if is_done { @@ -50,7 +53,10 @@ async fn clear_player(target: &Player, item: &ItemPredicate, max: i32) -> i32 { } if !is_done { - let mut entity_equipment_lock = inventory.entity_equipment.lock().await; + let mut entity_equipment_lock = inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for slot in entity_equipment_lock.equipment.values_mut() { test_and_clear(&mut count, &mut max, item, slot, &mut is_done); if is_done { @@ -182,8 +188,7 @@ impl CommandExecutor for SelfExecutor { Box::pin(async move { let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; - let items_cleared = - clear_player(&target, &ItemPredicate::Any, MAX_NO_UPPER_LIMIT).await; + let items_cleared = clear_player(&target, &ItemPredicate::Any, MAX_NO_UPPER_LIMIT); command_result(sender, items_cleared, MAX_NO_UPPER_LIMIT, &[target]).await }) @@ -207,7 +212,7 @@ impl CommandExecutor for Executor { let mut total_items_cleared = 0; for target in targets { total_items_cleared += - clear_player(target, &ItemPredicate::Any, MAX_NO_UPPER_LIMIT).await; + clear_player(target, &ItemPredicate::Any, MAX_NO_UPPER_LIMIT); } command_result(sender, total_items_cleared, MAX_NO_UPPER_LIMIT, targets).await @@ -233,7 +238,7 @@ impl CommandExecutor for ItemExecutor { let mut total_items_cleared = 0; for target in targets { - total_items_cleared += clear_player(target, &item, MAX_NO_UPPER_LIMIT).await; + total_items_cleared += clear_player(target, &item, MAX_NO_UPPER_LIMIT); } command_result(sender, total_items_cleared, MAX_NO_UPPER_LIMIT, targets).await @@ -266,7 +271,7 @@ impl CommandExecutor for ItemCountExecutor { let mut total_items_cleared = 0; for target in targets { - total_items_cleared += clear_player(target, &item, max).await; + total_items_cleared += clear_player(target, &item, max); } command_result(sender, total_items_cleared, max, targets).await diff --git a/crates/pumpkin/src/command/commands/clone.rs b/crates/pumpkin/src/command/commands/clone.rs index e8ab80b46..9104bd3b5 100644 --- a/crates/pumpkin/src/command/commands/clone.rs +++ b/crates/pumpkin/src/command/commands/clone.rs @@ -202,9 +202,7 @@ impl CommandExecutor for CloneExecutor { let mut count = 0; for block in &blocks_to_clone { - world - .set_block_state(&block.dest_pos, block.state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&block.dest_pos, block.state_id, BlockFlags::NOTIFY_ALL); if let Some(nbt) = &block.block_entity_nbt { let mut new_nbt = nbt.clone(); @@ -236,13 +234,11 @@ impl CommandExecutor for CloneExecutor { if self.clone_mode == CloneMode::Move { for block in &blocks_to_clone { if !is_dest_pos(&block.src_pos) { - world - .set_block_state( - &block.src_pos, - BlockStateId::AIR, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &block.src_pos, + BlockStateId::AIR, + BlockFlags::NOTIFY_ALL, + ); } } } diff --git a/crates/pumpkin/src/command/commands/damage.rs b/crates/pumpkin/src/command/commands/damage.rs index 015563054..db9f2d679 100644 --- a/crates/pumpkin/src/command/commands/damage.rs +++ b/crates/pumpkin/src/command/commands/damage.rs @@ -78,9 +78,14 @@ impl CommandExecutor for LocationExecutor { let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - let success = target - .damage_with_context(&*target, amount, damage_type, Some(location), None, None) - .await; + let success = target.damage_with_context( + &*target, + amount, + damage_type, + Some(location), + None, + None, + ); send_damage_result(sender, success, amount, target.get_display_name().await).await }) @@ -113,16 +118,14 @@ impl CommandExecutor for EntityExecutor { None }; - let success = target - .damage_with_context( - &*target, - amount, - damage_type, - None, - source.as_ref().map(|e| e.as_ref() as &dyn EntityBase), - cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase), - ) - .await; + let success = target.damage_with_context( + &*target, + amount, + damage_type, + None, + source.as_ref().map(|e| e.as_ref() as &dyn EntityBase), + cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase), + ); send_damage_result(sender, success, amount, target.get_display_name().await).await }) diff --git a/crates/pumpkin/src/command/commands/difficulty.rs b/crates/pumpkin/src/command/commands/difficulty.rs index 5c8d3eda9..604138dbc 100644 --- a/crates/pumpkin/src/command/commands/difficulty.rs +++ b/crates/pumpkin/src/command/commands/difficulty.rs @@ -59,7 +59,7 @@ impl CommandExecutor for DifficultySetExecutor { } } - server.set_difficulty(difficulty, true).await; + server.set_difficulty(difficulty, true); context .source diff --git a/crates/pumpkin/src/command/commands/effect.rs b/crates/pumpkin/src/command/commands/effect.rs index 632879a23..d79ba145a 100644 --- a/crates/pumpkin/src/command/commands/effect.rs +++ b/crates/pumpkin/src/command/commands/effect.rs @@ -91,21 +91,18 @@ impl CommandExecutor for GiveExecutor { let should_skip = target .living_entity .get_effect(effect) - .await .is_some_and(|existing| existing.amplifier >= amplifier); if !should_skip { - target - .add_effect(Effect { - effect_type: effect, - duration: second, - amplifier, - ambient: false, //this is not a beacon effect - show_particles: !hide_particles, - show_icon: true, - blend: true, //Currently only used in the DARKNESS effect to apply extra void fog and adjust the gamma value for lighting. - }) - .await; + target.add_effect(Effect { + effect_type: effect, + duration: second, + amplifier, + ambient: false, //this is not a beacon effect + show_particles: !hide_particles, + show_icon: true, + blend: false, + }); successes += 1; } } @@ -208,7 +205,7 @@ impl CommandExecutor for ClearExecutor { let mut succeeded_clears: i32 = 0; for target in targets { - if target.living_entity.has_effect(effect).await { + if target.living_entity.has_effect(effect) { target.remove_effect(effect).await; succeeded_clears += 1; } diff --git a/crates/pumpkin/src/command/commands/enchant.rs b/crates/pumpkin/src/command/commands/enchant.rs index aea0d3531..7922b4c72 100644 --- a/crates/pumpkin/src/command/commands/enchant.rs +++ b/crates/pumpkin/src/command/commands/enchant.rs @@ -135,7 +135,7 @@ async fn enchant_target( return Err(commands_enchant_failed()); }; - let mut item = player.inventory().held_item().await; + let mut item = player.inventory().held_item(); if item.is_empty() { let msg = TextComponent::translate_cross( @@ -168,7 +168,7 @@ async fn enchant_target( item.enchant(enchantment, level); let inventory = player.inventory(); - inventory.set_held_item(item.clone()).await; + inventory.set_held_item(item.clone()); player .sync_hand_slot(inventory.get_selected_slot() as usize, item) diff --git a/crates/pumpkin/src/command/commands/execute.rs b/crates/pumpkin/src/command/commands/execute.rs index 93b31e21d..30d0c2093 100644 --- a/crates/pumpkin/src/command/commands/execute.rs +++ b/crates/pumpkin/src/command/commands/execute.rs @@ -385,7 +385,7 @@ fn execute_summon_modifier<'a>( context.source.world(), Uuid::new_v4(), ); - context.source.world().spawn_entity(entity.clone()).await; + context.source.world().spawn_entity(entity.clone()); let mut source = context.source.as_ref().clone(); source.entity = Some(entity); Ok(vec![Arc::new(source)]) diff --git a/crates/pumpkin/src/command/commands/fill.rs b/crates/pumpkin/src/command/commands/fill.rs index 665216dc6..9df4acfcd 100644 --- a/crates/pumpkin/src/command/commands/fill.rs +++ b/crates/pumpkin/src/command/commands/fill.rs @@ -88,14 +88,14 @@ impl Context { } trait Filler { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult; + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult; - async fn execute_for_region(context: &mut Context) { + fn execute_for_region(context: &mut Context) { for x in context.start_x..=context.end_x { for y in context.start_y..=context.end_y { for z in context.start_z..=context.end_z { let block_position = BlockPos(Vector3::new(x, y, z)); - let filler_result = Self::execute_for_pos(context, block_position).await; + let filler_result = Self::execute_for_pos(context, block_position); match filler_result { FillerResult::PlacedBlock => { context.placed_blocks += 1; @@ -114,54 +114,46 @@ trait Filler { struct DestroyFiller; impl Filler for DestroyFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { if let Some(filter) = &context.option_filter && not_in_filter(filter, context.world.get_block(&block_position)) { return FillerResult::DidNotPlaceBlock; } - context - .world - .break_block( - &block_position, - None, - BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE, - ) - .await; - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; + context.world.break_block( + &block_position, + None, + BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE, + ); + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::FORCE_STATE, + ); FillerResult::PlacedBlock } } struct HollowFiller; impl Filler for HollowFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { if let Some(filter) = &context.option_filter && not_in_filter(filter, context.world.get_block(&block_position)) { return FillerResult::DidNotPlaceBlock; } if context.is_edge(block_position) { - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::FORCE_STATE, + ); } else { - context - .world - .set_block_state(&block_position, BlockStateId::AIR, BlockFlags::FORCE_STATE) - .await; + context.world.set_block_state( + &block_position, + BlockStateId::AIR, + BlockFlags::FORCE_STATE, + ); } FillerResult::PlacedBlock } @@ -169,7 +161,7 @@ impl Filler for HollowFiller { struct KeepFiller; impl Filler for KeepFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { let (old_block, old_state) = context.world.get_block_and_state(&block_position); if old_state.is_air() { if let Some(filter) = &context.option_filter @@ -177,14 +169,11 @@ impl Filler for KeepFiller { { return FillerResult::DidNotPlaceBlock; } - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::FORCE_STATE, + ); FillerResult::PlacedBlock } else { FillerResult::DidNotPlaceBlock @@ -194,7 +183,7 @@ impl Filler for KeepFiller { struct OutlineFiller; impl Filler for OutlineFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { if !context.is_edge(block_position) { return FillerResult::DidNotPlaceBlock; } @@ -203,54 +192,45 @@ impl Filler for OutlineFiller { { return FillerResult::DidNotPlaceBlock; } - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::FORCE_STATE, + ); FillerResult::PlacedBlock } } struct ReplaceFiller; impl Filler for ReplaceFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { if let Some(filter) = &context.option_filter && not_in_filter(filter, context.world.get_block(&block_position)) { return FillerResult::DidNotPlaceBlock; } - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::FORCE_STATE, + ); FillerResult::PlacedBlock } } struct StrictFiller; impl Filler for StrictFiller { - async fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { + fn execute_for_pos(context: &Context, block_position: BlockPos) -> FillerResult { if let Some(filter) = &context.option_filter && not_in_filter(filter, context.world.get_block(&block_position)) { return FillerResult::DidNotPlaceBlock; } - context - .world - .set_block_state( - &block_position, - context.block_state_id, - BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; + context.world.set_block_state( + &block_position, + context.block_state_id, + BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); FillerResult::PlacedBlockWithoutUpdate } } @@ -316,16 +296,16 @@ impl CommandExecutor for Executor { } match mode { - Mode::Destroy => DestroyFiller::execute_for_region(&mut context).await, - Mode::Replace => ReplaceFiller::execute_for_region(&mut context).await, - Mode::Keep => KeepFiller::execute_for_region(&mut context).await, - Mode::Hollow => HollowFiller::execute_for_region(&mut context).await, - Mode::Outline => OutlineFiller::execute_for_region(&mut context).await, - Mode::Strict => StrictFiller::execute_for_region(&mut context).await, + Mode::Destroy => DestroyFiller::execute_for_region(&mut context), + Mode::Replace => ReplaceFiller::execute_for_region(&mut context), + Mode::Keep => KeepFiller::execute_for_region(&mut context), + Mode::Hollow => HollowFiller::execute_for_region(&mut context), + Mode::Outline => OutlineFiller::execute_for_region(&mut context), + Mode::Strict => StrictFiller::execute_for_region(&mut context), } for i in context.to_update { - context.world.update_neighbors(&i, None).await; + context.world.update_neighbors(&i, None); } if context.placed_blocks == 0 { diff --git a/crates/pumpkin/src/command/commands/give.rs b/crates/pumpkin/src/command/commands/give.rs index e0e0a2e05..7b704a1f3 100644 --- a/crates/pumpkin/src/command/commands/give.rs +++ b/crates/pumpkin/src/command/commands/give.rs @@ -52,9 +52,9 @@ impl CommandExecutor for Executor { let take = remaining.min(max_stack); let mut stack = parsed_stack.clone(); stack.item_count = take as u8; - target.inventory().insert_stack_anywhere(&mut stack).await; + target.inventory().insert_stack_anywhere(&mut stack); if !stack.is_empty() { - target.drop_item(stack).await; + target.drop_item(stack); } remaining -= take; } diff --git a/crates/pumpkin/src/command/commands/item.rs b/crates/pumpkin/src/command/commands/item.rs index bdff6404a..01b366a9c 100644 --- a/crates/pumpkin/src/command/commands/item.rs +++ b/crates/pumpkin/src/command/commands/item.rs @@ -238,7 +238,7 @@ impl CommandExecutor for EntityReplaceExecutor { living .entity_equipment .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .put(&eq, item_stack.clone()); living.send_equipment_changes(&[(eq, item_stack.clone())]); modified_count += 1; diff --git a/crates/pumpkin/src/command/commands/locate.rs b/crates/pumpkin/src/command/commands/locate.rs index c3a2352d6..22efb0256 100644 --- a/crates/pumpkin/src/command/commands/locate.rs +++ b/crates/pumpkin/src/command/commands/locate.rs @@ -345,7 +345,10 @@ impl CommandExecutor for LocatePoiExecutor { let world = context.source.world().clone(); let found = { - let mut poi_storage = world.portal_poi.lock().await; + let mut poi_storage = world + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); poi_storage.find_closest_matching(origin, POI_SEARCH_RADIUS, |poi_type| { targets.contains(poi_type) }) diff --git a/crates/pumpkin/src/command/commands/loot.rs b/crates/pumpkin/src/command/commands/loot.rs index f2a9ee92a..1d3c4f58a 100644 --- a/crates/pumpkin/src/command/commands/loot.rs +++ b/crates/pumpkin/src/command/commands/loot.rs @@ -170,9 +170,9 @@ impl CommandExecutor for LootExecutor { for player in &targets { for stack in &stacks { let mut remaining = stack.clone(); - player.inventory.insert_stack_anywhere(&mut remaining).await; + player.inventory.insert_stack_anywhere(&mut remaining); if !remaining.is_empty() { - player.drop_item(remaining).await; + player.drop_item(remaining); } } } @@ -181,7 +181,7 @@ impl CommandExecutor for LootExecutor { let pos = BlockPosArgumentType::get_block_pos(context, "pos")?; let world = context.world(); for stack in stacks { - world.drop_stack(&pos, stack).await; + world.drop_stack(&pos, stack); } } Target::Insert => { @@ -193,7 +193,7 @@ impl CommandExecutor for LootExecutor { let remaining = insert_into_inventory(inventory.as_ref(), stack).await; if !remaining.is_empty() { - world.drop_stack(&pos, remaining).await; + world.drop_stack(&pos, remaining); } } } else { diff --git a/crates/pumpkin/src/command/commands/place.rs b/crates/pumpkin/src/command/commands/place.rs index 249fca0e2..435640598 100644 --- a/crates/pumpkin/src/command/commands/place.rs +++ b/crates/pumpkin/src/command/commands/place.rs @@ -158,9 +158,8 @@ impl CommandExecutor for PlaceTemplateExecutor { placer.finalize(); context .world() - .queue_block_updates(&placer.changed_positions) - .await; - context.world().flush_block_updates().await; + .queue_block_updates(&placer.changed_positions); + context.world().flush_block_updates(); context .source @@ -251,9 +250,8 @@ impl CommandExecutor for PlaceJigsawExecutor { placer.finalize(); context .world() - .queue_block_updates(&placer.changed_positions) - .await; - context.world().flush_block_updates().await; + .queue_block_updates(&placer.changed_positions); + context.world().flush_block_updates(); context .source @@ -517,9 +515,8 @@ impl CommandExecutor for PlaceStructureExecutor { placer.finalize(); context .world() - .queue_block_updates(&placer.changed_positions) - .await; - context.world().flush_block_updates().await; + .queue_block_updates(&placer.changed_positions); + context.world().flush_block_updates(); context .source @@ -633,9 +630,8 @@ impl CommandExecutor for PlaceFeatureExecutor { placer.finalize(); context .world() - .queue_block_updates(&placer.changed_positions) - .await; - context.world().flush_block_updates().await; + .queue_block_updates(&placer.changed_positions); + context.world().flush_block_updates(); context .source diff --git a/crates/pumpkin/src/command/commands/raid.rs b/crates/pumpkin/src/command/commands/raid.rs index db8bd8261..47ba2cbcf 100644 --- a/crates/pumpkin/src/command/commands/raid.rs +++ b/crates/pumpkin/src/command/commands/raid.rs @@ -7,6 +7,8 @@ use pumpkin_data::potion::Effect; use pumpkin_data::sound::Sound; use pumpkin_util::text::TextComponent; +use std::sync::Arc; + use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::dispatcher::CommandError; @@ -15,8 +17,10 @@ use crate::command::tree::builder::{argument, literal}; use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; use crate::entity::mob::raider::create_ominous_banner; +use crate::entity::player::Player; use crate::entity::r#type::from_type; use crate::server::Server; +use crate::world::raid::RaidStatus; const NAMES: [&str; 1] = ["raid"]; const DESCRIPTION: &str = "Controls or queries village raids."; @@ -41,28 +45,38 @@ impl CommandExecutor for StartExecutor { let pos = entity.block_pos.load(); let world = entity.world.load(); - let mut raids = world.raids.lock().await; - if raids.get_raid_at(&pos).is_some() { + let (is_already_started, raid_created) = { + let mut raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if raids.get_raid_at(&pos).is_some() { + (true, None) + } else { + let omen_lvl = if self.has_omen_lvl { + BoundedNumArgumentConsumer::::find_arg(args, ARG_OMEN_LVL) + .ok() + .and_then(Result::ok) + .unwrap_or(1) + } else { + 1 + }; + let raid_id = raids.create_or_extend_raid(&player, pos, &world); + if let Some(id) = raid_id + && let Some(raid) = raids.get_mut(id) + { + raid.set_raid_omen_level(omen_lvl); + } + (false, raid_id) + } + }; + if is_already_started { sender .send_message(TextComponent::text("Raid already started close by")) .await; return Ok(0); } - - let omen_lvl = if self.has_omen_lvl { - BoundedNumArgumentConsumer::::find_arg(args, ARG_OMEN_LVL) - .ok() - .and_then(Result::ok) - .unwrap_or(1) - } else { - 1 - }; - - let raid_id = raids.create_or_extend_raid(&player, pos, &world); - if let Some(id) = raid_id { - if let Some(raid) = raids.get_mut(id) { - raid.set_raid_omen_level(omen_lvl); - } + if raid_created.is_some() { sender .send_message(TextComponent::text("Created a raid in your local village")) .await; @@ -94,9 +108,32 @@ impl CommandExecutor for StopExecutor { let pos = entity.block_pos.load(); let world = entity.world.load(); - let mut raids = world.raids.lock().await; - if let Some(raid) = raids.get_raid_at_mut(&pos) { - raid.stop(&world).await; + let stopped = { + let mut raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(raid) = raids.get_raid_at_mut(&pos) { + raid.active = false; + raid.status = RaidStatus::Stopped; + let players_to_remove: Vec> = world + .players + .load() + .iter() + .filter(|p| raid.players_in_raid.contains(&p.gameprofile.id)) + .cloned() + .collect(); + let bossbar_uuid = raid.bossbar.uuid; + raid.players_in_raid.clear(); + Some((players_to_remove, bossbar_uuid)) + } else { + None + } + }; + if let Some((players, bossbar_uuid)) = stopped { + for p in players { + p.remove_bossbar(bossbar_uuid); + } sender .send_message(TextComponent::text("Stopped raid")) .await; @@ -126,21 +163,28 @@ impl CommandExecutor for CheckExecutor { let pos = entity.block_pos.load(); let world = entity.world.load(); - let raids = world.raids.lock().await; - if let Some(raid) = raids.get_raid_at(&pos) { + let info = { + let raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + raids.get_raid_at(&pos).map(|raid| { + let alive = raid.get_total_raiders_alive(); + let living_health = raid.get_health_of_living_raiders(&world); + format!( + "Num groups spawned: {} Raid omen level: {} Num mobs: {} Raid health: {} / {}", + raid.get_groups_spawned(), + raid.get_raid_omen_level(), + alive, + living_health, + raid.total_health + ) + }) + }; + if let Some(msg) = info { sender .send_message(TextComponent::text("Found a started raid!")) .await; - let alive = raid.get_total_raiders_alive(); - let living_health = raid.get_health_of_living_raiders(&world); - let msg = format!( - "Num groups spawned: {} Raid omen level: {} Num mobs: {} Raid health: {} / {}", - raid.get_groups_spawned(), - raid.get_raid_omen_level(), - alive, - living_health, - raid.total_health - ); sender.send_message(TextComponent::text(msg)).await; Ok(1) } else { @@ -203,13 +247,16 @@ impl CommandExecutor for SpawnLeaderExecutor { raider.set_patrol_leader(true); let banner = create_ominous_banner(); let living = &mob.get_mob_entity().living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment.put(&EquipmentSlot::HEAD, banner.clone()); drop(equipment); living.send_equipment_changes(&[(EquipmentSlot::HEAD, banner)]); } - world.spawn_entity(raider_entity).await; + world.spawn_entity(raider_entity); sender .send_message(TextComponent::text("Spawned a raid captain")) .await; @@ -238,29 +285,36 @@ impl CommandExecutor for SetOmenExecutor { .and_then(Result::ok) .unwrap_or(1); - let mut raids = world.raids.lock().await; - if let Some(raid) = raids.get_raid_at_mut(&pos) { - if level > 5 { + let res = { + let mut raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + raids + .get_raid_at_mut(&pos) + .map_or(Err("No raid found here"), |raid| { + if level > 5 { + Err("Sorry, the max raid omen level you can set is 5") + } else { + let before = raid.get_raid_omen_level(); + raid.set_raid_omen_level(level); + Ok(before) + } + }) + }; + match res { + Ok(before) => { sender - .send_message(TextComponent::text( - "Sorry, the max raid omen level you can set is 5", - )) + .send_message(TextComponent::text(format!( + "Changed village's raid omen level from {before} to {level}" + ))) .await; - return Ok(0); + Ok(1) + } + Err(msg) => { + sender.send_message(TextComponent::text(msg)).await; + Ok(0) } - let before = raid.get_raid_omen_level(); - raid.set_raid_omen_level(level); - sender - .send_message(TextComponent::text(format!( - "Changed village's raid omen level from {before} to {level}" - ))) - .await; - Ok(1) - } else { - sender - .send_message(TextComponent::text("No raid found here")) - .await; - Ok(0) } }) } @@ -281,8 +335,16 @@ impl CommandExecutor for GlowExecutor { let pos = entity.block_pos.load(); let world = entity.world.load(); - let raids = world.raids.lock().await; - if let Some(raid) = raids.get_raid_at(&pos) { + let raiders = { + let raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + raids + .get_raid_at(&pos) + .map(crate::world::raid::Raid::get_all_raiders) + }; + if let Some(raider_uuids) = raiders { let effect = Effect { effect_type: &StatusEffect::GLOWING, duration: 1000, @@ -292,11 +354,11 @@ impl CommandExecutor for GlowExecutor { show_icon: true, blend: true, }; - for raider_uuid in raid.get_all_raiders() { + for raider_uuid in raider_uuids { if let Some(e) = world.get_entity_by_uuid(raider_uuid) && let Some(living) = e.get_living_entity() { - living.add_effect(effect.clone()).await; + living.add_effect(effect.clone()); } } Ok(1) diff --git a/crates/pumpkin/src/command/commands/setblock.rs b/crates/pumpkin/src/command/commands/setblock.rs index 79d457ec6..7e88da216 100644 --- a/crates/pumpkin/src/command/commands/setblock.rs +++ b/crates/pumpkin/src/command/commands/setblock.rs @@ -50,52 +50,41 @@ impl CommandExecutor for Executor { let success = match mode { Mode::Destroy => { - world - .clone() - .break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE) - .await; - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE); + world.set_block_state( + &pos, + block_state_id, + BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, + ); true } Mode::Replace => { - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + &pos, + block_state_id, + BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, + ); true } Mode::Keep => { let old_state = world.get_block_state(&pos); if old_state.is_air() { - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + &pos, + block_state_id, + BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, + ); true } else { false } } Mode::Strict => { - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; + world.set_block_state( + &pos, + block_state_id, + BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); true } }; diff --git a/crates/pumpkin/src/command/commands/summon.rs b/crates/pumpkin/src/command/commands/summon.rs index 61b900df9..da5611c3e 100644 --- a/crates/pumpkin/src/command/commands/summon.rs +++ b/crates/pumpkin/src/command/commands/summon.rs @@ -67,7 +67,7 @@ impl CommandExecutor for Executor { }; let entity = from_type(entity_type, pos, &world, Uuid::new_v4()); let name = entity.get_display_name().await; - world.spawn_entity(entity).await; + world.spawn_entity(entity); sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_SUMMON_SUCCESS, diff --git a/crates/pumpkin/src/command/commands/tick.rs b/crates/pumpkin/src/command/commands/tick.rs index b4e9064f5..d71af2a20 100644 --- a/crates/pumpkin/src/command/commands/tick.rs +++ b/crates/pumpkin/src/command/commands/tick.rs @@ -141,7 +141,7 @@ impl TickExecutor { let sample_size = (tick_count as usize).min(100); if sample_size > 0 { - let mut tick_times = server.get_tick_times_nanos_copy().await; + let mut tick_times = server.get_tick_times_nanos_copy(); let relevant_ticks = &mut tick_times[..sample_size]; relevant_ticks.sort_unstable(); diff --git a/crates/pumpkin/src/command/commands/time.rs b/crates/pumpkin/src/command/commands/time.rs index 21deb86ae..1b25073bc 100644 --- a/crates/pumpkin/src/command/commands/time.rs +++ b/crates/pumpkin/src/command/commands/time.rs @@ -73,7 +73,11 @@ impl CommandExecutor for QueryExecutor { let mode = self.0; let worlds = server.worlds.load(); let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; - let level_time = world.level_time.lock().await; + let level_time = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); match mode { QueryMode::GameTime => { @@ -129,6 +133,7 @@ impl CommandExecutor for QueryExecutor { struct ActionExecutor(Action); impl CommandExecutor for ActionExecutor { + #[allow(clippy::too_many_lines)] fn execute<'a>( &'a self, sender: &'a CommandSender, @@ -141,7 +146,6 @@ impl CommandExecutor for ActionExecutor { let action = self.0; let worlds = server.worlds.load(); let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; - let mut level_time = world.level_time.lock().await; match action { Action::Set(preset) => { @@ -150,8 +154,15 @@ impl CommandExecutor for ActionExecutor { } else { TimeArgumentConsumer::find_arg(args, ARG_TIME)? }; - level_time.set_time(time_count.into()); - level_time.send_time(world).await; + let level_time = { + let mut guard = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.set_time(time_count.into()); + guard.clone() + }; + level_time.send_time(world); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_TIME_SET_ABSOLUTE, @@ -164,9 +175,16 @@ impl CommandExecutor for ActionExecutor { } Action::Add => { let time_count = TimeArgumentConsumer::find_arg(args, ARG_TIME)?; - level_time.add_time(time_count.into()); - level_time.send_time(world).await; - let total_ticks = level_time.time_of_day; + let (level_time, total_ticks) = { + let mut guard = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.add_time(time_count.into()); + let total_ticks = guard.time_of_day; + (guard.clone(), total_ticks) + }; + level_time.send_time(world); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_TIME_SET_ABSOLUTE, @@ -178,8 +196,15 @@ impl CommandExecutor for ActionExecutor { Ok(wrap_time(total_ticks)) } Action::Pause => { - level_time.set_paused(true); - level_time.send_time(world).await; + let level_time = { + let mut guard = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.set_paused(true); + guard.clone() + }; + level_time.send_time(world); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_TIME_PAUSE, @@ -190,8 +215,15 @@ impl CommandExecutor for ActionExecutor { Ok(1) } Action::Resume => { - level_time.set_paused(false); - level_time.send_time(world).await; + let level_time = { + let mut guard = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.set_paused(false); + guard.clone() + }; + level_time.send_time(world); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_TIME_RESUME, @@ -207,8 +239,15 @@ impl CommandExecutor for ActionExecutor { Ok(val) => val, Err(err) => return Err(err.into()), }; - level_time.set_rate(rate); - level_time.send_time(world).await; + let level_time = { + let mut guard = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.set_rate(rate); + guard.clone() + }; + level_time.send_time(world); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_TIME_RATE, diff --git a/crates/pumpkin/src/command/commands/weather.rs b/crates/pumpkin/src/command/commands/weather.rs index f11bbc8bc..37455de85 100644 --- a/crates/pumpkin/src/command/commands/weather.rs +++ b/crates/pumpkin/src/command/commands/weather.rs @@ -41,49 +41,50 @@ impl CommandExecutor for Executor { .cloned() .ok_or(CommandError::InvalidRequirement)? }; - let mut weather = world.weather.lock().await; + let message = { + let mut weather = world + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - match self.mode { - WeatherMode::Clear => { - let processed_duration = - duration.unwrap_or_else(|| rand::random_range(12_000..=180_000)); + match self.mode { + WeatherMode::Clear => { + let processed_duration = + duration.unwrap_or_else(|| rand::random_range(12_000..=180_000)); - weather.set_weather_parameters(&world, processed_duration, 0, false, false); - sender - .send_message(TextComponent::translate_cross( + weather.set_weather_parameters(&world, processed_duration, 0, false, false); + TextComponent::translate_cross( translation::java::COMMANDS_WEATHER_SET_CLEAR, translation::bedrock::COMMANDS_WEATHER_CLEAR, [], - )) - .await; - } - WeatherMode::Rain => { - let processed_duration = - duration.unwrap_or_else(|| rand::random_range(12_000..=24_000)); + ) + } + WeatherMode::Rain => { + let processed_duration = + duration.unwrap_or_else(|| rand::random_range(12_000..=24_000)); - weather.set_weather_parameters(&world, 0, processed_duration, true, false); - sender - .send_message(TextComponent::translate_cross( + weather.set_weather_parameters(&world, 0, processed_duration, true, false); + TextComponent::translate_cross( translation::java::COMMANDS_WEATHER_SET_RAIN, translation::bedrock::COMMANDS_WEATHER_RAIN, [], - )) - .await; - } - WeatherMode::Thunder => { - let processed_duration = - duration.unwrap_or_else(|| rand::random_range(3_600..=15_600)); + ) + } + WeatherMode::Thunder => { + let processed_duration = + duration.unwrap_or_else(|| rand::random_range(3_600..=15_600)); - weather.set_weather_parameters(&world, 0, processed_duration, true, true); - sender - .send_message(TextComponent::translate_cross( + weather.set_weather_parameters(&world, 0, processed_duration, true, true); + TextComponent::translate_cross( translation::java::COMMANDS_WEATHER_SET_THUNDER, translation::bedrock::COMMANDS_WEATHER_THUNDER, [], - )) - .await; + ) + } } - } + }; + + sender.send_message(message).await; // Vanilla returns -1 when duration is not specified Ok(duration.unwrap_or(-1)) diff --git a/crates/pumpkin/src/command/mod.rs b/crates/pumpkin/src/command/mod.rs index ff704c44f..6917e8165 100644 --- a/crates/pumpkin/src/command/mod.rs +++ b/crates/pumpkin/src/command/mod.rs @@ -111,7 +111,10 @@ impl CommandSender { Self::Player(c) => c.send_system_message(&text).await, Self::Rcon(s) => s.lock().await.push(text.to_pretty_console()), Self::CommandBlock(block_entity, _) => { - let mut last_output = block_entity.last_output.lock().await; + let mut last_output = block_entity + .last_output + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let now = time::OffsetDateTime::now_utc(); let format = time::macros::format_description!("[hour]:[minute]:[second]"); diff --git a/crates/pumpkin/src/entity/ai/goal/active_target.rs b/crates/pumpkin/src/entity/ai/goal/active_target.rs index 30b1e3c5e..5cd1fa284 100644 --- a/crates/pumpkin/src/entity/ai/goal/active_target.rs +++ b/crates/pumpkin/src/entity/ai/goal/active_target.rs @@ -1,5 +1,5 @@ use super::{Controls, Goal, to_goal_ticks}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::goal::track_target::TrackTargetGoal; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::living::LivingEntity; @@ -9,7 +9,6 @@ use crate::world::World; use pumpkin_data::attributes::Attributes; use pumpkin_data::entity::EntityType; use rand::RngExt; -use std::future::Future; use std::sync::Arc; const DEFAULT_RECIPROCAL_CHANCE: i32 = 10; @@ -23,7 +22,7 @@ pub struct ActiveTargetGoal { } impl ActiveTargetGoal { - pub fn new( + pub fn new( mob: &MobEntity, target_type: &'static EntityType, reciprocal_chance: i32, @@ -32,8 +31,7 @@ impl ActiveTargetGoal { predicate: Option, ) -> Self where - F: Fn(Arc, Arc) -> Fut + Send + Sync + 'static, - Fut: Future + Send + 'static, + F: Fn(&LivingEntity, &World) -> bool + Send + Sync + 'static, { let track_target_goal = TrackTargetGoal::new(check_visibility, check_can_navigate); let mut target_predicate = TargetPredicate::create_attackable(); @@ -79,7 +77,7 @@ impl ActiveTargetGoal { self.target = target; } - async fn find_closest_target(&mut self, mob: &MobEntity) { + fn find_closest_target(&mut self, mob: &MobEntity) { let follow_range = mob .living_entity .get_attribute_value(&Attributes::FOLLOW_RANGE); @@ -103,7 +101,6 @@ impl ActiveTargetGoal { && self .target_predicate .test(&world, Some(&mob.living_entity), living) - .await { self.target = Some(potential_entity); return; @@ -117,7 +114,6 @@ impl ActiveTargetGoal { && self .target_predicate .test(&world, Some(&mob.living_entity), living) - .await { self.target = Some(potential_entity); return; @@ -128,33 +124,27 @@ impl ActiveTargetGoal { } impl Goal for ActiveTargetGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - if self.reciprocal_chance > 0 - && mob.get_random().random_range(0..self.reciprocal_chance) != 0 - { - return false; - } - self.find_closest_target(mob.get_mob_entity()).await; - self.target.is_some() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if self.reciprocal_chance > 0 + && mob.get_random().random_range(0..self.reciprocal_chance) != 0 + { + return false; + } + self.find_closest_target(mob.get_mob_entity()); + self.target.is_some() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.track_target_goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.track_target_goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - mob.set_mob_target(self.target.clone()).await; - self.track_target_goal.start(mob).await; - }) + fn start(&mut self, mob: &dyn Mob) { + mob.set_mob_target(self.target.clone()); + self.track_target_goal.start(mob); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.track_target_goal.stop(mob).await; - }) + fn stop(&mut self, mob: &dyn Mob) { + self.track_target_goal.stop(mob); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/ambient_stand.rs b/crates/pumpkin/src/entity/ai/goal/ambient_stand.rs index 183ae2732..a6ee7f5ca 100644 --- a/crates/pumpkin/src/entity/ai/goal/ambient_stand.rs +++ b/crates/pumpkin/src/entity/ai/goal/ambient_stand.rs @@ -1,5 +1,5 @@ -use super::{Controls, Goal}; -use crate::entity::{ai::goal::GoalFuture, mob::Mob}; +use crate::entity::ai::goal::{Controls, Goal}; +use crate::entity::mob::Mob; use rand::RngExt; pub struct AmbientStandGoal { @@ -16,15 +16,13 @@ impl AmbientStandGoal { } impl Goal for AmbientStandGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - self.cooldown += 1; - if self.cooldown > 0 && mob.get_random().random_range(0..1000) < self.cooldown { - self.reset_cooldown(); - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.cooldown += 1; + if self.cooldown > 0 && mob.get_random().random_range(0..1000) < self.cooldown { + self.reset_cooldown(); + } - false - }) + false } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/avoid_entity.rs b/crates/pumpkin/src/entity/ai/goal/avoid_entity.rs index 3d86c4eca..312ab1f65 100644 --- a/crates/pumpkin/src/entity/ai/goal/avoid_entity.rs +++ b/crates/pumpkin/src/entity/ai/goal/avoid_entity.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::{EntityBase, ai::pathfinder::NavigatorGoal, mob::Mob}; use pumpkin_data::entity::EntityType; use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; @@ -127,80 +127,70 @@ impl AvoidEntityGoal { } impl Goal for AvoidEntityGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let threat = self.find_threat(mob); - let Some(target) = threat else { - return false; - }; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let threat = self.find_threat(mob); + let Some(target) = threat else { + return false; + }; - let threat_pos = target.get_entity().pos.load(); - let flee_pos = Self::find_flee_position(mob, &threat_pos); - let Some(pos) = flee_pos else { - return false; - }; + let threat_pos = target.get_entity().pos.load(); + let flee_pos = Self::find_flee_position(mob, &threat_pos); + let Some(pos) = flee_pos else { + return false; + }; - self.target = Some(target); - self.flee_pos = Some(pos); - true - }) + self.target = Some(target); + self.flee_pos = Some(pos); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let navigator = mob + fn should_continue(&self, mob: &dyn Mob) -> bool { + let navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + !navigator.is_idle() + } + + fn start(&mut self, mob: &dyn Mob) { + if let Some(flee_pos) = self.flee_pos { + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let mut navigator = mob .get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - !navigator.is_idle() - }) + navigator.set_progress(NavigatorGoal::new(mob_pos, flee_pos, self.slow_speed)); + } } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(flee_pos) = self.flee_pos { - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(mob_pos, flee_pos, self.slow_speed)); - } - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target) = &self.target { - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let threat_pos = target.get_entity().pos.load(); - let dist_sq = mob_pos.squared_distance_to_vec(&threat_pos); - let speed = if dist_sq < FAST_DISTANCE_SQ { - self.fast_speed - } else { - self.slow_speed - }; - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_speed(speed); - } - }) + fn tick(&mut self, mob: &dyn Mob) { + if let Some(target) = &self.target { + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let threat_pos = target.get_entity().pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(&threat_pos); + let speed = if dist_sq < FAST_DISTANCE_SQ { + self.fast_speed + } else { + self.slow_speed + }; + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.set_speed(speed); + } } fn should_run_every_tick(&self) -> bool { true } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target = None; - self.flee_pos = None; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.target = None; + self.flee_pos = None; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/beg.rs b/crates/pumpkin/src/entity/ai/goal/beg.rs index 5d6a4801f..b62e69e50 100644 --- a/crates/pumpkin/src/entity/ai/goal/beg.rs +++ b/crates/pumpkin/src/entity/ai/goal/beg.rs @@ -1,4 +1,4 @@ -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::EntityBase; use crate::entity::mob::Mob; use crate::entity::player::Player; @@ -28,13 +28,13 @@ impl BegGoal { item.id == Item::BONE.id || item.has_tag(&tag::Item::MINECRAFT_WOLF_FOOD) } - async fn player_holding_interesting(&self, player: &Player) -> bool { - let main_stack = player.inventory().held_item().await; + fn player_holding_interesting(player: &Player) -> bool { + let main_stack = player.inventory().held_item(); if main_stack.item_count > 0 && Self::is_interesting_item(main_stack.item) { return true; } - let off_stack = player.inventory().off_hand_item().await; + let off_stack = player.inventory().off_hand_item(); off_stack.item_count > 0 && Self::is_interesting_item(off_stack.item) } @@ -56,78 +56,79 @@ impl BegGoal { } impl Goal for BegGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let entity = &mob.get_mob_entity().living_entity.entity; - let world = entity.world.load_full(); - let pos = entity.pos.load(); - let radius = self.look_distance_sq.sqrt(); + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + let world = mob_entity.living_entity.entity.world.load(); + let pos = mob_entity.living_entity.entity.pos.load(); - let Some(player) = world.get_closest_player(pos, radius) else { - return false; - }; + let mut closest_player = None; + let mut min_distance = self.look_distance_sq; - if !self.player_holding_interesting(&player).await { - return false; + for player in world.get_nearby_players(pos, 8.0) { + let distance = Self::distance_sq(mob, &player); + + if distance < min_distance { + min_distance = distance; + closest_player = Some(player); } + } - self.player = Some(player); - true - }) + let Some(player) = closest_player else { + return false; + }; + + if !Self::player_holding_interesting(&player) { + return false; + } + + self.player = Some(player); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(player) = &self.player else { - return false; - }; + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(player) = &self.player else { + return false; + }; - if !player.get_entity().is_alive() { - return false; - } + if !player.get_entity().is_alive() { + return false; + } - if Self::distance_sq(mob, player) > self.look_distance_sq { - return false; - } + if Self::distance_sq(mob, player) > self.look_distance_sq { + return false; + } - self.look_time > 0 && self.player_holding_interesting(player).await - }) + self.look_time > 0 && Self::player_holding_interesting(player) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - Self::set_is_interested(mob, true); - let ticks = 40 + mob.get_random().random_range(0..40); - self.look_time = self.get_tick_count(ticks); - }) + fn start(&mut self, mob: &dyn Mob) { + Self::set_is_interested(mob, true); + let ticks = 40 + mob.get_random().random_range(0..40); + self.look_time = self.get_tick_count(ticks); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - Self::set_is_interested(mob, false); - self.player = None; - }) + fn stop(&mut self, mob: &dyn Mob) { + Self::set_is_interested(mob, false); + self.player = None; } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(player) = &self.player { - let player_pos = player.get_entity().get_eye_pos(); - let mut look_control = mob - .get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.look_at_with_range( - player_pos.x, - player_pos.y, - player_pos.z, - 10.0, - mob.get_max_look_pitch_change(), - ); - } - self.look_time -= 1; - }) + fn tick(&mut self, mob: &dyn Mob) { + if let Some(player) = &self.player { + let player_pos = player.get_entity().get_eye_pos(); + let mut look_control = mob + .get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + look_control.look_at_with_range( + player_pos.x, + player_pos.y, + player_pos.z, + 10.0, + mob.get_max_look_pitch_change(), + ); + } + self.look_time -= 1; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/blaze_attack.rs b/crates/pumpkin/src/entity/ai/goal/blaze_attack.rs index ad6d493d8..9ef281af9 100644 --- a/crates/pumpkin/src/entity/ai/goal/blaze_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/blaze_attack.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use crate::entity::{ Entity, - ai::goal::{Controls, Goal, GoalFuture}, + ai::goal::{Controls, Goal}, mob::Mob, mob::blaze::BlazeEntity, projectile::small_fireball::SmallFireballEntity, @@ -36,184 +36,172 @@ impl BlazeShootFireballGoal { } impl Goal for BlazeShootFireballGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(blaze) = self.blaze.upgrade() else { - return false; - }; - let target = blaze.entity.target.lock().await.clone(); - if target.is_some() { - // TODO: check is_alive - true - } else { - false - } - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(blaze) = self.blaze.upgrade() else { + return false; + }; + let target = blaze.entity.get_target(); + if target.is_some() { + // TODO: check is_alive + true + } else { + false + } } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(blaze) = self.blaze.upgrade() else { - return false; - }; - let target = blaze.entity.target.lock().await.clone(); - if target.is_some() { - // TODO: check is_alive - true - } else { - false - } - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(blaze) = self.blaze.upgrade() else { + return false; + }; + let target = blaze.entity.get_target(); + if target.is_some() { + // TODO: check is_alive + true + } else { + false + } } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.attack_step = 0; - }) + fn start(&mut self, _mob: &dyn Mob) { + self.attack_step = 0; } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(blaze) = self.blaze.upgrade() { - blaze.set_charged(false); - } - self.last_seen = 0; - }) + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(blaze) = self.blaze.upgrade() { + blaze.set_charged(false); + } + self.last_seen = 0; } fn should_run_every_tick(&self) -> bool { true } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.attack_time -= 1; + fn tick(&mut self, _mob: &dyn Mob) { + self.attack_time -= 1; - let Some(blaze) = self.blaze.upgrade() else { + let Some(blaze) = self.blaze.upgrade() else { + return; + }; + + let target = blaze.entity.get_target(); + let Some(target) = target else { + return; + }; + + // TODO: hasLineOfSight check + let has_line_of_sight = true; + + if has_line_of_sight { + self.last_seen = 0; + } else { + self.last_seen += 1; + } + + let blaze_pos = blaze.entity.living_entity.entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + + let dx = target_pos.x - blaze_pos.x; + let dy = target_pos.y - blaze_pos.y; + let dz = target_pos.z - blaze_pos.z; + + let distance_sq = dx * dx + dy * dy + dz * dz; + + if distance_sq < 4.0 { + if !has_line_of_sight { return; - }; - - let target = blaze.entity.target.lock().await.clone(); - let Some(target) = target else { - return; - }; - - // TODO: hasLineOfSight check - let has_line_of_sight = true; - - if has_line_of_sight { - self.last_seen = 0; - } else { - self.last_seen += 1; } - let blaze_pos = blaze.entity.living_entity.entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - - let dx = target_pos.x - blaze_pos.x; - let dy = target_pos.y - blaze_pos.y; - let dz = target_pos.z - blaze_pos.z; - - let distance_sq = dx * dx + dy * dy + dz * dz; - - if distance_sq < 4.0 { - if !has_line_of_sight { - return; - } - - if self.attack_time <= 0 { - self.attack_time = 20; - // TODO: doHurtTarget - } - - // TODO: set wanted position to target - } else if distance_sq < Self::get_follow_distance().powi(2) && has_line_of_sight { - let target_y_offset = target_pos.y + 0.5; // roughly target.getY(0.5) - let blaze_y_offset = blaze_pos.y + 0.5; // roughly blaze.getY(0.5) - let yd = target_y_offset - blaze_y_offset; - - if self.attack_time <= 0 { - self.attack_step += 1; - if self.attack_step == 1 { - self.attack_time = 60; - blaze.set_charged(true); - } else if self.attack_step <= 4 { - self.attack_time = 6; - } else { - self.attack_time = 100; - self.attack_step = 0; - blaze.set_charged(false); - } - - if self.attack_step > 1 { - let distance = distance_sq.sqrt(); - let sqd = distance.sqrt() * 0.5; - // play shoot sound - let chunk_pos = blaze.entity.living_entity.entity.chunk_pos.load(); - blaze - .entity - .living_entity - .entity - .world - .load() - .broadcast_to_chunk( - chunk_pos, - &CWorldEvent::new( - 1018, - blaze.entity.living_entity.entity.block_pos.load(), - 0, - false, - ), - ); - - for _ in 0..1 { - // Vanilla loops 1 time - // Calculate spread - let direction = { - let mut rng = rand::rng(); - let dir_x = (dx - 2.297 * sqd) - + rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0); - let dir_z = (dz - 2.297 * sqd) - + rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0); - Vector3::new(dir_x, yd, dir_z).normalize() - }; - - // Spawn SmallFireball - let world = blaze.entity.living_entity.entity.world.load(); - let uuid = uuid::Uuid::new_v4(); - - let mut pos = blaze.entity.living_entity.entity.pos.load(); - pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1; - - let base_entity = Entity::from_uuid( - uuid, - world.clone(), - pos, - &pumpkin_data::entity::EntityType::SMALL_FIREBALL, - ); - - let fireball = SmallFireballEntity::new_shot( - base_entity, - &blaze.entity.living_entity.entity, - ); - fireball.thrown.entity.velocity.store(direction); - - world.spawn_entity(Arc::new(fireball)).await; - } - } - } - - // Look at target - blaze - .entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity(&*blaze, &target); - } else if self.last_seen < 5 { - // TODO: set wanted position to target + if self.attack_time <= 0 { + self.attack_time = 20; + // TODO: doHurtTarget } - }) + + // TODO: set wanted position to target + } else if distance_sq < Self::get_follow_distance().powi(2) && has_line_of_sight { + let target_y_offset = target_pos.y + 0.5; // roughly target.getY(0.5) + let blaze_y_offset = blaze_pos.y + 0.5; // roughly blaze.getY(0.5) + let yd = target_y_offset - blaze_y_offset; + + if self.attack_time <= 0 { + self.attack_step += 1; + if self.attack_step == 1 { + self.attack_time = 60; + blaze.set_charged(true); + } else if self.attack_step <= 4 { + self.attack_time = 6; + } else { + self.attack_time = 100; + self.attack_step = 0; + blaze.set_charged(false); + } + + if self.attack_step > 1 { + let distance = distance_sq.sqrt(); + let sqd = distance.sqrt() * 0.5; + // play shoot sound + let chunk_pos = blaze.entity.living_entity.entity.chunk_pos.load(); + blaze + .entity + .living_entity + .entity + .world + .load() + .broadcast_to_chunk( + chunk_pos, + &CWorldEvent::new( + 1018, + blaze.entity.living_entity.entity.block_pos.load(), + 0, + false, + ), + ); + + for _ in 0..1 { + // Vanilla loops 1 time + // Calculate spread + let _direction = { + let mut rng = rand::rng(); + let dir_x = (dx - 2.297 * sqd) + + rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0); + let dir_z = (dz - 2.297 * sqd) + + rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0); + Vector3::new(dir_x, yd, dir_z).normalize() + }; + + // Spawn SmallFireball + let world = blaze.entity.living_entity.entity.world.load_full(); + let uuid = uuid::Uuid::new_v4(); + + let mut pos = blaze.entity.living_entity.entity.pos.load(); + pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1; + + let base_entity = Entity::from_uuid( + uuid, + world.clone(), + pos, + &pumpkin_data::entity::EntityType::SMALL_FIREBALL, + ); + + let fireball = SmallFireballEntity::new_shot( + base_entity, + &blaze.entity.living_entity.entity, + ); + world.spawn_entity(Arc::new(fireball)); + } + } + } + + // Look at target + blaze + .entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity(&*blaze, &target); + } else if self.last_seen < 5 { + // TODO: set wanted position to target + } } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/bow_attack.rs b/crates/pumpkin/src/entity/ai/goal/bow_attack.rs index 13a3e2e7b..97f8f14f0 100644 --- a/crates/pumpkin/src/entity/ai/goal/bow_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/bow_attack.rs @@ -6,7 +6,7 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::Hand; use std::sync::Arc; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::projectile::arrow::{ArrowEntity, ArrowPickup}; @@ -44,44 +44,34 @@ impl BowAttackGoal { } } - async fn main_hand_item(mob: &dyn Mob) -> ItemStack { + fn main_hand_item(mob: &dyn Mob) -> ItemStack { mob.get_mob_entity() .living_entity .entity_equipment - .lock() - .await - .get(&EquipmentSlot::MAIN_HAND) + .try_lock() + .map_or_else( + |_| ItemStack::EMPTY.clone(), + |eq| eq.get(&EquipmentSlot::MAIN_HAND), + ) } - async fn is_holding_bow(mob: &dyn Mob) -> bool { - Self::main_hand_item(mob).await.item.id == Item::BOW.id + fn is_holding_bow(mob: &dyn Mob) -> bool { + Self::main_hand_item(mob).item.id == Item::BOW.id } - async fn stop_drawing(&mut self, mob: &dyn Mob) { + fn stop_drawing(&mut self, mob: &dyn Mob) { if self.drawing { - mob.get_mob_entity().living_entity.clear_active_hand().await; + mob.get_mob_entity().living_entity.clear_active_hand(); self.drawing = false; self.draw_ticks = 0; } } /// Spawns the arrow, matching vanilla `AbstractSkeleton::performRangedAttack`. - async fn shoot(mob: &dyn Mob, target: &Arc) { + fn shoot(mob: &dyn Mob, target: &Arc) { let entity = mob.get_entity(); let world = entity.world.load(); - - let mut event = - crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent::new( - entity.entity_id, - "minecraft:bow".to_string(), - 1.0, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return; - } + let world_full = entity.world.load_full(); let arrow_entity = Entity::new(world.clone(), entity.pos.load(), &EntityType::ARROW); let projectile = ItemStack::new(1, &Item::ARROW); @@ -112,110 +102,112 @@ impl BowAttackGoal { world.play_sound(Sound::EntityArrowShoot, SoundCategory::Hostile, &mob_pos); let arrow: Arc = Arc::new(arrow); - world.spawn_entity(arrow).await; + let entity_id = entity.entity_id; + if let Some(server) = world_full.server.upgrade() { + let mut event = + crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent::new( + entity_id, + "minecraft:bow".to_string(), + 1.0, + ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + return; + } + } + world_full.spawn_entity(arrow); } } impl Goal for BowAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } - Self::is_holding_bow(mob).await - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } + Self::is_holding_bow(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return false; - }; - target.get_entity().is_alive() && Self::is_holding_bow(mob).await - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return false; + }; + target.get_entity().is_alive() && Self::is_holding_bow(mob) } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.cooldown = -1; - self.draw_ticks = 0; - self.drawing = false; - }) + fn start(&mut self, _mob: &dyn Mob) { + self.cooldown = -1; + self.draw_ticks = 0; + self.drawing = false; } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.stop_drawing(mob).await; - self.cooldown = -1; - mob.get_mob_entity() + fn stop(&mut self, mob: &dyn Mob) { + self.stop_drawing(mob); + self.cooldown = -1; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); + } + + fn tick(&mut self, mob: &dyn Mob) { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return; + }; + + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let distance_sq = mob_pos.squared_distance_to_vec(&target_pos); + + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity_with_range(&target, 30.0, 30.0); + + // Close the gap while out of shooting range, otherwise hold position. + { + let mut navigator = mob + .get_mob_entity() .navigator .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - }) - } + .unwrap_or_else(std::sync::PoisonError::into_inner); + if distance_sq > self.squared_range { + navigator.set_progress(NavigatorGoal { + current_progress: mob_pos, + destination: target_pos, + speed: self.speed, + }); + } else { + navigator.stop(); + } + } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return; - }; - - let mob_pos = mob.get_entity().pos.load(); - let target_pos = target.get_entity().pos.load(); - let distance_sq = mob_pos.squared_distance_to_vec(&target_pos); + if self.drawing { + self.draw_ticks += 1; + if self.draw_ticks >= Self::DRAW_TIME { + self.stop_drawing(mob); + Self::shoot(mob, &target); + self.cooldown = self.attack_interval; + } + return; + } + self.cooldown -= 1; + if self.cooldown <= 0 && distance_sq <= self.squared_range { + let stack = Self::main_hand_item(mob); mob.get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity_with_range(&target, 30.0, 30.0); - - // Close the gap while out of shooting range, otherwise hold position. - { - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if distance_sq > self.squared_range { - navigator.set_progress(NavigatorGoal { - current_progress: mob_pos, - destination: target_pos, - speed: self.speed, - }); - } else { - navigator.stop(); - } - } - - if self.drawing { - self.draw_ticks += 1; - if self.draw_ticks >= Self::DRAW_TIME { - self.stop_drawing(mob).await; - Self::shoot(mob, &target).await; - self.cooldown = self.attack_interval; - } - return; - } - - self.cooldown -= 1; - if self.cooldown <= 0 && distance_sq <= self.squared_range { - let stack = Self::main_hand_item(mob).await; - mob.get_mob_entity() - .living_entity - .set_active_hand(Hand::Right, stack, i32::MAX) - .await; - self.drawing = true; - self.draw_ticks = 0; - } - }) + .living_entity + .set_active_hand(Hand::Right, stack, i32::MAX); + self.drawing = true; + self.draw_ticks = 0; + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/break_door.rs b/crates/pumpkin/src/entity/ai/goal/break_door.rs index d8f374621..994efdfe1 100644 --- a/crates/pumpkin/src/entity/ai/goal/break_door.rs +++ b/crates/pumpkin/src/entity/ai/goal/break_door.rs @@ -7,7 +7,7 @@ use pumpkin_world::world::BlockFlags; use rand::RngExt; use super::door_interact::DoorInteractGoal; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::block::blocks::doors::DoorBlock; use crate::entity::mob::Mob; @@ -71,112 +71,103 @@ impl Default for BreakDoorGoal { } impl Goal for BreakDoorGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if !self.door_interact_goal.can_use(mob) { - return false; - } - let world = mob.get_entity().world.load(); - let level_info = world.level_info.load(); - if !level_info.game_rules.mob_griefing { - return false; - } - self.is_valid_difficulty(level_info.difficulty) && !self.door_interact_goal.is_open(mob) - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if !self.door_interact_goal.can_use(mob) { + return false; + } + let world = mob.get_entity().world.load(); + let level_info = world.level_info.load(); + if !level_info.game_rules.mob_griefing { + return false; + } + self.is_valid_difficulty(level_info.difficulty) && !self.door_interact_goal.is_open(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let world = mob.get_entity().world.load(); - let level_info = world.level_info.load(); - let mob_pos = mob.get_entity().pos.load(); - let door_pos = self.door_interact_goal.door_pos; - let center_x = f64::from(door_pos.0.x) + 0.5; - let center_y = f64::from(door_pos.0.y) + 0.5; - let center_z = f64::from(door_pos.0.z) + 0.5; - let dx = center_x - mob_pos.x; - let dy = center_y - mob_pos.y; - let dz = center_z - mob_pos.z; - let dist_sq = dx * dx + dy * dy + dz * dz; + fn should_continue(&self, mob: &dyn Mob) -> bool { + let world = mob.get_entity().world.load(); + let level_info = world.level_info.load(); + let mob_pos = mob.get_entity().pos.load(); + let door_pos = self.door_interact_goal.door_pos; + let center_x = f64::from(door_pos.0.x) + 0.5; + let center_y = f64::from(door_pos.0.y) + 0.5; + let center_z = f64::from(door_pos.0.z) + 0.5; + let dx = center_x - mob_pos.x; + let dy = center_y - mob_pos.y; + let dz = center_z - mob_pos.z; + let dist_sq = dx * dx + dy * dy + dz * dz; - self.break_time <= self.get_door_break_time() - && !DoorBlock::is_open(&world, &door_pos) - && dist_sq < 4.0 - && self.is_valid_difficulty(level_info.difficulty) - }) + self.break_time <= self.get_door_break_time() + && !DoorBlock::is_open(&world, &door_pos) + && dist_sq < 4.0 + && self.is_valid_difficulty(level_info.difficulty) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.door_interact_goal.start_interaction(mob); - self.break_time = 0; - self.last_break_progress = -1; - }) + fn start(&mut self, mob: &dyn Mob) { + self.door_interact_goal.start_interaction(mob); + self.break_time = 0; + self.last_break_progress = -1; } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let world = mob.get_entity().world.load(); + fn stop(&mut self, mob: &dyn Mob) { + let world = mob.get_entity().world.load(); + world.set_block_destroy_stage( + mob.get_entity().entity_id, + self.door_interact_goal.door_pos, + -1, + ); + } + + fn tick(&mut self, mob: &dyn Mob) { + self.door_interact_goal.tick_interaction(mob); + let world = mob.get_entity().world.load_full(); + + if mob.get_random().random_range(0..20) == 0 { + world.sync_world_event( + WorldEvent::SoundZombieWoodenDoor, + self.door_interact_goal.door_pos, + 0, + ); + mob.get_mob_entity().living_entity.swing_hand(); + } + + self.break_time += 1; + let progress = (self.break_time as f32 / self.get_door_break_time() as f32 * 10.0) as i32; + if progress != self.last_break_progress { world.set_block_destroy_stage( mob.get_entity().entity_id, self.door_interact_goal.door_pos, - -1, + progress as i8, ); - }) - } + self.last_break_progress = progress; + } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.door_interact_goal.tick_interaction(mob); - let world = mob.get_entity().world.load_full(); - - if mob.get_random().random_range(0..20) == 0 { - world.sync_world_event( - WorldEvent::SoundZombieWoodenDoor, - self.door_interact_goal.door_pos, - 0, - ); - mob.get_mob_entity().living_entity.swing_hand().await; - } - - self.break_time += 1; - let progress = - (self.break_time as f32 / self.get_door_break_time() as f32 * 10.0) as i32; - if progress != self.last_break_progress { - world.set_block_destroy_stage( + let level_info = world.level_info.load(); + if self.break_time == self.get_door_break_time() + && self.is_valid_difficulty(level_info.difficulty) + { + let (_, block_state_id) = + world.get_block_and_state_id(&self.door_interact_goal.door_pos); + let door_pos = self.door_interact_goal.door_pos; + let mut event = + crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new( mob.get_entity().entity_id, - self.door_interact_goal.door_pos, - progress as i8, + door_pos, ); - self.last_break_progress = progress; + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); } - - let level_info = world.level_info.load(); - if self.break_time == self.get_door_break_time() - && self.is_valid_difficulty(level_info.difficulty) - { - let (_, block_state_id) = - world.get_block_and_state_id(&self.door_interact_goal.door_pos); - mob.break_door(self.door_interact_goal.door_pos).await; - world - .set_block_state( - &self.door_interact_goal.door_pos, - BlockStateId::AIR, - BlockFlags::NOTIFY_ALL, - ) - .await; - world.sync_world_event( - WorldEvent::SoundZombieDoorCrash, - self.door_interact_goal.door_pos, - 0, - ); - world.sync_world_event( - WorldEvent::ParticlesDestroyBlock, - self.door_interact_goal.door_pos, - i32::from(block_state_id.as_u16()), - ); - } - }) + world.set_block_state(&door_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); + world.sync_world_event( + WorldEvent::SoundZombieDoorCrash, + self.door_interact_goal.door_pos, + 0, + ); + world.sync_world_event( + WorldEvent::ParticlesDestroyBlock, + self.door_interact_goal.door_pos, + i32::from(block_state_id.as_u16()), + ); + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/breed.rs b/crates/pumpkin/src/entity/ai/goal/breed.rs index 0334d0287..205abab7b 100644 --- a/crates/pumpkin/src/entity/ai/goal/breed.rs +++ b/crates/pumpkin/src/entity/ai/goal/breed.rs @@ -4,7 +4,7 @@ use uuid::Uuid; use crate::entity::{EntityBase, ai::pathfinder::NavigatorGoal, mob::Mob, r#type::from_type}; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; pub struct BreedGoal { speed: f64, @@ -60,31 +60,31 @@ impl BreedGoal { closest.map(|(_, e)| e) } - async fn breed(mob: &dyn Mob, mate: &dyn EntityBase) { + fn breed(mob: &dyn Mob, mate: &dyn EntityBase) { let mob_entity = mob.get_mob_entity(); let entity = mob.get_entity(); let world = entity.world.load(); - if let Some(player) = mob_entity + let player_opt = mob_entity .breeder .load() - .and_then(|uuid| world.get_player_by_uuid(uuid)) - { - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::AnimalsBred as i32, - 1, - ) - .await; + .and_then(|uuid| world.get_player_by_uuid(uuid)); + if let Some(player) = player_opt { + let entity_type_name = entity.entity_type.resource_name; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::AnimalsBred as i32, + 1, + ); - player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::BredAnimal { - parent_type: format!("minecraft:{}", entity.entity_type.resource_name), - }, - ) - .await; + player.trigger_advancement_criterion( + pumpkin_data::advancement::Advancement::HUSBANDRY_BREED_AN_ANIMAL, + "bred", + ); + player.trigger_advancement_criterion( + pumpkin_data::advancement::Advancement::HUSBANDRY_BRED_ALL_ANIMALS, + &format!("minecraft:{entity_type_name}"), + ); } mob_entity.reset_love_ticks(); @@ -98,90 +98,81 @@ impl BreedGoal { let parent_pos = entity.pos.load(); let baby = from_type(entity.entity_type, parent_pos, &world, Uuid::new_v4()); baby.get_entity().set_age(-24000); - world.spawn_entity(baby).await; + let world_full = entity.world.load_full(); + world_full.spawn_entity(baby); } } impl Goal for BreedGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - if !mob_entity.is_breeding_ready() || !mob_entity.is_in_love() { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + if !mob_entity.is_breeding_ready() || !mob_entity.is_in_love() { + return false; + } - self.mate = Self::find_mate(mob); - self.mate.is_some() - }) + self.mate = Self::find_mate(mob); + self.mate.is_some() } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let Some(mate) = &self.mate else { - return false; - }; + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(mate) = &self.mate else { + return false; + }; - if !mate.get_entity().is_alive() || mate.is_panicking() { - return false; - } + if !mate.get_entity().is_alive() || mate.is_panicking() { + return false; + } - mate.is_in_love() && self.timer < 60 - }) + mate.is_in_love() && self.timer < 60 } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.timer = 0; - }) + fn start(&mut self, _mob: &dyn Mob) { + self.timer = 0; } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.mate = None; - self.timer = 0; - let mut navigator = mob - .get_mob_entity() + fn stop(&mut self, mob: &dyn Mob) { + self.mate = None; + self.timer = 0; + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.stop(); + } + + fn tick(&mut self, mob: &dyn Mob) { + let Some(mate) = &self.mate else { + return; + }; + + let mob_entity = mob.get_mob_entity(); + let mate_pos = mate.get_entity().pos.load(); + + { + let mut look_control = mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + look_control.look_at_entity(mob, mate); + }; + + let my_pos = mob.get_entity().pos.load(); + let dist_sq = my_pos.squared_distance_to_vec(&mate_pos); + + { + let mut navigator = mob_entity .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.stop(); - }) - } + navigator.set_progress(NavigatorGoal::new(my_pos, mate_pos, self.speed)); + }; - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let Some(mate) = &self.mate else { - return; - }; + self.timer += 1; - let mob_entity = mob.get_mob_entity(); - let mate_pos = mate.get_entity().pos.load(); - - { - let mut look_control = mob_entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.look_at_entity(mob, mate); - }; - - let my_pos = mob.get_entity().pos.load(); - let dist_sq = my_pos.squared_distance_to_vec(&mate_pos); - - { - let mut navigator = mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(my_pos, mate_pos, self.speed)); - }; - - self.timer += 1; - - if self.timer >= 60 && dist_sq < 9.0 { - Self::breed(mob, mate.as_ref()).await; - } - }) + if self.timer >= 60 && dist_sq < 9.0 { + Self::breed(mob, mate.as_ref()); + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/chase_player.rs b/crates/pumpkin/src/entity/ai/goal/chase_player.rs index 13c51b429..c483d4eca 100644 --- a/crates/pumpkin/src/entity/ai/goal/chase_player.rs +++ b/crates/pumpkin/src/entity/ai/goal/chase_player.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::EntityBase; use crate::entity::mob::Mob; use crate::entity::mob::enderman::{EndermanEntity, PLAYER_EYE_HEIGHT}; @@ -21,90 +21,82 @@ impl ChasePlayerGoal { } impl Goal for ChasePlayerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - let target = mob_entity.target.lock().await.clone(); - - let Some(target) = target else { - self.target = None; - return false; - }; - - let Some(player) = target.get_player() else { - self.target = None; - return false; - }; - - let entity = &mob_entity.living_entity.entity; - let mob_pos = entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - if mob_pos.squared_distance_to_vec(&target_pos) > 256.0 { - self.target = None; - return false; - } - - if !self.enderman.is_player_staring(player).await { - self.target = None; - return false; - } - - let world = entity.world.load(); - let closest = world.get_closest_player(mob_pos, 256.0); - if let Some(p) = closest - && p.get_entity().entity_id == target.get_entity().entity_id - { - self.target = Some(p); - return true; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + let target = mob_entity.get_target(); + let Some(target) = target else { self.target = None; - false - }) + return false; + }; + + let Some(player) = target.get_player() else { + self.target = None; + return false; + }; + + let entity = &mob_entity.living_entity.entity; + let mob_pos = entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + if mob_pos.squared_distance_to_vec(&target_pos) > 256.0 { + self.target = None; + return false; + } + + if !self.enderman.is_player_staring(player) { + self.target = None; + return false; + } + + let world = entity.world.load(); + let closest = world.get_closest_player(mob_pos, 256.0); + if let Some(p) = closest + && p.get_entity().entity_id == target.get_entity().entity_id + { + self.target = Some(p); + return true; + } + + self.target = None; + false } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(player) = &self.target else { - return false; - }; + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(player) = &self.target else { + return false; + }; - let mob_entity = mob.get_mob_entity(); - let entity = &mob_entity.living_entity.entity; - let mob_pos = entity.pos.load(); - let target_pos = player.get_entity().pos.load(); - if mob_pos.squared_distance_to_vec(&target_pos) > 256.0 { - return false; - } + let mob_entity = mob.get_mob_entity(); + let entity = &mob_entity.living_entity.entity; + let mob_pos = entity.pos.load(); + let target_pos = player.get_entity().pos.load(); + if mob_pos.squared_distance_to_vec(&target_pos) > 256.0 { + return false; + } - self.enderman.is_player_staring(player).await - }) + self.enderman.is_player_staring(player) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let mut navigator = mob + fn start(&mut self, mob: &dyn Mob) { + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.stop(); + } + + fn tick(&mut self, mob: &dyn Mob) { + if let Some(player) = &self.target { + let player_pos = player.get_entity().pos.load(); + let eye_y = player_pos.y + PLAYER_EYE_HEIGHT; + let mut look_control = mob .get_mob_entity() - .navigator + .look_control .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.stop(); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(player) = &self.target { - let player_pos = player.get_entity().pos.load(); - let eye_y = player_pos.y + PLAYER_EYE_HEIGHT; - let mut look_control = mob - .get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.look_at(mob, player_pos.x, eye_y, player_pos.z); - } - }) + look_control.look_at(mob, player_pos.x, eye_y, player_pos.z); + } } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/creeper_ignite.rs b/crates/pumpkin/src/entity/ai/goal/creeper_ignite.rs index 61b79e9ca..fa0f65fd5 100644 --- a/crates/pumpkin/src/entity/ai/goal/creeper_ignite.rs +++ b/crates/pumpkin/src/entity/ai/goal/creeper_ignite.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use std::sync::atomic::Ordering; use super::{Controls, Goal}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::mob::Mob; use crate::entity::mob::creeper::CreeperEntity; @@ -22,68 +22,63 @@ impl CreeperIgniteGoal { } impl Goal for CreeperIgniteGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let creeper = mob.get_mob_entity(); - let target_lock = creeper.target.lock().await; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let creeper = mob.get_mob_entity(); + let target_lock = creeper + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - if self.creeper.fuse_speed.load(Ordering::Relaxed) > 0 { - return true; - } - - if let Some(target) = target_lock.as_ref() { - let dist_sq = mob - .get_entity() - .pos - .load() - .squared_distance_to_vec(&target.get_entity().pos.load()); - return dist_sq < 9.0; - } - - false - }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.stop(); - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.creeper.set_fuse_speed(-1); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let target_lock = mob.get_mob_entity().target.lock().await; - - let Some(target) = target_lock.as_ref() else { - self.creeper.set_fuse_speed(-1); - return; - }; + if self.creeper.fuse_speed.load(Ordering::Relaxed) > 0 { + return true; + } + if let Some(target) = target_lock.as_ref() { let dist_sq = mob .get_entity() .pos .load() .squared_distance_to_vec(&target.get_entity().pos.load()); + return dist_sq < 9.0; + } - if dist_sq > 49.0 { - self.creeper.set_fuse_speed(-1); - } - // TODO: line of sight check (needs world raycast) - else { - self.creeper.set_fuse_speed(1); - } - }) + false + } + + fn start(&mut self, mob: &dyn Mob) { + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.stop(); + } + + fn stop(&mut self, _mob: &dyn Mob) { + self.creeper.set_fuse_speed(-1); + } + + fn tick(&mut self, mob: &dyn Mob) { + let target_lock = mob.get_mob_entity().get_target(); + + let Some(target) = target_lock.as_ref() else { + self.creeper.set_fuse_speed(-1); + return; + }; + + let dist_sq = mob + .get_entity() + .pos + .load() + .squared_distance_to_vec(&target.get_entity().pos.load()); + + if dist_sq > 49.0 { + self.creeper.set_fuse_speed(-1); + } + // TODO: line of sight check (needs world raycast) + else { + self.creeper.set_fuse_speed(1); + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/destroy_egg.rs b/crates/pumpkin/src/entity/ai/goal/destroy_egg.rs index 557f08be2..f09317056 100644 --- a/crates/pumpkin/src/entity/ai/goal/destroy_egg.rs +++ b/crates/pumpkin/src/entity/ai/goal/destroy_egg.rs @@ -1,15 +1,12 @@ use crate::entity::ai::goal::move_to_target_pos::MoveToTargetPos; -use crate::entity::ai::goal::step_and_destroy_block::{ - StepAndDestroyBlockGoal, Stepping, SteppingFuture, -}; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture, ParentHandle}; +use crate::entity::ai::goal::step_and_destroy_block::{StepAndDestroyBlockGoal, Stepping}; +use crate::entity::ai::goal::{Controls, Goal, ParentHandle}; use crate::entity::mob::Mob; use crate::world::World; use pumpkin_data::Block; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::position::BlockPos; use rand::{RngExt, rng}; -use std::pin::Pin; use std::sync::Arc; pub struct DestroyEggGoal { @@ -40,30 +37,24 @@ impl DestroyEggGoal { } impl Goal for DestroyEggGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.step_and_destroy_block_goal.can_start(mob).await }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.step_and_destroy_block_goal.can_start(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.step_and_destroy_block_goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.step_and_destroy_block_goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.step_and_destroy_block_goal.start(mob).await; - }) + fn start(&mut self, mob: &dyn Mob) { + self.step_and_destroy_block_goal.start(mob); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.step_and_destroy_block_goal.stop(mob).await; - }) + fn stop(&mut self, mob: &dyn Mob) { + self.step_and_destroy_block_goal.stop(mob); } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.step_and_destroy_block_goal.tick(mob).await; - }) + fn tick(&mut self, mob: &dyn Mob) { + self.step_and_destroy_block_goal.tick(mob); } fn should_run_every_tick(&self) -> bool { @@ -76,52 +67,35 @@ impl Goal for DestroyEggGoal { } impl Stepping for DestroyEggGoal { - fn tick_stepping(&self, world: Arc, block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async move { - let random = rng().random::(); - - // NOTE: block_pos.0.to_f64() is assumed to be the correct way to get Vector3 - let pos_f64 = (block_pos.0).to_f64(); - - world.play_sound_raw( - Sound::EntityZombieDestroyEgg as u16, - SoundCategory::Hostile, - &pos_f64, - 0.7, - random.mul_add(0.2, 0.9), - ); - }) + fn tick_stepping(&self, world: Arc, block_pos: BlockPos) { + let random = rng().random::(); + let pos_f64 = (block_pos.0).to_f64(); + world.play_sound_raw( + Sound::EntityZombieDestroyEgg as u16, + SoundCategory::Hostile, + &pos_f64, + 0.7, + random.mul_add(0.2, 0.9), + ); } - fn on_destroy_block(&self, world: Arc, block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async move { - let random = rng().random::(); - - // NOTE: block_pos.0.to_f64() is assumed to be the correct way to get Vector3 - let pos_f64 = (block_pos.0).to_f64(); - - world.play_sound_raw( - Sound::EntityTurtleEggBreak as u16, - SoundCategory::Blocks, - &pos_f64, - 0.7, - random.mul_add(0.2, 0.9), - ); - }) + fn on_destroy_block(&self, world: Arc, block_pos: BlockPos) { + let random = rng().random::(); + let pos_f64 = (block_pos.0).to_f64(); + world.play_sound_raw( + Sound::EntityTurtleEggBreak as u16, + SoundCategory::Blocks, + &pos_f64, + 0.7, + random.mul_add(0.2, 0.9), + ); } } impl MoveToTargetPos for DestroyEggGoal { - fn is_target_pos<'a>( - &'a self, - world: Arc, - block_pos: BlockPos, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - self.step_and_destroy_block_goal - .is_target_pos(world, block_pos) - .await - }) + fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool { + self.step_and_destroy_block_goal + .is_target_pos(world, block_pos) } fn get_desired_distance_to_target(&self) -> f64 { diff --git a/crates/pumpkin/src/entity/ai/goal/door_interact.rs b/crates/pumpkin/src/entity/ai/goal/door_interact.rs index bf4dd95a9..600ad27de 100644 --- a/crates/pumpkin/src/entity/ai/goal/door_interact.rs +++ b/crates/pumpkin/src/entity/ai/goal/door_interact.rs @@ -2,7 +2,7 @@ use pumpkin_data::tag::{self, Taggable}; use pumpkin_util::math::position::BlockPos; use std::sync::atomic::Ordering; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::block::blocks::doors::DoorBlock; use crate::entity::mob::Mob; @@ -45,12 +45,12 @@ impl DoorInteractGoal { DoorBlock::is_open(&world, &self.door_pos) } - pub async fn set_open(&mut self, mob: &dyn Mob, open: bool) { + pub fn set_open(&mut self, mob: &dyn Mob, open: bool) { if self.has_door { let world = mob.get_entity().world.load_full(); let (block, _) = world.get_block_and_state_id(&self.door_pos); if block.has_tag(&tag::Block::MINECRAFT_DOORS) { - DoorBlock::set_open(&world, &self.door_pos, open).await; + DoorBlock::set_open(&world, &self.door_pos, open); } } } @@ -130,24 +130,20 @@ impl DoorInteractGoal { } impl Goal for DoorInteractGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.can_use(mob) }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.can_use(mob) } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.can_continue_to_use() }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.can_continue_to_use() } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.start_interaction(mob); - }) + fn start(&mut self, mob: &dyn Mob) { + self.start_interaction(mob); } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.tick_interaction(mob); - }) + fn tick(&mut self, mob: &dyn Mob) { + self.tick_interaction(mob); } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/eat_grass.rs b/crates/pumpkin/src/entity/ai/goal/eat_grass.rs index da0cbf659..10f619c25 100644 --- a/crates/pumpkin/src/entity/ai/goal/eat_grass.rs +++ b/crates/pumpkin/src/entity/ai/goal/eat_grass.rs @@ -1,4 +1,4 @@ -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::mob::Mob; use pumpkin_data::Block; use pumpkin_data::tag::{self, Taggable}; @@ -29,83 +29,71 @@ impl EatGrassGoal { } impl Goal for EatGrassGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if mob.get_random().random_range(0..1000) != 0 { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if mob.get_random().random_range(0..1000) != 0 { + return false; + } + let entity = &mob.get_mob_entity().living_entity.entity; + let block_pos = entity.block_pos.load(); + let world = entity.world.load(); + + let block_at_pos = world.get_block(&block_pos); + if block_at_pos.has_tag(&tag::Block::MINECRAFT_EDIBLE_FOR_SHEEP) { + return true; + } + + let block_below = world.get_block(&block_pos.down()); + block_below.id == Block::GRASS_BLOCK.id + } + + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.timer > 0 + } + + fn start(&mut self, mob: &dyn Mob) { + self.timer = MAX_TIMER; + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.stop(); + } + + fn tick(&mut self, mob: &dyn Mob) { + self.timer -= 1; + + if self.timer == 4 { let entity = &mob.get_mob_entity().living_entity.entity; let block_pos = entity.block_pos.load(); - let world = entity.world.load(); + let world = entity.world.load_full(); let block_at_pos = world.get_block(&block_pos); if block_at_pos.has_tag(&tag::Block::MINECRAFT_EDIBLE_FOR_SHEEP) { - return true; - } - - let block_below = world.get_block(&block_pos.down()); - block_below.id == Block::GRASS_BLOCK.id - }) - } - - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.timer > 0 }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.timer = MAX_TIMER; - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.stop(); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.timer -= 1; - - if self.timer == 4 { - let entity = &mob.get_mob_entity().living_entity.entity; - let block_pos = entity.block_pos.load(); - let world = entity.world.load_full(); - - let block_at_pos = world.get_block(&block_pos); - if block_at_pos.has_tag(&tag::Block::MINECRAFT_EDIBLE_FOR_SHEEP) { - world - .set_block_state( - &block_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - mob.on_eating_grass().await; - } else { - let below_pos = block_pos.down(); - let block_below = world.get_block(&below_pos); - if block_below.id == Block::GRASS_BLOCK.id { - world - .set_block_state( - &below_pos, - Block::DIRT.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - mob.on_eating_grass().await; - } + world.set_block_state( + &block_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + mob.on_eating_grass(); + } else { + let below_pos = block_pos.down(); + let block_below = world.get_block(&below_pos); + if block_below.id == Block::GRASS_BLOCK.id { + world.set_block_state( + &below_pos, + Block::DIRT.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + mob.on_eating_grass(); } } - }) + } } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.timer = 0; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.timer = 0; } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/escape_danger.rs b/crates/pumpkin/src/entity/ai/goal/escape_danger.rs index 2c853e581..eba30ab4c 100644 --- a/crates/pumpkin/src/entity/ai/goal/escape_danger.rs +++ b/crates/pumpkin/src/entity/ai/goal/escape_danger.rs @@ -1,6 +1,6 @@ use std::sync::atomic::Ordering::Relaxed; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::{ai::pathfinder::NavigatorGoal, mob::Mob}; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; @@ -58,45 +58,37 @@ impl EscapeDangerGoal { } impl Goal for EscapeDangerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if !Self::is_in_danger(mob) { - return false; - } - self.target = Self::find_escape_target(mob); - self.target.is_some() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if !Self::is_in_danger(mob) { + return false; + } + self.target = Self::find_escape_target(mob); + self.target.is_some() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let navigator = mob + fn should_continue(&self, mob: &dyn Mob) -> bool { + let navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + !navigator.is_idle() + } + + fn start(&mut self, mob: &dyn Mob) { + if let Some(target) = self.target { + let pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let mut navigator = mob .get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - !navigator.is_idle() - }) + navigator.set_progress(NavigatorGoal::new(pos, target, self.speed)); + } } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target) = self.target { - let pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(pos, target, self.speed)); - } - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target = None; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.target = None; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/follow_owner.rs b/crates/pumpkin/src/entity/ai/goal/follow_owner.rs index 261a4e839..3928a4002 100644 --- a/crates/pumpkin/src/entity/ai/goal/follow_owner.rs +++ b/crates/pumpkin/src/entity/ai/goal/follow_owner.rs @@ -1,5 +1,4 @@ -use super::{Controls, Goal, GoalFuture, to_goal_ticks}; -use crate::entity::EntityBase; +use super::{Controls, Goal, to_goal_ticks}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::ai::pathfinder::node::PathType; use crate::entity::mob::Mob; @@ -33,12 +32,12 @@ impl FollowOwnerGoal { }) } - async fn unable_to_move_to_owner(mob: &dyn Mob, owner: Option<&Player>) -> bool { + fn unable_to_move_to_owner(mob: &dyn Mob, owner: Option<&Player>) -> bool { if mob.is_sitting() { return true; } let mob_entity = &mob.get_mob_entity().living_entity.entity; - if mob_entity.has_vehicle().await || mob_entity.is_leashed().await { + if mob_entity.has_vehicle() || mob_entity.is_leashed() { return true; } let Some(owner) = owner else { @@ -133,124 +132,110 @@ impl FollowOwnerGoal { } impl Goal for FollowOwnerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(owner) = Self::find_owner(mob) else { - return false; - }; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(owner) = Self::find_owner(mob) else { + return false; + }; - if Self::unable_to_move_to_owner(mob, Some(&owner)).await { - return false; - } + if Self::unable_to_move_to_owner(mob, Some(&owner)) { + return false; + } - let dist_sq = Self::distance_to_owner_sq(mob, &owner); - if dist_sq < self.start_distance_sq { - return false; - } + let dist_sq = Self::distance_to_owner_sq(mob, &owner); + if dist_sq < self.start_distance_sq { + return false; + } - self.owner = Some(owner); - true - }) + self.owner = Some(owner); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let is_idle = { - let navigator = mob + fn should_continue(&self, mob: &dyn Mob) -> bool { + let is_idle = { + let navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.is_idle() + }; + if is_idle { + return false; + } + + if Self::unable_to_move_to_owner(mob, self.owner.as_deref()) { + return false; + } + + let Some(owner) = &self.owner else { + return false; + }; + + let dist_sq = Self::distance_to_owner_sq(mob, owner); + dist_sq > self.stop_distance_sq + } + + fn start(&mut self, mob: &dyn Mob) { + self.time_to_recalc_path = 0; + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + self.old_water_cost = navigator.get_pathfinding_malus(PathType::Water); + navigator.set_pathfinding_malus(PathType::Water, 0.0); + } + + fn stop(&mut self, mob: &dyn Mob) { + self.owner = None; + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.stop(); + navigator.set_pathfinding_malus(PathType::Water, self.old_water_cost); + } + + fn tick(&mut self, mob: &dyn Mob) { + let Some(owner) = &self.owner else { + return; + }; + + let is_owner_far_away = Self::should_try_teleport_to_owner(mob, owner); + + if !is_owner_far_away { + let mob_entity = mob.get_mob_entity(); + let owner_eye_pos = owner.living_entity.entity.get_eye_pos(); + let mut look_control = mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + look_control.look_at_with_range( + owner_eye_pos.x, + owner_eye_pos.y, + owner_eye_pos.z, + 10.0, + mob.get_max_look_pitch_change(), + ); + } + + self.time_to_recalc_path -= 1; + if self.time_to_recalc_path <= 0 { + self.time_to_recalc_path = to_goal_ticks(10); + if is_owner_far_away { + Self::try_teleport_to_owner(mob, owner); + } else { + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let owner_pos = owner.living_entity.entity.pos.load(); + let mut navigator = mob .get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.is_idle() - }; - if is_idle { - return false; + navigator.set_progress(NavigatorGoal::new(mob_pos, owner_pos, self.speed_modifier)); } - - if Self::unable_to_move_to_owner(mob, self.owner.as_deref()).await { - return false; - } - - let Some(owner) = &self.owner else { - return false; - }; - - let dist_sq = Self::distance_to_owner_sq(mob, owner); - dist_sq > self.stop_distance_sq - }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.time_to_recalc_path = 0; - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - self.old_water_cost = navigator.get_pathfinding_malus(PathType::Water); - navigator.set_pathfinding_malus(PathType::Water, 0.0); - }) - } - - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.owner = None; - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.stop(); - navigator.set_pathfinding_malus(PathType::Water, self.old_water_cost); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(owner) = &self.owner else { - return; - }; - - let is_owner_far_away = Self::should_try_teleport_to_owner(mob, owner); - - if !is_owner_far_away { - let mob_entity = mob.get_mob_entity(); - let owner_eye_pos = owner.living_entity.entity.get_eye_pos(); - let mut look_control = mob_entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.look_at_with_range( - owner_eye_pos.x, - owner_eye_pos.y, - owner_eye_pos.z, - 10.0, - mob.get_max_look_pitch_change(), - ); - } - - self.time_to_recalc_path -= 1; - if self.time_to_recalc_path <= 0 { - self.time_to_recalc_path = to_goal_ticks(10); - if is_owner_far_away { - Self::try_teleport_to_owner(mob, owner); - } else { - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let owner_pos = owner.living_entity.entity.pos.load(); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new( - mob_pos, - owner_pos, - self.speed_modifier, - )); - } - } - }) + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/follow_parent.rs b/crates/pumpkin/src/entity/ai/goal/follow_parent.rs index 5886f4ff6..2b4416394 100644 --- a/crates/pumpkin/src/entity/ai/goal/follow_parent.rs +++ b/crates/pumpkin/src/entity/ai/goal/follow_parent.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use std::sync::atomic::Ordering::Relaxed; -use super::{Controls, Goal, GoalFuture, to_goal_ticks}; +use super::{Controls, Goal, to_goal_ticks}; use crate::entity::{EntityBase, ai::pathfinder::NavigatorGoal, mob::Mob}; const SEARCH_RADIUS: f64 = 8.0; @@ -62,67 +62,57 @@ impl FollowParentGoal { } impl Goal for FollowParentGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let age = mob.get_mob_entity().living_entity.entity.age.load(Relaxed); - if age >= 0 { - return false; - } - self.parent = Self::find_parent(mob); - self.parent.is_some() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let age = mob.get_mob_entity().living_entity.entity.age.load(Relaxed); + if age >= 0 { + return false; + } + self.parent = Self::find_parent(mob); + self.parent.is_some() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let age = mob.get_mob_entity().living_entity.entity.age.load(Relaxed); - if age >= 0 { - return false; - } - let Some(parent) = &self.parent else { - return false; - }; - let parent_entity = parent.get_entity(); - if !parent_entity.is_alive() { - return false; - } + fn should_continue(&self, mob: &dyn Mob) -> bool { + let age = mob.get_mob_entity().living_entity.entity.age.load(Relaxed); + if age >= 0 { + return false; + } + let Some(parent) = &self.parent else { + return false; + }; + let parent_entity = parent.get_entity(); + if !parent_entity.is_alive() { + return false; + } + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let parent_pos = parent_entity.pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(&parent_pos); + (MIN_DISTANCE_SQ..=MAX_DISTANCE_SQ).contains(&dist_sq) + } + + fn start(&mut self, _mob: &dyn Mob) { + self.delay = 0; + } + + fn tick(&mut self, mob: &dyn Mob) { + self.delay -= 1; + if self.delay > 0 { + return; + } + self.delay = to_goal_ticks(10); + if let Some(parent) = &self.parent { let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let parent_pos = parent_entity.pos.load(); - let dist_sq = mob_pos.squared_distance_to_vec(&parent_pos); - (MIN_DISTANCE_SQ..=MAX_DISTANCE_SQ).contains(&dist_sq) - }) + let parent_pos = parent.get_entity().pos.load(); + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.set_progress(NavigatorGoal::new(mob_pos, parent_pos, self.speed)); + } } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.delay = 0; - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.delay -= 1; - if self.delay > 0 { - return; - } - self.delay = to_goal_ticks(10); - if let Some(parent) = &self.parent { - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let parent_pos = parent.get_entity().pos.load(); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(mob_pos, parent_pos, self.speed)); - } - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.parent = None; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.parent = None; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/goal_selector.rs b/crates/pumpkin/src/entity/ai/goal/goal_selector.rs index ed14e15ce..5a729361b 100644 --- a/crates/pumpkin/src/entity/ai/goal/goal_selector.rs +++ b/crates/pumpkin/src/entity/ai/goal/goal_selector.rs @@ -22,14 +22,14 @@ impl GoalSelector { .push(PrioritizedGoal::new(TypeId::of::(), priority, goal)); } - pub async fn remove_goal(&mut self, mob: &dyn Mob) { - let mut stopped = self.remove_goal_sync::(); + pub fn remove_goal(&mut self, mob: &dyn Mob) { + let mut stopped = self.remove_goal_by_type_id(TypeId::of::()); for goal in &mut stopped { - goal.stop(mob).await; + goal.stop(mob); } } - pub fn remove_goal_sync(&mut self) -> Vec { + pub fn remove_goals(&mut self) -> Vec { self.remove_goal_by_type_id(TypeId::of::()) } @@ -95,13 +95,13 @@ impl GoalSelector { true } - pub async fn tick(&mut self, mob: &dyn Mob) { + pub fn tick(&mut self, mob: &dyn Mob) { for prioritized_goal in &mut self.goals { if prioritized_goal.running && (Self::uses_any(prioritized_goal, self.disabled_controls) - || !prioritized_goal.should_continue(mob).await) + || !prioritized_goal.should_continue(mob)) { - prioritized_goal.stop(mob).await; + prioritized_goal.stop(mob); } } @@ -115,28 +115,28 @@ impl GoalSelector { if !self.goals[i].running && !Self::uses_any(&self.goals[i], self.disabled_controls) && self.can_replace_all(&self.goals[i]) - && self.goals[i].can_start(mob).await + && self.goals[i].can_start(mob) { let controls = self.goals[i].controls(); for control in Controls::ITER { if controls.get(control) { if let Some(goal) = self.get_goal_by_control(control) { - goal.stop(mob).await; + goal.stop(mob); } self.goals_by_control[control.idx()] = i; } } - self.goals[i].start(mob).await; + self.goals[i].start(mob); } } - self.tick_goals(mob, true).await; + self.tick_goals(mob, true); } - pub async fn tick_goals(&mut self, mob: &dyn Mob, tick_all: bool) { + pub fn tick_goals(&mut self, mob: &dyn Mob, tick_all: bool) { for prioritized_goal in &mut self.goals { if prioritized_goal.running && (tick_all || prioritized_goal.should_run_every_tick()) { - prioritized_goal.tick(mob).await; + prioritized_goal.tick(mob); } } } diff --git a/crates/pumpkin/src/entity/ai/goal/look_around.rs b/crates/pumpkin/src/entity/ai/goal/look_around.rs index 25b70d95d..ffb4d5310 100644 --- a/crates/pumpkin/src/entity/ai/goal/look_around.rs +++ b/crates/pumpkin/src/entity/ai/goal/look_around.rs @@ -1,7 +1,7 @@ use std::f64::consts::TAU; -use super::{Controls, Goal}; -use crate::entity::{ai::goal::GoalFuture, mob::Mob}; +use crate::entity::ai::goal::{Controls, Goal}; +use crate::entity::mob::Mob; use rand::RngExt; pub struct RandomLookAroundGoal { @@ -23,41 +23,37 @@ impl Default for RandomLookAroundGoal { } impl Goal for RandomLookAroundGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { mob.get_random().random::() < 0.02 }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + mob.get_random().random::() < 0.02 } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.look_time >= 0 }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.look_time >= 0 } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let d = TAU * mob.get_random().random::(); - self.delta_x = d.cos(); - self.delta_z = d.sin(); - let look_time = 20 + mob.get_random().random_range(0..20); - self.look_time = look_time; - }) + fn start(&mut self, mob: &dyn Mob) { + let d = TAU * mob.get_random().random::(); + self.delta_x = d.cos(); + self.delta_z = d.sin(); + let look_time = 20 + mob.get_random().random_range(0..20); + self.look_time = look_time; } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - self.look_time -= 1; - let mut look_control = mob_entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + fn tick(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + self.look_time -= 1; + let mut look_control = mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - let pos = mob_entity.living_entity.entity.pos.load(); - look_control.look_at( - mob, - pos.x + self.delta_x, - mob_entity.living_entity.entity.get_eye_y(), - pos.z + self.delta_z, - ); - }) + let pos = mob_entity.living_entity.entity.pos.load(); + look_control.look_at( + mob, + pos.x + self.delta_x, + mob_entity.living_entity.entity.get_eye_y(), + pos.z + self.delta_z, + ); } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/look_at_entity.rs b/crates/pumpkin/src/entity/ai/goal/look_at_entity.rs index 611514ef4..bece79b0e 100644 --- a/crates/pumpkin/src/entity/ai/goal/look_at_entity.rs +++ b/crates/pumpkin/src/entity/ai/goal/look_at_entity.rs @@ -1,5 +1,5 @@ use super::{Controls, Goal}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::mob::Mob; use crate::entity::predicate::EntityPredicate; @@ -60,16 +60,10 @@ impl LookAtEntityGoal { target_predicate.base_max_distance = range as f64; // TODO if target_type == &EntityType::PLAYER { target_predicate.set_predicate(move |living_entity, _world| { - let mob_weak = mob_weak.clone(); - async move { - if let Some(mob_arc) = mob_weak.upgrade() { - let predicate = EntityPredicate::Rides(mob_arc.get_entity()); - predicate.test(&living_entity.entity).await - } else { - // MobEntity is destroyed - false - } - } + mob_weak.upgrade().is_some_and(|mob_arc| { + let predicate = EntityPredicate::Rides(mob_arc.get_entity()); + predicate.test(&living_entity.entity) + }) }); } target_predicate @@ -77,88 +71,81 @@ impl LookAtEntityGoal { } impl Goal for LookAtEntityGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - if mob.get_random().random::() >= self.chance { + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if mob.get_random().random::() >= self.chance { + return false; + } + + let mob_entity = mob.get_mob_entity(); + + { + let mob_target = mob_entity + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if mob_target.is_some() { + self.target.clone_from(&mob_target); + } + } + + let world = mob_entity.living_entity.entity.world.load(); + let mob_pos = mob_entity.living_entity.entity.pos.load(); + + if *self.target_type == EntityType::PLAYER { + self.target = world + .get_closest_player(mob_pos, self.range.into()) + .map(|p: Arc| p as Arc); + } else { + self.target = + world.get_closest_entity(mob_pos, self.range.into(), Some(&[self.target_type])); + } + + self.target.is_some() + } + + fn should_continue(&self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + if let Some(target) = &self.target { + if !target.get_entity().is_alive() { return false; } - - let mob_entity = mob.get_mob_entity(); - - { - let mob_target = mob_entity.target.lock().await; - if mob_target.is_some() { - self.target.clone_from(&mob_target); - } - } - - let world = mob_entity.living_entity.entity.world.load(); let mob_pos = mob_entity.living_entity.entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + if mob_pos.squared_distance_to_vec(&target_pos) as f32 > (self.range * self.range) { + return false; + } + return self.look_time > 0; + } + false + } - if *self.target_type == EntityType::PLAYER { - self.target = world - .get_closest_player(mob_pos, self.range.into()) - .map(|p: Arc| p as Arc); + fn start(&mut self, mob: &dyn Mob) { + self.look_time = self.get_tick_count(40 + mob.get_random().random_range(0..40)); + } + + fn stop(&mut self, _mob: &dyn Mob) { + self.target = None; + } + + fn tick(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + if let Some(target) = &self.target + && target.get_entity().is_alive() + { + let target_entity = target.get_entity(); + let target_pos = target_entity.pos.load(); + let look_y = if self.look_forward { + mob_entity.living_entity.entity.get_eye_y() } else { - self.target = - world.get_closest_entity(mob_pos, self.range.into(), Some(&[self.target_type])); - } - - self.target.is_some() - }) - } - - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - if let Some(target) = &self.target { - if !target.get_entity().is_alive() { - return false; - } - let mob_pos = mob_entity.living_entity.entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - if mob_pos.squared_distance_to_vec(&target_pos) as f32 > (self.range * self.range) { - return false; - } - return self.look_time > 0; - } - false - }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.look_time = self.get_tick_count(40 + mob.get_random().random_range(0..40)); - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.target = None; - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - if let Some(target) = &self.target - && target.get_entity().is_alive() - { - let target_entity = target.get_entity(); - let target_pos = target_entity.pos.load(); - let look_y = if self.look_forward { - mob_entity.living_entity.entity.get_eye_y() - } else { - target_entity.get_eye_y() - }; - mob_entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at(mob, target_pos.x, look_y, target_pos.z); - self.look_time -= 1; - } - }) + target_entity.get_eye_y() + }; + mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at(mob, target_pos.x, look_y, target_pos.z); + self.look_time -= 1; + } } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/melee_attack.rs b/crates/pumpkin/src/entity/ai/goal/melee_attack.rs index 05da71025..262955ed4 100644 --- a/crates/pumpkin/src/entity/ai/goal/melee_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/melee_attack.rs @@ -1,6 +1,5 @@ use super::{Controls, Goal}; -use crate::entity::EntityBase; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::predicate::EntityPredicate; @@ -47,172 +46,151 @@ impl MeleeAttackGoal { } impl Goal for MeleeAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let time = { - let world = mob.get_entity().world.load(); - let level_time = world.level_time.lock().await; - level_time.world_age - }; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let time = mob.get_entity().world.load().get_world_age(); - if time - self.last_update_time < MAX_ATTACK_TIME { - return false; - } - self.last_update_time = time; + if time - self.last_update_time < MAX_ATTACK_TIME { + return false; + } + self.last_update_time = time; - let target = mob.get_mob_entity().target.lock().await; + let target = mob.get_mob_entity().get_target(); - let Some(target) = target.as_ref() else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } - // TODO: add path when is implemented Navigation - true //TODO: modify that because if a path to the target not exists then call mob.is_in_attack_range(target) - }) + let Some(target) = target.as_ref() else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } + // TODO: add path when is implemented Navigation + true //TODO: modify that because if a path to the target not exists then call mob.is_in_attack_range(target) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await.clone(); + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); - let Some(target) = target else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } + let Some(target) = target else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } - if !self.pause_when_mob_idle { - let is_idle = mob - .get_mob_entity() - .navigator - .try_lock() - .is_ok_and(|navigator| navigator.is_idle()); - return !is_idle; - } - - let is_valid_target = !target - .get_player() - .is_some_and(|p| p.is_spectator() || p.is_creative()); - - let in_range = mob + if !self.pause_when_mob_idle { + let is_idle = mob .get_mob_entity() - .is_in_position_target_range_pos(&target.get_entity().block_pos.load()); + .navigator + .try_lock() + .is_ok_and(|navigator| navigator.is_idle()); + return !is_idle; + } - in_range && is_valid_target - }) + let is_valid_target = !target + .get_player() + .is_some_and(|p| p.is_spectator() || p.is_creative()); + + let in_range = mob + .get_mob_entity() + .is_in_position_target_range_pos(&target.get_entity().block_pos.load()); + + in_range && is_valid_target } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - // TODO: add missing fields like mob attacking to true and correct Navigation methods + fn start(&mut self, mob: &dyn Mob) { + // TODO: add missing fields like mob attacking to true and correct Navigation methods - let target = mob.get_mob_entity().target.lock().await.clone(); - if let Some(target) = target { - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let target_pos = target.get_entity().pos.load(); - navigator.set_progress(NavigatorGoal { - current_progress: mob.get_entity().pos.load(), - destination: target_pos, - speed: self.speed, - }); - self.last_target_position = Some(target_pos); - } - self.update_countdown_ticks = 0; - self.cooldown = 0; - }) - } - - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - // Only clear target if they switched to creative/spectator - let should_clear = { - let target = mob.get_mob_entity().target.lock().await; - if let Some(entity) = target.as_deref() { - !EntityPredicate::ExceptCreativeOrSpectator - .test(entity.get_entity()) - .await - } else { - false - } - }; - if should_clear { - mob.set_mob_target(None).await; - } - - // Vanilla: this.mob.getNavigation().stop() - mob.get_mob_entity() + let target = mob.get_mob_entity().get_target().clone(); + if let Some(target) = target { + let mut navigator = mob + .get_mob_entity() .navigator .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - self.last_target_position = None; - }) + .unwrap_or_else(std::sync::PoisonError::into_inner); + let target_pos = target.get_entity().pos.load(); + navigator.set_progress(NavigatorGoal { + current_progress: mob.get_entity().pos.load(), + destination: target_pos, + speed: self.speed, + }); + self.last_target_position = Some(target_pos); + } + self.update_countdown_ticks = 0; + self.cooldown = 0; } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return; - }; + fn stop(&mut self, mob: &dyn Mob) { + // Only clear target if they switched to creative/spectator + let should_clear = mob + .get_mob_entity() + .get_target() + .as_deref() + .is_some_and(|entity| { + !EntityPredicate::ExceptCreativeOrSpectator.test(entity.get_entity()) + }); + if should_clear { + mob.set_mob_target(None); + } - mob.get_mob_entity() - .look_control + // Vanilla: this.mob.getNavigation().stop() + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); + self.last_target_position = None; + } + + fn tick(&mut self, mob: &dyn Mob) { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return; + }; + + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity_with_range(&target, 30.0, 30.0); + + self.update_countdown_ticks = (self.update_countdown_ticks - 1).max(0); + + let current_target_pos = target.get_entity().pos.load(); + let should_update_nav = self.update_countdown_ticks <= 0 + && (self.last_target_position.is_none_or(|last_pos| { + current_target_pos.squared_distance_to_vec(&last_pos) >= 1.0 + }) || mob.get_random().random_range(0..20) == 0); + + if should_update_nav { + let mob_pos = mob.get_entity().pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(¤t_target_pos); + let mut navigator = mob + .get_mob_entity() + .navigator .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity_with_range(&target, 30.0, 30.0); - - self.update_countdown_ticks = (self.update_countdown_ticks - 1).max(0); - - let current_target_pos = target.get_entity().pos.load(); - let should_update_nav = self.update_countdown_ticks <= 0 - && (self.last_target_position.is_none_or(|last_pos| { - current_target_pos.squared_distance_to_vec(&last_pos) >= 1.0 - }) || mob.get_random().random_range(0..20) == 0); - - if should_update_nav { - let mob_pos = mob.get_entity().pos.load(); - let dist_sq = mob_pos.squared_distance_to_vec(¤t_target_pos); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal { - current_progress: mob_pos, - destination: current_target_pos, - speed: self.speed, - }); - self.last_target_position = Some(current_target_pos); - self.update_countdown_ticks = 4 + mob.get_random().random_range(0..7); - if dist_sq > 1024.0 { - self.update_countdown_ticks += 10; - } else if dist_sq > 256.0 { - self.update_countdown_ticks += 5; - } + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.set_progress(NavigatorGoal { + current_progress: mob_pos, + destination: current_target_pos, + speed: self.speed, + }); + self.last_target_position = Some(current_target_pos); + self.update_countdown_ticks = 4 + mob.get_random().random_range(0..7); + if dist_sq > 1024.0 { + self.update_countdown_ticks += 10; + } else if dist_sq > 256.0 { + self.update_countdown_ticks += 5; } + } - self.cooldown = (self.cooldown - 1).max(0); + self.cooldown = (self.cooldown - 1).max(0); - // TODO: Add visibility check (canSee) - requires world raycast - if self.cooldown <= 0 - && mob - .get_mob_entity() - .is_in_attack_range(target.as_ref()) - .await - { - self.cooldown = self.get_max_cooldown(); - mob.get_mob_entity().living_entity.swing_hand().await; - mob.get_mob_entity().try_attack(mob, target.as_ref()).await; - } - }) + // TODO: Add visibility check (canSee) - requires world raycast + if self.cooldown <= 0 && mob.get_mob_entity().is_in_attack_range(target.as_ref()) { + self.cooldown = self.get_max_cooldown(); + mob.get_mob_entity().living_entity.swing_hand(); + mob.get_mob_entity() + .try_attack(mob.get_entity(), target.as_ref()); + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/mod.rs b/crates/pumpkin/src/entity/ai/goal/mod.rs index 5c7661782..c47deffaf 100644 --- a/crates/pumpkin/src/entity/ai/goal/mod.rs +++ b/crates/pumpkin/src/entity/ai/goal/mod.rs @@ -1,5 +1,5 @@ use crate::entity::mob::Mob; -use std::{any::TypeId, ops::BitOr, pin::Pin, ptr}; +use std::{any::TypeId, ops::BitOr, ptr}; pub mod active_target; pub mod ambient_stand; @@ -48,33 +48,25 @@ pub const fn to_goal_ticks(server_ticks: i32) -> i32 { -(-server_ticks).div_euclid(2) } -pub type GoalFuture<'a, T> = Pin + Send + 'a>>; - pub trait Goal: Send + Sync { /// How should the `Goal` initially start? - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { false }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + false } /// When it's started, how should it continue to run? - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { false }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + false } /// Call when goal start - fn start<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async {}) - } + fn start(&mut self, _mob: &dyn Mob) {} /// Call when goal stop - fn stop<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async {}) - } + fn stop(&mut self, _mob: &dyn Mob) {} /// If the `Goal` is running, this gets called every tick. - fn tick<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async {}) - } + fn tick(&mut self, _mob: &dyn Mob) {} fn should_run_every_tick(&self) -> bool { false @@ -110,32 +102,44 @@ impl Controls { pub const ITER: [Self; 4] = [Self::MOVE, Self::LOOK, Self::JUMP, Self::TARGET]; #[must_use] - pub fn empty() -> Self { - Self::default() + pub const fn empty() -> Self { + Self(0) } - pub const fn set(&mut self, control: Self, val: bool) { - if val { - self.0 |= control.0; + #[must_use] + pub const fn contains(&self, other: Self) -> bool { + (self.0 & other.0) == other.0 + } + + pub const fn insert(&mut self, other: Self) { + self.0 |= other.0; + } + + pub const fn remove(&mut self, other: Self) { + self.0 &= !other.0; + } + + pub const fn set(&mut self, control: Self, value: bool) { + if value { + self.insert(control); } else { - self.0 &= !control.0; + self.remove(control); } } + #[must_use] + pub const fn is_empty(&self) -> bool { + self.0 == 0 + } + #[must_use] pub const fn get(&self, control: Self) -> bool { - self.0 & control.0 != 0 + (self.0 & control.0) != 0 } #[must_use] - pub fn idx(&self) -> usize { - for (i, control) in Self::ITER.into_iter().enumerate() { - if self.get(control) { - return i; - } - } - tracing::error!("Controls::idx called with no controls set"); - 0 + pub const fn idx(&self) -> usize { + self.0.trailing_zeros() as usize } } @@ -172,36 +176,30 @@ impl PrioritizedGoal { } impl Goal for PrioritizedGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.goal.can_start(mob).await }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.goal.can_start(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - if !self.running { - self.running = true; - self.goal.start(mob).await; - } - }) + fn start(&mut self, mob: &dyn Mob) { + if !self.running { + self.running = true; + self.goal.start(mob); + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - if self.running { - self.running = false; - self.goal.stop(mob).await; - } - }) + fn stop(&mut self, mob: &dyn Mob) { + if self.running { + self.running = false; + self.goal.stop(mob); + } } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.goal.tick(mob).await; - }) + fn tick(&mut self, mob: &dyn Mob) { + self.goal.tick(mob); } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs b/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs index e26a8e5ce..aa3c30616 100644 --- a/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs +++ b/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs @@ -1,12 +1,11 @@ use super::{Controls, Goal, to_goal_ticks}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::{ai::goal::ParentHandle, mob::Mob}; use crate::world::World; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; -use std::pin::Pin; use std::sync::Arc; const MIN_WAITING_TIME: i32 = 1200; @@ -59,7 +58,7 @@ impl MoveToTargetPosGoal { to_goal_ticks(MIN_INTERVAL + mob.get_random().random_range(0..MIN_INTERVAL)) } - pub async fn find_target_pos(&mut self, mob: &dyn Mob) -> bool { + pub fn find_target_pos(&mut self, mob: &dyn Mob) -> bool { let block_pos = mob.get_entity().block_pos.load(); let mut block_pos_mut = BlockPos::new(0, 0, 0); @@ -78,11 +77,11 @@ impl MoveToTargetPosGoal { let world = mob.get_entity().world.load_full(); let can_target = - if let Some(move_to_target_pos) = self.move_to_target_pos.get() { - move_to_target_pos.is_target_pos(world, block_pos_mut).await - } else { - false - }; + self.move_to_target_pos + .get() + .is_some_and(|move_to_target_pos| { + move_to_target_pos.is_target_pos(world, block_pos_mut) + }); if mob .get_mob_entity() @@ -120,11 +119,7 @@ impl MoveToTargetPosGoal { // Contains overridable functions pub trait MoveToTargetPos: Send + Sync { - fn is_target_pos<'a>( - &'a self, - world: Arc, - block_pos: BlockPos, - ) -> Pin + Send + 'a>>; + fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool; fn get_desired_distance_to_target(&self) -> f64 { 1.0 @@ -132,77 +127,64 @@ pub trait MoveToTargetPos: Send + Sync { } impl Goal for MoveToTargetPosGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - if self.cooldown > 0 { - self.cooldown -= 1; - return false; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if self.cooldown > 0 { + self.cooldown -= 1; + return false; + } + self.cooldown = Self::get_interval(mob); + self.find_target_pos(mob) + } + + fn should_continue(&self, mob: &dyn Mob) -> bool { + let world = mob.get_entity().world.load_full(); + let can_target = self + .move_to_target_pos + .get() + .is_some_and(|x| x.is_target_pos(world, self.target_pos)); + self.trying_time >= -self.safe_waiting_time + && self.trying_time <= MAX_TRYING_TIME + && can_target + } + + fn start(&mut self, mob: &dyn Mob) { + Self::start_moving_to_target(mob); + self.trying_time = 0; + let random = mob.get_random().random_range(0..MIN_WAITING_TIME); + self.safe_waiting_time = + mob.get_random().random_range(random..MIN_WAITING_TIME) + MIN_WAITING_TIME; + } + + fn tick(&mut self, mob: &dyn Mob) { + let block_pos = self.get_target_pos(); + let block_pos: Vector3 = block_pos.to_f64(); + let Some(move_to_target_pos) = self.move_to_target_pos.get() else { + return; + }; + let desired_distance = move_to_target_pos.get_desired_distance_to_target(); + + if block_pos.squared_distance_to_vec(&mob.get_entity().pos.load()) + < desired_distance * desired_distance + { + self.reached = true; + self.trying_time -= 1; + } else { + self.reached = false; + self.trying_time += 1; + if self.should_reset_path() { + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + navigator.set_progress(NavigatorGoal { + current_progress: mob.get_entity().pos.load(), + destination: Vector3::new(block_pos.x + 0.5, block_pos.y, block_pos.z + 0.5), + speed: self.speed, + }); } - self.cooldown = Self::get_interval(mob); - self.find_target_pos(mob).await - }) - } - - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let world = mob.get_entity().world.load_full(); - let can_target = if let Some(x) = self.move_to_target_pos.get() { - x.is_target_pos(world, self.target_pos).await - } else { - false - }; - self.trying_time >= -self.safe_waiting_time - && self.trying_time <= MAX_TRYING_TIME - && can_target - }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - Self::start_moving_to_target(mob); - self.trying_time = 0; - let random = mob.get_random().random_range(0..MIN_WAITING_TIME); - self.safe_waiting_time = - mob.get_random().random_range(random..MIN_WAITING_TIME) + MIN_WAITING_TIME; - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let block_pos = self.get_target_pos(); - let block_pos: Vector3 = block_pos.to_f64(); - let Some(move_to_target_pos) = self.move_to_target_pos.get() else { - return; - }; - let desired_distance = move_to_target_pos.get_desired_distance_to_target(); - - if block_pos.squared_distance_to_vec(&mob.get_entity().pos.load()) - < desired_distance * desired_distance - { - self.reached = true; - self.trying_time -= 1; - } else { - self.reached = false; - self.trying_time += 1; - if self.should_reset_path() { - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - - navigator.set_progress(NavigatorGoal { - current_progress: mob.get_entity().pos.load(), - destination: Vector3::new( - block_pos.x + 0.5, - block_pos.y, - block_pos.z + 0.5, - ), - speed: self.speed, - }); - } - } - }) + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/offer_flower.rs b/crates/pumpkin/src/entity/ai/goal/offer_flower.rs index 04f6b9513..29866878f 100644 --- a/crates/pumpkin/src/entity/ai/goal/offer_flower.rs +++ b/crates/pumpkin/src/entity/ai/goal/offer_flower.rs @@ -4,7 +4,7 @@ use pumpkin_data::entity::EntityStatus; use pumpkin_data::tag::{self, Taggable}; use rand::RngExt; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::mob::Mob; pub const OFFER_TICKS: i32 = 400; @@ -31,134 +31,131 @@ impl OfferFlowerGoal { } impl Goal for OfferFlowerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let world = mob.get_entity().world.load(); - if world.level_time.lock().await.is_night() { - return false; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let world = mob.get_entity().world.load(); + let is_night = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_night(); + if is_night { + return false; + } + + if mob.get_random().random_range(0..8000) != 0 { + return false; + } + + let golem_entity = mob.get_entity(); + let golem_pos = golem_entity.pos.load(); + let bb = golem_entity.bounding_box.load().expand(6.0, 2.0, 6.0); + let nearby = world.get_entities_at_box(&bb); + + let mut closest: Option<(i32, f64)> = None; + + for candidate in nearby { + let cand_entity = candidate.get_entity(); + if cand_entity.entity_id == golem_entity.entity_id { + continue; } - if mob.get_random().random_range(0..8000) != 0 { - return false; + if !cand_entity + .entity_type + .has_tag(&tag::EntityType::MINECRAFT_CANDIDATE_FOR_IRON_GOLEM_GIFT) + { + continue; } - let golem_entity = mob.get_entity(); - let golem_pos = golem_entity.pos.load(); - let bb = golem_entity.bounding_box.load().expand(6.0, 2.0, 6.0); - let nearby = world.get_entities_at_box(&bb); + let cand_pos = cand_entity.pos.load(); + let dx = cand_pos.x - golem_pos.x; + let dy = cand_pos.y - golem_pos.y; + let dz = cand_pos.z - golem_pos.z; + let dist_sq = dx * dx + dy * dy + dz * dz; - let mut closest: Option<(i32, f64)> = None; - - for candidate in nearby { - let cand_entity = candidate.get_entity(); - if cand_entity.entity_id == golem_entity.entity_id { - continue; - } - - if !cand_entity - .entity_type - .has_tag(&tag::EntityType::MINECRAFT_CANDIDATE_FOR_IRON_GOLEM_GIFT) - { - continue; - } - - let cand_pos = cand_entity.pos.load(); - let dx = cand_pos.x - golem_pos.x; - let dy = cand_pos.y - golem_pos.y; - let dz = cand_pos.z - golem_pos.z; - let dist_sq = dx * dx + dy * dy + dz * dz; - - if dist_sq <= 36.0 { - if let Some((_, closest_dist)) = closest { - if dist_sq < closest_dist { - closest = Some((cand_entity.entity_id, dist_sq)); - } - } else { + if dist_sq <= 36.0 { + if let Some((_, closest_dist)) = closest { + if dist_sq < closest_dist { closest = Some((cand_entity.entity_id, dist_sq)); } + } else { + closest = Some((cand_entity.entity_id, dist_sq)); } } + } - if let Some((id, _)) = closest { - self.target_entity_id = Some(id); - true - } else { - self.target_entity_id = None; - false - } - }) - } - - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.tick > 0 }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.tick = OFFER_TICKS; - if let Some(golem) = mob.as_iron_golem() { - golem.offer_flower(true); - } else { - let entity = mob.get_entity(); - let world = entity.world.load(); - world.send_entity_status(entity, EntityStatus::OfferFlower, None); - } - }) - } - - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(golem) = mob.as_iron_golem() { - golem.offer_flower(false); - } else { - let entity = mob.get_entity(); - let world = entity.world.load(); - world.send_entity_status(entity, EntityStatus::StopOfferFlower, None); - } - - if self.tick == 0 - && let Some(target_id) = self.target_entity_id - { - let world = mob.get_entity().world.load(); - if let Some(target) = world.get_entity_by_id(target_id) { - let target_entity = target.get_entity(); - let bb = mob.get_entity().bounding_box.load().expand(6.0, 2.0, 6.0); - if target_entity - .entity_type - .has_tag(&tag::EntityType::MINECRAFT_ACCEPTS_IRON_GOLEM_GIFT) - && bb.intersects(&target_entity.bounding_box.load()) - { - // Target accepted gift - } - } - } - + if let Some((id, _)) = closest { + self.target_entity_id = Some(id); + true + } else { self.target_entity_id = None; - }) + false + } } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target_id) = self.target_entity_id { - let world = mob.get_entity().world.load(); - if let Some(target) = world.get_entity_by_id(target_id) { - let target_entity = target.get_entity(); - let target_pos = target_entity.pos.load(); - mob.get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_with_range( - target_pos.x, - target_entity.get_eye_y(), - target_pos.z, - 30.0, - 30.0, - ); + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.tick > 0 + } + + fn start(&mut self, mob: &dyn Mob) { + self.tick = OFFER_TICKS; + if let Some(golem) = mob.as_iron_golem() { + golem.offer_flower(true); + } else { + let entity = mob.get_entity(); + let world = entity.world.load(); + world.send_entity_status(entity, EntityStatus::OfferFlower, None); + } + } + + fn stop(&mut self, mob: &dyn Mob) { + if let Some(golem) = mob.as_iron_golem() { + golem.offer_flower(false); + } else { + let entity = mob.get_entity(); + let world = entity.world.load(); + world.send_entity_status(entity, EntityStatus::StopOfferFlower, None); + } + + if self.tick == 0 + && let Some(target_id) = self.target_entity_id + { + let world = mob.get_entity().world.load(); + if let Some(target) = world.get_entity_by_id(target_id) { + let target_entity = target.get_entity(); + let bb = mob.get_entity().bounding_box.load().expand(6.0, 2.0, 6.0); + if target_entity + .entity_type + .has_tag(&tag::EntityType::MINECRAFT_ACCEPTS_IRON_GOLEM_GIFT) + && bb.intersects(&target_entity.bounding_box.load()) + { + // Target accepted gift } } - self.tick -= 1; - }) + } + + self.target_entity_id = None; + } + + fn tick(&mut self, mob: &dyn Mob) { + if let Some(target_id) = self.target_entity_id { + let world = mob.get_entity().world.load(); + if let Some(target) = world.get_entity_by_id(target_id) { + let target_entity = target.get_entity(); + let target_pos = target_entity.pos.load(); + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_with_range( + target_pos.x, + target_entity.get_eye_y(), + target_pos.z, + 30.0, + 30.0, + ); + } + } + self.tick -= 1; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/open_door.rs b/crates/pumpkin/src/entity/ai/goal/open_door.rs index 59ff900a8..83433c4f6 100644 --- a/crates/pumpkin/src/entity/ai/goal/open_door.rs +++ b/crates/pumpkin/src/entity/ai/goal/open_door.rs @@ -1,5 +1,5 @@ use super::door_interact::DoorInteractGoal; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::mob::Mob; pub struct OpenDoorGoal { @@ -26,37 +26,29 @@ impl Default for OpenDoorGoal { } impl Goal for OpenDoorGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.door_interact_goal.can_use(mob) }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.door_interact_goal.can_use(mob) } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - self.close_door && self.forget_time > 0 && self.door_interact_goal.can_continue_to_use() - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.close_door && self.forget_time > 0 && self.door_interact_goal.can_continue_to_use() } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.door_interact_goal.start_interaction(mob); - self.forget_time = 20; - self.door_interact_goal.set_open(mob, true).await; - }) + fn start(&mut self, mob: &dyn Mob) { + self.door_interact_goal.start_interaction(mob); + self.forget_time = 20; + self.door_interact_goal.set_open(mob, true); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if self.close_door { - self.door_interact_goal.set_open(mob, false).await; - } - }) + fn stop(&mut self, mob: &dyn Mob) { + if self.close_door { + self.door_interact_goal.set_open(mob, false); + } } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.forget_time -= 1; - self.door_interact_goal.tick_interaction(mob); - }) + fn tick(&mut self, mob: &dyn Mob) { + self.forget_time -= 1; + self.door_interact_goal.tick_interaction(mob); } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/owner_hurt_by_target.rs b/crates/pumpkin/src/entity/ai/goal/owner_hurt_by_target.rs index f610a7305..30424887b 100644 --- a/crates/pumpkin/src/entity/ai/goal/owner_hurt_by_target.rs +++ b/crates/pumpkin/src/entity/ai/goal/owner_hurt_by_target.rs @@ -1,4 +1,4 @@ -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::EntityBase; use crate::entity::mob::Mob; use std::sync::Arc; @@ -22,83 +22,75 @@ impl OwnerHurtByTargetGoal { } impl Goal for OwnerHurtByTargetGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - if !mob.is_tamed() || mob.is_sitting() { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if !mob.is_tamed() || mob.is_sitting() { + return false; + } - let Some(owner_uuid) = mob.get_owner_uuid() else { - return false; - }; + let Some(owner_uuid) = mob.get_owner_uuid() else { + return false; + }; - let entity = &mob.get_mob_entity().living_entity.entity; - let world = entity.world.load_full(); - let Some(owner) = world.get_player_by_uuid(owner_uuid) else { - return false; - }; + let entity = &mob.get_mob_entity().living_entity.entity; + let world = entity.world.load_full(); + let Some(owner) = world.get_player_by_uuid(owner_uuid) else { + return false; + }; - let attacked_time = owner.living_entity.last_attacked_time.load(Relaxed); - if attacked_time == self.last_attacked_time { - return false; - } + let attacked_time = owner.living_entity.last_attacked_time.load(Relaxed); + if attacked_time == self.last_attacked_time { + return false; + } - let attacker_id = owner.living_entity.last_attacker_id.load(Relaxed); - if attacker_id == 0 { - return false; - } + let attacker_id = owner.living_entity.last_attacker_id.load(Relaxed); + if attacker_id == 0 { + return false; + } - let Some(attacker) = world.get_entity_by_id(attacker_id) else { - return false; - }; + let Some(attacker) = world.get_entity_by_id(attacker_id) else { + return false; + }; - if !attacker.get_entity().is_alive() { - return false; - } + if !attacker.get_entity().is_alive() { + return false; + } - if !mob.can_attack_with_owner(attacker.as_ref(), &*owner) { - return false; - } + if !mob.can_attack_with_owner(attacker.as_ref(), &*owner) { + return false; + } - self.target = Some(attacker); - true - }) + self.target = Some(attacker); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await; - let Some(t) = target.as_ref() else { - return false; - }; - if !t.get_entity().is_alive() { - return false; - } - let my_pos = mob.get_entity().pos.load(); - let target_pos = t.get_entity().pos.load(); - my_pos.squared_distance_to_vec(&target_pos) <= FOLLOW_RANGE * FOLLOW_RANGE - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target(); + let Some(t) = target.as_ref() else { + return false; + }; + if !t.get_entity().is_alive() { + return false; + } + let my_pos = mob.get_entity().pos.load(); + let target_pos = t.get_entity().pos.load(); + my_pos.squared_distance_to_vec(&target_pos) <= FOLLOW_RANGE * FOLLOW_RANGE } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - mob_entity.target.lock().await.clone_from(&self.target); + fn start(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + mob_entity.set_target(self.target.clone()); - if let Some(owner_uuid) = mob.get_owner_uuid() { - let world = mob_entity.living_entity.entity.world.load_full(); - if let Some(owner) = world.get_player_by_uuid(owner_uuid) { - self.last_attacked_time = owner.living_entity.last_attacked_time.load(Relaxed); - } + if let Some(owner_uuid) = mob.get_owner_uuid() { + let world = mob_entity.living_entity.entity.world.load_full(); + if let Some(owner) = world.get_player_by_uuid(owner_uuid) { + self.last_attacked_time = owner.living_entity.last_attacked_time.load(Relaxed); } - }) + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.target = None; - *mob.get_mob_entity().target.lock().await = None; - }) + fn stop(&mut self, mob: &dyn Mob) { + self.target = None; + mob.get_mob_entity().set_target(None); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/owner_hurt_target.rs b/crates/pumpkin/src/entity/ai/goal/owner_hurt_target.rs index 75b3d591b..4bd3e6633 100644 --- a/crates/pumpkin/src/entity/ai/goal/owner_hurt_target.rs +++ b/crates/pumpkin/src/entity/ai/goal/owner_hurt_target.rs @@ -1,4 +1,4 @@ -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::EntityBase; use crate::entity::mob::Mob; use std::sync::Arc; @@ -22,83 +22,75 @@ impl OwnerHurtTargetGoal { } impl Goal for OwnerHurtTargetGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - if !mob.is_tamed() || mob.is_sitting() { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if !mob.is_tamed() || mob.is_sitting() { + return false; + } - let Some(owner_uuid) = mob.get_owner_uuid() else { - return false; - }; + let Some(owner_uuid) = mob.get_owner_uuid() else { + return false; + }; - let entity = &mob.get_mob_entity().living_entity.entity; - let world = entity.world.load_full(); - let Some(owner) = world.get_player_by_uuid(owner_uuid) else { - return false; - }; + let entity = &mob.get_mob_entity().living_entity.entity; + let world = entity.world.load_full(); + let Some(owner) = world.get_player_by_uuid(owner_uuid) else { + return false; + }; - let attack_time = owner.living_entity.last_attack_time.load(Relaxed); - if attack_time == self.last_attack_time { - return false; - } + let attack_time = owner.living_entity.last_attack_time.load(Relaxed); + if attack_time == self.last_attack_time { + return false; + } - let attacking_id = owner.living_entity.last_attacking_id.load(Relaxed); - if attacking_id == 0 { - return false; - } + let attacking_id = owner.living_entity.last_attacking_id.load(Relaxed); + if attacking_id == 0 { + return false; + } - let Some(target) = world.get_entity_by_id(attacking_id) else { - return false; - }; + let Some(target) = world.get_entity_by_id(attacking_id) else { + return false; + }; - if !target.get_entity().is_alive() { - return false; - } + if !target.get_entity().is_alive() { + return false; + } - if !mob.can_attack_with_owner(target.as_ref(), &*owner) { - return false; - } + if !mob.can_attack_with_owner(target.as_ref(), &*owner) { + return false; + } - self.target = Some(target); - true - }) + self.target = Some(target); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await; - let Some(t) = target.as_ref() else { - return false; - }; - if !t.get_entity().is_alive() { - return false; - } - let my_pos = mob.get_entity().pos.load(); - let target_pos = t.get_entity().pos.load(); - my_pos.squared_distance_to_vec(&target_pos) <= FOLLOW_RANGE * FOLLOW_RANGE - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target(); + let Some(t) = target.as_ref() else { + return false; + }; + if !t.get_entity().is_alive() { + return false; + } + let my_pos = mob.get_entity().pos.load(); + let target_pos = t.get_entity().pos.load(); + my_pos.squared_distance_to_vec(&target_pos) <= FOLLOW_RANGE * FOLLOW_RANGE } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - mob_entity.target.lock().await.clone_from(&self.target); + fn start(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + mob_entity.set_target(self.target.clone()); - if let Some(owner_uuid) = mob.get_owner_uuid() { - let world = mob_entity.living_entity.entity.world.load_full(); - if let Some(owner) = world.get_player_by_uuid(owner_uuid) { - self.last_attack_time = owner.living_entity.last_attack_time.load(Relaxed); - } + if let Some(owner_uuid) = mob.get_owner_uuid() { + let world = mob_entity.living_entity.entity.world.load_full(); + if let Some(owner) = world.get_player_by_uuid(owner_uuid) { + self.last_attack_time = owner.living_entity.last_attack_time.load(Relaxed); } - }) + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.target = None; - *mob.get_mob_entity().target.lock().await = None; - }) + fn stop(&mut self, mob: &dyn Mob) { + self.target = None; + mob.get_mob_entity().set_target(None); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/pathfind_to_raid.rs b/crates/pumpkin/src/entity/ai/goal/pathfind_to_raid.rs index d198ecff8..b027001aa 100644 --- a/crates/pumpkin/src/entity/ai/goal/pathfind_to_raid.rs +++ b/crates/pumpkin/src/entity/ai/goal/pathfind_to_raid.rs @@ -2,7 +2,7 @@ use std::sync::atomic::AtomicI32; use pumpkin_util::math::vector3::Vector3; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; @@ -28,177 +28,180 @@ impl PathfindToRaidGoal { } impl Goal for PathfindToRaidGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; - let target = mob.get_mob_entity().target.lock().await.clone(); - if target.is_some() || !raider.has_active_raid() { - return false; - } + let target = mob.get_mob_entity().get_target().clone(); + if target.is_some() || !raider.has_active_raid() { + return false; + } - let Some(raid_id) = raider.get_raider_data().raid_id.load() else { - return false; - }; + let Some(raid_id) = raider.get_raider_data().raid_id.load() else { + return false; + }; - let world = mob.get_entity().world.load(); - let raids = world.raids.lock().await; + let pos = mob.get_entity().block_pos.load(); + let world = mob.get_entity().world.load(); + let is_village = { + let raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let Some(raid) = raids.get(raid_id) else { return false; }; - if raid.is_over() { return false; } - - let pos = mob.get_entity().block_pos.load(); - let is_village = world + world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .get_nearest_job_site(pos, 32) - .is_some(); + .is_some() + }; - !is_village - }) + !is_village } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; - let target = mob.get_mob_entity().target.lock().await.clone(); - if target.is_some() || !raider.has_active_raid() { - return false; - } + let target = mob.get_mob_entity().get_target().clone(); + if target.is_some() || !raider.has_active_raid() { + return false; + } - let Some(raid_id) = raider.get_raider_data().raid_id.load() else { - return false; - }; + let Some(raid_id) = raider.get_raider_data().raid_id.load() else { + return false; + }; - let world = mob.get_entity().world.load(); - let raids = world.raids.lock().await; + let pos = mob.get_entity().block_pos.load(); + let world = mob.get_entity().world.load(); + let is_village = { + let raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let Some(raid) = raids.get(raid_id) else { return false; }; - if raid.is_over() { return false; } - - let pos = mob.get_entity().block_pos.load(); - let is_village = world + world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .get_nearest_job_site(pos, 32) - .is_some(); + .is_some() + }; - !is_village - }) + !is_village } fn controls(&self) -> Controls { Controls::MOVE } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return; - }; + fn tick(&mut self, mob: &dyn Mob) { + let Some(raider) = mob.as_raider() else { + return; + }; - let Some(raid_id) = raider.get_raider_data().raid_id.load() else { - return; - }; + let Some(raid_id) = raider.get_raider_data().raid_id.load() else { + return; + }; - let entity = mob.get_entity(); - let world = entity.world.load(); - let current_age = entity.age.load(std::sync::atomic::Ordering::Relaxed); + let entity = mob.get_entity(); + let world = entity.world.load(); + let current_age = entity.age.load(std::sync::atomic::Ordering::Relaxed); - let raid_center = { - let raids = world.raids.lock().await; - let Some(raid) = raids.get(raid_id) else { - return; - }; - if raid.is_over() { - return; - } - raid.center - }; - - // Periodic recruitment of nearby raiders - let next_recruit = self - .recruitment_tick - .load(std::sync::atomic::Ordering::Relaxed); - if current_age >= next_recruit { - self.recruitment_tick - .store(current_age + 20, std::sync::atomic::Ordering::Relaxed); - - let bb = entity.bounding_box.load().expand(16.0, 16.0, 16.0); - let nearby = world.get_entities_at_box(&bb); - - for cand in nearby { - if cand.get_entity().entity_id != entity.entity_id - && let Some(cand_mob) = cand.get_mob() - && let Some(cand_raider) = cand_mob.as_raider() - && !cand_raider.has_active_raid() - && cand_raider.can_join_raid() - { - cand_raider.get_raider_data().raid_id.store(Some(raid_id)); - } - } - } - - // Pathfind towards raid center if idle - let pos = entity.pos.load(); - let mut nav = mob - .get_mob_entity() - .navigator + let raid_center = { + let raids = world + .raids .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - - if nav.is_idle() { - // Generate a point towards the raid center - let center_vec = Vector3::new( - f64::from(raid_center.0.x) + 0.5, - f64::from(raid_center.0.y), - f64::from(raid_center.0.z) + 0.5, - ); - let dir = center_vec - pos; - let dir_len = dir.x.hypot(dir.z); - - let step_dist = 15.0f64.min(dir_len); - let norm_dir_x = if dir_len > 0.001 { - dir.x / dir_len - } else { - 0.0 - }; - let norm_dir_z = if dir_len > 0.001 { - dir.z / dir_len - } else { - 0.0 - }; - - // Add slight random offset (-45 to +45 degrees) - let angle = (rand::random::() - 0.5) * std::f64::consts::FRAC_PI_2; - let cos_a = angle.cos(); - let sin_a = angle.sin(); - let rx = norm_dir_x * cos_a - norm_dir_z * sin_a; - let rz = norm_dir_x * sin_a + norm_dir_z * cos_a; - - let dest = Vector3::new(pos.x + rx * step_dist, pos.y, pos.z + rz * step_dist); - - nav.set_progress(NavigatorGoal { - current_progress: pos, - destination: dest, - speed: self.speed_modifier, - }); + let Some(raid) = raids.get(raid_id) else { + return; + }; + if raid.is_over() { + return; } - }) + raid.center + }; + + // Periodic recruitment of nearby raiders + let next_recruit = self + .recruitment_tick + .load(std::sync::atomic::Ordering::Relaxed); + if current_age >= next_recruit { + self.recruitment_tick + .store(current_age + 20, std::sync::atomic::Ordering::Relaxed); + + let bb = entity.bounding_box.load().expand(16.0, 16.0, 16.0); + let nearby = world.get_entities_at_box(&bb); + + for cand in nearby { + if cand.get_entity().entity_id != entity.entity_id + && let Some(cand_mob) = cand.get_mob() + && let Some(cand_raider) = cand_mob.as_raider() + && !cand_raider.has_active_raid() + && cand_raider.can_join_raid() + { + cand_raider.get_raider_data().raid_id.store(Some(raid_id)); + } + } + } + + // Pathfind towards raid center if idle + let pos = entity.pos.load(); + let mut nav = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + if nav.is_idle() { + // Generate a point towards the raid center + let center_vec = Vector3::new( + f64::from(raid_center.0.x) + 0.5, + f64::from(raid_center.0.y), + f64::from(raid_center.0.z) + 0.5, + ); + let dir = center_vec - pos; + let dir_len = dir.x.hypot(dir.z); + + let step_dist = 15.0f64.min(dir_len); + let norm_dir_x = if dir_len > 0.001 { + dir.x / dir_len + } else { + 0.0 + }; + let norm_dir_z = if dir_len > 0.001 { + dir.z / dir_len + } else { + 0.0 + }; + + // Add slight random offset (-45 to +45 degrees) + let angle = (rand::random::() - 0.5) * std::f64::consts::FRAC_PI_2; + let cos_a = angle.cos(); + let sin_a = angle.sin(); + let rx = norm_dir_x * cos_a - norm_dir_z * sin_a; + let rz = norm_dir_x * sin_a + norm_dir_z * cos_a; + + let dest = Vector3::new(pos.x + rx * step_dist, pos.y, pos.z + rz * step_dist); + + nav.set_progress(NavigatorGoal { + current_progress: pos, + destination: dest, + speed: self.speed_modifier, + }); + } } } diff --git a/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs b/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs index e79f36fa9..b8388a1cc 100644 --- a/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs +++ b/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::{Goal, GoalFuture, to_goal_ticks}; +use super::{Goal, to_goal_ticks}; use crate::entity::mob::Mob; use crate::entity::mob::enderman::EndermanEntity; use pumpkin_data::BlockStateId; @@ -20,90 +20,81 @@ impl PickUpBlockGoal { } impl Goal for PickUpBlockGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if self.enderman.get_carried_block().is_some() { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if self.enderman.get_carried_block().is_some() { + return false; + } - let entity = &mob.get_mob_entity().living_entity.entity; - let world = entity.world.load(); - if !world.level_info.load().game_rules.mob_griefing { - return false; - } + let entity = &mob.get_mob_entity().living_entity.entity; + let world = entity.world.load(); + if !world.level_info.load().game_rules.mob_griefing { + return false; + } - if mob.get_random().random_range(0..to_goal_ticks(20)) != 0 { - return false; - } + if mob.get_random().random_range(0..to_goal_ticks(20)) != 0 { + return false; + } - true - }) + true } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let entity = &mob.get_mob_entity().living_entity.entity; - let pos = entity.pos.load(); + fn tick(&mut self, mob: &dyn Mob) { + let entity = &mob.get_mob_entity().living_entity.entity; + let pos = entity.pos.load(); - let (bx, by, bz) = { - let mut rng = mob.get_random(); - ( - pos.x.floor() as i32 + rng.random_range(-2..=2), - pos.y.floor() as i32 + rng.random_range(0..=2), - pos.z.floor() as i32 + rng.random_range(-2..=2), - ) - }; + let (bx, by, bz) = { + let mut rng = mob.get_random(); + ( + pos.x.floor() as i32 + rng.random_range(-2..=2), + pos.y.floor() as i32 + rng.random_range(0..=2), + pos.z.floor() as i32 + rng.random_range(-2..=2), + ) + }; - let world = entity.world.load(); - let target_pos = BlockPos::new(bx, by, bz); + let world = entity.world.load_full(); + let target_pos = BlockPos::new(bx, by, bz); - let block = world.get_block(&target_pos); + let block = world.get_block(&target_pos); - if !block.has_tag(&tag::Block::MINECRAFT_ENDERMAN_HOLDABLE) { - return; - } + if !block.has_tag(&tag::Block::MINECRAFT_ENDERMAN_HOLDABLE) { + return; + } - let enderman_block = entity.block_pos.load(); - let enderman_center = Vector3::new( - enderman_block.0.x as f64 + 0.5, - by as f64 + 0.5, - enderman_block.0.z as f64 + 0.5, - ); - let block_center = Vector3::new(bx as f64 + 0.5, by as f64 + 0.5, bz as f64 + 0.5); - if let Some((hit_pos, _)) = world - .raycast(enderman_center, block_center, async |block_pos, w| { - let state = w.get_block_state(block_pos); - state.is_solid() - }) - .await - && hit_pos != target_pos - { - return; - } + let enderman_block = entity.block_pos.load(); + let enderman_center = Vector3::new( + enderman_block.0.x as f64 + 0.5, + by as f64 + 0.5, + enderman_block.0.z as f64 + 0.5, + ); + let block_center = Vector3::new(bx as f64 + 0.5, by as f64 + 0.5, bz as f64 + 0.5); + if let Some((hit_pos, _)) = world.raycast(enderman_center, block_center, |block_pos, w| { + let state = w.get_block_state(block_pos); + state.is_solid() + }) && hit_pos != target_pos + { + return; + } - let default_state_id = block.default_state.id; - - let mut event = crate::plugin::api::events::entity::entity_change_block::EntityChangeBlockEvent::new( + let default_state_id = block.default_state.id; + let mut event = + crate::plugin::api::events::entity::entity_change_block::EntityChangeBlockEvent::new( entity.entity_id, target_pos, "minecraft:air".to_string(), ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return; - } + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + if event.cancelled { + return; + } - // TODO: Emit game event (BLOCK_DESTROY) - world - .set_block_state(&target_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; - self.enderman.set_carried_block(Some(default_state_id)); - }) + // TODO: Emit game event (BLOCK_DESTROY) + world.set_block_state(&target_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); + self.enderman.set_carried_block(Some(default_state_id)); } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { false }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + false } } diff --git a/crates/pumpkin/src/entity/ai/goal/place_block.rs b/crates/pumpkin/src/entity/ai/goal/place_block.rs index f47834977..bc6b8841e 100644 --- a/crates/pumpkin/src/entity/ai/goal/place_block.rs +++ b/crates/pumpkin/src/entity/ai/goal/place_block.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::{Goal, GoalFuture, to_goal_ticks}; +use super::{Goal, to_goal_ticks}; use crate::entity::mob::Mob; use crate::entity::mob::enderman::EndermanEntity; use pumpkin_data::Block; @@ -20,70 +20,62 @@ impl PlaceBlockGoal { } impl Goal for PlaceBlockGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if self.enderman.get_carried_block().is_none() { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if self.enderman.get_carried_block().is_none() { + return false; + } - let entity = &mob.get_mob_entity().living_entity.entity; - let world = entity.world.load(); - if !world.level_info.load().game_rules.mob_griefing { - return false; - } + let entity = &mob.get_mob_entity().living_entity.entity; + let world = entity.world.load(); + if !world.level_info.load().game_rules.mob_griefing { + return false; + } - if mob.get_random().random_range(0..to_goal_ticks(2000)) != 0 { - return false; - } + if mob.get_random().random_range(0..to_goal_ticks(2000)) != 0 { + return false; + } - true - }) + true } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(block_state_id) = self.enderman.get_carried_block() else { - return; - }; + fn tick(&mut self, mob: &dyn Mob) { + let Some(block_state_id) = self.enderman.get_carried_block() else { + return; + }; - let entity = &mob.get_mob_entity().living_entity.entity; - let pos = entity.pos.load(); + let entity = &mob.get_mob_entity().living_entity.entity; + let pos = entity.pos.load(); - let (bx, by, bz) = { - let mut rng = mob.get_random(); - ( - pos.x.floor() as i32 + rng.random_range(-1..=1), - pos.y.floor() as i32 + rng.random_range(0..=2), - pos.z.floor() as i32 + rng.random_range(-1..=1), - ) - }; + let (bx, by, bz) = { + let mut rng = mob.get_random(); + ( + pos.x.floor() as i32 + rng.random_range(-1..=1), + pos.y.floor() as i32 + rng.random_range(0..=2), + pos.z.floor() as i32 + rng.random_range(-1..=1), + ) + }; - let world = entity.world.load(); - let target_pos = BlockPos::new(bx, by, bz); + let world = entity.world.load(); + let target_pos = BlockPos::new(bx, by, bz); - let state_id = world.get_block_state_id(&target_pos); - if !is_air(state_id) { - return; - } + let state_id = world.get_block_state_id(&target_pos); + if !is_air(state_id) { + return; + } - let below_pos = BlockPos::new(bx, by - 1, bz); - let (below_block, below_state) = world.get_block_and_state(&below_pos); - if !below_state.is_solid() - || !below_state.is_full_cube() - || below_block == &Block::BEDROCK - { - return; - } + let below_pos = BlockPos::new(bx, by - 1, bz); + let (below_block, below_state) = world.get_block_and_state(&below_pos); + if !below_state.is_solid() || !below_state.is_full_cube() || below_block == &Block::BEDROCK + { + return; + } - // TODO: Validate canPlaceAt and check entity collisions at target position - world - .set_block_state(&target_pos, block_state_id, BlockFlags::NOTIFY_ALL) - .await; - self.enderman.set_carried_block(None); - }) + let world_clone = entity.world.load_full(); + world_clone.set_block_state(&target_pos, block_state_id, BlockFlags::NOTIFY_ALL); + self.enderman.set_carried_block(None); } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { false }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + false } } diff --git a/crates/pumpkin/src/entity/ai/goal/ranged_attack.rs b/crates/pumpkin/src/entity/ai/goal/ranged_attack.rs index 659924457..93a4ec618 100644 --- a/crates/pumpkin/src/entity/ai/goal/ranged_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/ranged_attack.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Weak}; use crate::entity::EntityBase; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::{Mob, RangedAttackMob}; @@ -61,45 +61,39 @@ impl RangedAttackGoal { } impl Goal for RangedAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - if let Some(target) = target - && target.get_entity().is_alive() - { - self.target = Some(target); - true - } else { - false - } - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + if let Some(target) = target + && target.get_entity().is_alive() + { + self.target = Some(target); + true + } else { + false + } } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if let Some(target) = &self.target { - if !target.get_entity().is_alive() { - return false; - } - let current_target = mob.get_mob_entity().target.lock().await.clone(); - current_target.is_some() - } else { - false + fn should_continue(&self, mob: &dyn Mob) -> bool { + if let Some(target) = &self.target { + if !target.get_entity().is_alive() { + return false; } - }) + let current_target = mob.get_mob_entity().get_target().clone(); + current_target.is_some() + } else { + false + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target = None; - self.see_time = 0; - self.attack_time = -1; - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - }) + fn stop(&mut self, mob: &dyn Mob) { + self.target = None; + self.see_time = 0; + self.attack_time = -1; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); } fn should_run_every_tick(&self) -> bool { @@ -110,69 +104,69 @@ impl Goal for RangedAttackGoal { Controls::MOVE | Controls::LOOK } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(target) = self.target.clone() else { - return; - }; + fn tick(&mut self, mob: &dyn Mob) { + let Some(target) = self.target.clone() else { + return; + }; - let Some(ranged_mob) = self.mob.upgrade() else { - return; - }; + if self.mob.upgrade().is_none() { + return; + } - let mob_pos = mob.get_entity().pos.load(); - let target_pos = target.get_entity().pos.load(); - let target_dist_sq = mob_pos.squared_distance_to_vec(&target_pos); + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let target_dist_sq = mob_pos.squared_distance_to_vec(&target_pos); - let has_line_of_sight = true; - if has_line_of_sight { - self.see_time += 1; - } else { - self.see_time = 0; - } + let has_line_of_sight = true; + if has_line_of_sight { + self.see_time += 1; + } else { + self.see_time = 0; + } - { - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if target_dist_sq <= self.attack_radius_sqr && self.see_time >= 5 { - navigator.stop(); - } else { - navigator.set_progress(NavigatorGoal { - current_progress: mob_pos, - destination: target_pos, - speed: self.speed_modifier, - }); - } - } - - mob.get_mob_entity() - .look_control + { + let mut navigator = mob + .get_mob_entity() + .navigator .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity_with_range(&target, 30.0, 30.0); - - self.attack_time -= 1; - if self.attack_time == 0 { - if !has_line_of_sight { - return; - } - - let dist = (target_dist_sq.sqrt() as f32) / self.attack_radius; - let power = dist.clamp(0.1, 1.0); - ranged_mob.perform_ranged_attack(&target, power).await; - - let min = self.attack_interval_min as f32; - let max = self.attack_interval_max as f32; - self.attack_time = (dist.mul_add(max - min, min)).floor() as i32; - } else if self.attack_time < 0 { - let ratio = (target_dist_sq.sqrt() as f32) / self.attack_radius; - let min = self.attack_interval_min as f32; - let max = self.attack_interval_max as f32; - self.attack_time = (ratio.mul_add(max - min, min)).floor() as i32; + .unwrap_or_else(std::sync::PoisonError::into_inner); + if target_dist_sq <= self.attack_radius_sqr && self.see_time >= 5 { + navigator.stop(); + } else { + navigator.set_progress(NavigatorGoal { + current_progress: mob_pos, + destination: target_pos, + speed: self.speed_modifier, + }); } - }) + } + + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity_with_range(&target, 30.0, 30.0); + + self.attack_time -= 1; + if self.attack_time == 0 { + if !has_line_of_sight { + return; + } + + let dist = (target_dist_sq.sqrt() as f32) / self.attack_radius; + let power = dist.clamp(0.1, 1.0); + if let Some(ranged) = self.mob.upgrade() { + ranged.perform_ranged_attack(&target, power); + } + + let min = self.attack_interval_min as f32; + let max = self.attack_interval_max as f32; + self.attack_time = (dist.mul_add(max - min, min)).floor() as i32; + } else if self.attack_time < 0 { + let ratio = (target_dist_sq.sqrt() as f32) / self.attack_radius; + let min = self.attack_interval_min as f32; + let max = self.attack_interval_max as f32; + self.attack_time = (ratio.mul_add(max - min, min)).floor() as i32; + } } } diff --git a/crates/pumpkin/src/entity/ai/goal/ranged_crossbow_attack.rs b/crates/pumpkin/src/entity/ai/goal/ranged_crossbow_attack.rs index 3eb9827a2..c1b4ac9b5 100644 --- a/crates/pumpkin/src/entity/ai/goal/ranged_crossbow_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/ranged_crossbow_attack.rs @@ -7,7 +7,7 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::Hand; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::projectile::arrow::{ArrowEntity, ArrowPickup}; @@ -54,33 +54,22 @@ impl RangedCrossbowAttackGoal { } } - async fn is_holding_crossbow(mob: &dyn Mob) -> bool { - let equipment = mob + fn is_holding_crossbow(mob: &dyn Mob) -> bool { + let equipment_guard = mob .get_mob_entity() .living_entity .entity_equipment - .lock() - .await; - equipment.get(&EquipmentSlot::MAIN_HAND).item.id == Item::CROSSBOW.id - || equipment.get(&EquipmentSlot::OFF_HAND).item.id == Item::CROSSBOW.id + .try_lock(); + equipment_guard.is_ok_and(|equipment| { + equipment.get(&EquipmentSlot::MAIN_HAND).item.id == Item::CROSSBOW.id + || equipment.get(&EquipmentSlot::OFF_HAND).item.id == Item::CROSSBOW.id + }) } - async fn shoot(mob: &dyn Mob, target: &Arc) { + fn shoot(mob: &dyn Mob, target: &Arc) { let entity = mob.get_entity(); let world = entity.world.load(); - - let mut event = - crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent::new( - entity.entity_id, - "minecraft:crossbow".to_string(), - 1.0, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - if event.cancelled { - return; - } + let world_full = entity.world.load_full(); let mob_pos = entity.pos.load(); let target_entity = target.get_entity(); @@ -110,7 +99,20 @@ impl RangedCrossbowAttackGoal { world.play_sound(Sound::ItemCrossbowShoot, SoundCategory::Hostile, &mob_pos); let arrow: Arc = Arc::new(arrow); - world.spawn_entity(arrow).await; + let entity_id = entity.entity_id; + if let Some(server) = world_full.server.upgrade() { + let mut event = + crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent::new( + entity_id, + "minecraft:crossbow".to_string(), + 1.0, + ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + return; + } + } + world_full.spawn_entity(arrow); if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { crossbow_mob.on_crossbow_attack_performed(); @@ -119,173 +121,166 @@ impl RangedCrossbowAttackGoal { } impl Goal for RangedCrossbowAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } - Self::is_holding_crossbow(mob).await - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } + Self::is_holding_crossbow(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return false; - }; - target.get_entity().is_alive() && Self::is_holding_crossbow(mob).await - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return false; + }; + target.get_entity().is_alive() && Self::is_holding_crossbow(mob) } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.state = CrossbowState::Uncharged; + fn start(&mut self, _mob: &dyn Mob) { + self.state = CrossbowState::Uncharged; + self.see_time = 0; + self.attack_delay = 0; + self.update_path_delay = 0; + self.charge_ticks = 0; + } + + fn stop(&mut self, mob: &dyn Mob) { + if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { + crossbow_mob.set_charging_crossbow(false); + } + mob.get_mob_entity().living_entity.clear_active_hand(); + self.state = CrossbowState::Uncharged; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); + } + + #[expect(clippy::too_many_lines)] + fn tick(&mut self, mob: &dyn Mob) { + let target = mob.get_mob_entity().get_target().clone(); + let Some(target) = target else { + return; + }; + + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let distance_sq = mob_pos.squared_distance_to_vec(&target_pos); + + let has_line_of_sight = true; // In future: raycast check + if has_line_of_sight { + self.see_time += 1; + } else { self.see_time = 0; - self.attack_delay = 0; - self.update_path_delay = 0; - self.charge_ticks = 0; - }) - } + } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { - crossbow_mob.set_charging_crossbow(false); + let needs_to_move = + (distance_sq > self.squared_range || self.see_time < 5) && self.attack_delay == 0; + + if needs_to_move { + self.update_path_delay -= 1; + if self.update_path_delay <= 0 { + let move_speed = if self.state == CrossbowState::Uncharged { + self.speed + } else { + self.speed * 0.5 + }; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_progress(NavigatorGoal { + current_progress: mob_pos, + destination: target_pos, + speed: move_speed, + }); + self.update_path_delay = 20 + rand::random_range(0..20); } - mob.get_mob_entity().living_entity.clear_active_hand().await; - self.state = CrossbowState::Uncharged; + } else { + self.update_path_delay = 0; mob.get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .stop(); - }) - } + } - #[expect(clippy::too_many_lines)] - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - let Some(target) = target else { - return; - }; + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity_with_range(&target, 30.0, 30.0); - let mob_pos = mob.get_entity().pos.load(); - let target_pos = target.get_entity().pos.load(); - let distance_sq = mob_pos.squared_distance_to_vec(&target_pos); - - let has_line_of_sight = true; // In future: raycast check - if has_line_of_sight { - self.see_time += 1; - } else { - self.see_time = 0; - } - - let needs_to_move = - (distance_sq > self.squared_range || self.see_time < 5) && self.attack_delay == 0; - - if needs_to_move { - self.update_path_delay -= 1; - if self.update_path_delay <= 0 { - let move_speed = if self.state == CrossbowState::Uncharged { - self.speed - } else { - self.speed * 0.5 - }; - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_progress(NavigatorGoal { - current_progress: mob_pos, - destination: target_pos, - speed: move_speed, - }); - self.update_path_delay = 20 + rand::random_range(0..20); - } - } else { - self.update_path_delay = 0; - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - } - - mob.get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity_with_range(&target, 30.0, 30.0); - - match self.state { - CrossbowState::Uncharged => { - if !needs_to_move { - let stack = mob - .get_mob_entity() - .living_entity - .entity_equipment - .lock() - .await - .get(&EquipmentSlot::MAIN_HAND); - mob.get_mob_entity() - .living_entity - .set_active_hand(Hand::Right, stack, i32::MAX) - .await; - self.state = CrossbowState::Charging; - self.charge_ticks = 0; - if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { - crossbow_mob.set_charging_crossbow(true); - } - mob.get_entity().world.load().play_sound( - Sound::ItemCrossbowLoadingStart, - SoundCategory::Hostile, - &mob_pos, + match self.state { + CrossbowState::Uncharged => { + if !needs_to_move { + let stack = mob + .get_mob_entity() + .living_entity + .entity_equipment + .try_lock() + .map_or_else( + |_| ItemStack::EMPTY.clone(), + |eq| eq.get(&EquipmentSlot::MAIN_HAND), ); + mob.get_mob_entity().living_entity.set_active_hand( + Hand::Right, + stack, + i32::MAX, + ); + self.state = CrossbowState::Charging; + self.charge_ticks = 0; + if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { + crossbow_mob.set_charging_crossbow(true); } - } - CrossbowState::Charging => { - self.charge_ticks += 1; - if self.charge_ticks == 10 { - mob.get_entity().world.load().play_sound( - Sound::ItemCrossbowLoadingMiddle, - SoundCategory::Hostile, - &mob_pos, - ); - } - if self.charge_ticks >= Self::CHARGE_DURATION { - mob.get_mob_entity().living_entity.clear_active_hand().await; - self.state = CrossbowState::Charged; - self.attack_delay = 20 + rand::random_range(0..20); - if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { - crossbow_mob.set_charging_crossbow(false); - } - mob.get_entity().world.load().play_sound( - Sound::ItemCrossbowLoadingEnd, - SoundCategory::Hostile, - &mob_pos, - ); - } - } - CrossbowState::Charged => { - self.attack_delay -= 1; - if self.attack_delay <= 0 { - self.state = CrossbowState::ReadyToAttack; - } - } - CrossbowState::ReadyToAttack => { - if has_line_of_sight { - Self::shoot(mob, &target).await; - self.state = CrossbowState::Uncharged; - } + mob.get_entity().world.load().play_sound( + Sound::ItemCrossbowLoadingStart, + SoundCategory::Hostile, + &mob_pos, + ); } } - }) + CrossbowState::Charging => { + self.charge_ticks += 1; + if self.charge_ticks == 10 { + mob.get_entity().world.load().play_sound( + Sound::ItemCrossbowLoadingMiddle, + SoundCategory::Hostile, + &mob_pos, + ); + } + if self.charge_ticks >= Self::CHARGE_DURATION { + mob.get_mob_entity().living_entity.clear_active_hand(); + self.state = CrossbowState::Charged; + self.attack_delay = 20 + rand::random_range(0..20); + if let Some(crossbow_mob) = mob.as_crossbow_attack_mob() { + crossbow_mob.set_charging_crossbow(false); + } + mob.get_entity().world.load().play_sound( + Sound::ItemCrossbowLoadingEnd, + SoundCategory::Hostile, + &mob_pos, + ); + } + } + CrossbowState::Charged => { + self.attack_delay -= 1; + if self.attack_delay <= 0 { + self.state = CrossbowState::ReadyToAttack; + } + } + CrossbowState::ReadyToAttack => { + if has_line_of_sight { + Self::shoot(mob, &target); + self.state = CrossbowState::Uncharged; + } + } + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/revenge.rs b/crates/pumpkin/src/entity/ai/goal/revenge.rs index 3fae4a8f3..7419bc5fd 100644 --- a/crates/pumpkin/src/entity/ai/goal/revenge.rs +++ b/crates/pumpkin/src/entity/ai/goal/revenge.rs @@ -3,7 +3,7 @@ use std::sync::atomic::Ordering::Relaxed; use super::{Controls, Goal}; use crate::entity::EntityBase; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::goal::track_target::TrackTargetGoal; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::mob::Mob; @@ -31,65 +31,58 @@ impl RevengeGoal { } impl Goal for RevengeGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - let living = &mob_entity.living_entity; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + let living = &mob_entity.living_entity; - let attacked_time = living.last_attacked_time.load(Relaxed); - if attacked_time == self.last_attacked_time { - return false; - } + let attacked_time = living.last_attacked_time.load(Relaxed); + if attacked_time == self.last_attacked_time { + return false; + } - let attacker_id = living.last_attacker_id.load(Relaxed); - if attacker_id == 0 { - return false; - } + let attacker_id = living.last_attacker_id.load(Relaxed); + if attacker_id == 0 { + return false; + } - let world = living.entity.world.load(); - let Some(attacker) = world.get_entity_by_id(attacker_id) else { - return false; - }; + let world = living.entity.world.load(); + let Some(attacker) = world.get_entity_by_id(attacker_id) else { + return false; + }; - let Some(attacker_living) = attacker.get_living_entity() else { - return false; - }; + let Some(attacker_living) = attacker.get_living_entity() else { + return false; + }; - if !self - .target_predicate - .test(&world, Some(&mob_entity.living_entity), attacker_living) - .await - { - return false; - } + if !self + .target_predicate + .test(&world, Some(&mob_entity.living_entity), attacker_living) + { + return false; + } - self.target = Some(attacker); - true - }) + self.target = Some(attacker); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.track_target_goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.track_target_goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - mob.set_mob_target(self.target.clone()).await; + fn start(&mut self, mob: &dyn Mob) { + mob.set_mob_target(self.target.clone()); - let mob_entity = mob.get_mob_entity(); - self.last_attacked_time = mob_entity.living_entity.last_attacked_time.load(Relaxed); - self.track_target_goal.max_time_without_visibility = 300; + let mob_entity = mob.get_mob_entity(); + self.last_attacked_time = mob_entity.living_entity.last_attacked_time.load(Relaxed); + self.track_target_goal.max_time_without_visibility = 300; - self.track_target_goal.start(mob).await; - // TODO: group revenge — call nearby mobs of same type to help - }) + self.track_target_goal.start(mob); + // TODO: group revenge — call nearby mobs of same type to help } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.target = None; - self.track_target_goal.stop(mob).await; - }) + fn stop(&mut self, mob: &dyn Mob) { + self.target = None; + self.track_target_goal.stop(mob); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/step_and_destroy_block.rs b/crates/pumpkin/src/entity/ai/goal/step_and_destroy_block.rs index bdb667dd9..f77c151d7 100644 --- a/crates/pumpkin/src/entity/ai/goal/step_and_destroy_block.rs +++ b/crates/pumpkin/src/entity/ai/goal/step_and_destroy_block.rs @@ -1,12 +1,11 @@ use super::{Controls, Goal, to_goal_ticks}; +use crate::entity::ai::goal::ParentHandle; use crate::entity::ai::goal::move_to_target_pos::{MoveToTargetPos, MoveToTargetPosGoal}; -use crate::entity::ai::goal::{GoalFuture, ParentHandle}; use crate::entity::mob::Mob; use crate::world::World; use pumpkin_data::Block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; -use std::pin::Pin; use std::sync::Arc; const MAX_COOLDOWN: i32 = 20; @@ -72,108 +71,91 @@ impl StepAndDestroyBlockGoal { } } -pub type SteppingFuture<'a> = Pin + Send + 'a>>; - pub trait Stepping: Send + Sync { - fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async {}) - } + fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) {} - fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async {}) - } + fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) {} } impl Goal for StepAndDestroyBlockGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let world = mob.get_entity().world.load(); - let level_info = world.level_info.load(); - if !level_info.game_rules.mob_griefing { - false - } else if self.move_to_target_pos_goal.cooldown > 0 { - self.move_to_target_pos_goal.cooldown -= 1; - false - } else if self.move_to_target_pos_goal.find_target_pos(mob).await { - self.move_to_target_pos_goal.cooldown = to_goal_ticks(MAX_COOLDOWN); - true - } else { - self.move_to_target_pos_goal.cooldown = MoveToTargetPosGoal::::get_interval(mob); - false - } - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let world = mob.get_entity().world.load(); + let level_info = world.level_info.load(); + if !level_info.game_rules.mob_griefing { + false + } else if self.move_to_target_pos_goal.cooldown > 0 { + self.move_to_target_pos_goal.cooldown -= 1; + false + } else if self.move_to_target_pos_goal.find_target_pos(mob) { + self.move_to_target_pos_goal.cooldown = to_goal_ticks(MAX_COOLDOWN); + true + } else { + self.move_to_target_pos_goal.cooldown = MoveToTargetPosGoal::::get_interval(mob); + false + } } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.move_to_target_pos_goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.move_to_target_pos_goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.move_to_target_pos_goal.start(mob).await; - self.counter = 0; - }) + fn start(&mut self, mob: &dyn Mob) { + self.move_to_target_pos_goal.start(mob); + self.counter = 0; } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - // Assuming fall_distance is AtomicF32/f32 - mob.get_mob_entity().living_entity.fall_distance.store(1.0); - }) + fn stop(&mut self, mob: &dyn Mob) { + // Assuming fall_distance is AtomicF32/f32 + mob.get_mob_entity().living_entity.fall_distance.store(1.0); } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.move_to_target_pos_goal.tick(mob).await; - let mob_entity = mob.get_mob_entity(); - let world = mob.get_entity().world.load_full(); - let block_pos = mob.get_entity().block_pos.load(); + fn tick(&mut self, mob: &dyn Mob) { + self.move_to_target_pos_goal.tick(mob); + let mob_entity = mob.get_mob_entity(); + let world = mob.get_entity().world.load_full(); + let block_pos = mob.get_entity().block_pos.load(); - let Some(tweak_pos) = self.tweak_to_proper_pos(block_pos, &world) else { - return; - }; - if !self.move_to_target_pos_goal.reached { - return; - } - let counter = self.counter; + let Some(tweak_pos) = self.tweak_to_proper_pos(block_pos, &world) else { + return; + }; + if !self.move_to_target_pos_goal.reached { + return; + } + let counter = self.counter; - if counter > 0 { - let velocity = mob_entity.living_entity.entity.velocity.load(); - mob_entity - .living_entity - .entity - .set_velocity(Vector3::new(velocity.x, 0.3, velocity.z)); - // TODO: spawn particles - } + if counter > 0 { + let velocity = mob_entity.living_entity.entity.velocity.load(); + mob_entity + .living_entity + .entity + .set_velocity(Vector3::new(velocity.x, 0.3, velocity.z)); + // TODO: spawn particles + } - if counter % 2 == 0 { - let velocity = mob_entity.living_entity.entity.velocity.load(); - mob_entity - .living_entity - .entity - .set_velocity(Vector3::new(velocity.x, -0.3, velocity.z)); - if counter % 6 == 0 { - if let Some(stepping) = self.stepping.get() { - stepping - .tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) - .await; - } else { - self.tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) - .await; - } + if counter % 2 == 0 { + let velocity = mob_entity.living_entity.entity.velocity.load(); + mob_entity + .living_entity + .entity + .set_velocity(Vector3::new(velocity.x, -0.3, velocity.z)); + if counter % 6 == 0 { + if let Some(stepping) = self.stepping.get() { + stepping.tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos); + } else { + self.tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos); } } + } - if counter > 60 { - // TODO: world.removeBlock HOW? - // TODO: spawn particles - self.on_destroy_block(world.clone(), tweak_pos).await; - } + if counter > 60 { + // TODO: world.removeBlock HOW? + // TODO: spawn particles + self.on_destroy_block(world, tweak_pos); + } - self.counter += 1; - }) + self.counter += 1; } fn should_run_every_tick(&self) -> bool { @@ -188,27 +170,17 @@ impl Goal impl MoveToTargetPos for StepAndDestroyBlockGoal { - fn is_target_pos<'a>( - &'a self, - world: Arc, - block_pos: BlockPos, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - world.get_block(&block_pos).id == self.target_block.id - && world.get_block_state(&block_pos.up()).is_air() - && world.get_block_state(&block_pos.up_height(2)).is_air() - }) + fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool { + world.get_block(&block_pos).id == self.target_block.id + && world.get_block_state(&block_pos.up()).is_air() + && world.get_block_state(&block_pos.up_height(2)).is_air() } } impl Stepping for StepAndDestroyBlockGoal { - fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async {}) - } + fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) {} - fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { - Box::pin(async {}) - } + fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) {} } diff --git a/crates/pumpkin/src/entity/ai/goal/swim.rs b/crates/pumpkin/src/entity/ai/goal/swim.rs index a2c362153..c88b6f56d 100644 --- a/crates/pumpkin/src/entity/ai/goal/swim.rs +++ b/crates/pumpkin/src/entity/ai/goal/swim.rs @@ -1,6 +1,6 @@ use std::sync::atomic::Ordering; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::mob::Mob; use rand::RngExt; @@ -27,23 +27,21 @@ impl SwimGoal { } impl Goal for SwimGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { Self::is_in_fluid(mob) }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + Self::is_in_fluid(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { Self::is_in_fluid(mob) }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + Self::is_in_fluid(mob) } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if mob.get_random().random::() < 0.8 { - mob.get_mob_entity() - .living_entity - .jumping - .store(true, Ordering::SeqCst); - } - }) + fn tick(&mut self, mob: &dyn Mob) { + if mob.get_random().random::() < 0.8 { + mob.get_mob_entity() + .living_entity + .jumping + .store(true, Ordering::SeqCst); + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/teleport_towards_player.rs b/crates/pumpkin/src/entity/ai/goal/teleport_towards_player.rs index d01ba1594..034d3b222 100644 --- a/crates/pumpkin/src/entity/ai/goal/teleport_towards_player.rs +++ b/crates/pumpkin/src/entity/ai/goal/teleport_towards_player.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use super::track_target::TrackTargetGoal; -use super::{Controls, Goal, GoalFuture, to_goal_ticks}; +use super::{Controls, Goal, to_goal_ticks}; use crate::entity::EntityBase; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::mob::Mob; @@ -41,7 +41,7 @@ impl TeleportTowardsPlayerGoal { } } - async fn find_staring_player(&self) -> Option> { + fn find_staring_player(&self) -> Option> { let entity = &self.enderman.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -58,19 +58,15 @@ impl TeleportTowardsPlayerGoal { } let living = player.get_living_entity()?; - if !self - .target_predicate - .test( - &world, - Some(&self.enderman.mob_entity.living_entity), - living, - ) - .await - { + if !self.target_predicate.test( + &world, + Some(&self.enderman.mob_entity.living_entity), + living, + ) { return None; } - if self.enderman.is_player_staring(&player).await || self.enderman.is_angry() { + if self.enderman.is_player_staring(&player) || self.enderman.is_angry() { return Some(player); } @@ -79,130 +75,120 @@ impl TeleportTowardsPlayerGoal { } impl Goal for TeleportTowardsPlayerGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(player) = self.find_staring_player().await else { + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(player) = self.find_staring_player() else { + return false; + }; + self.target_player = Some(player); + true + } + + fn should_continue(&self, mob: &dyn Mob) -> bool { + if let Some(target) = &self.target_player + && let Some(player) = target.get_player() + { + if !self.enderman.is_player_staring(player) && !self.enderman.is_angry() { return false; - }; - self.target_player = Some(player); + } + let player_pos = player.get_entity().pos.load(); + let mut look_control = mob + .get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + look_control.look_at_with_range( + player_pos.x, + player_pos.y + PLAYER_EYE_HEIGHT, + player_pos.z, + 10.0, + 10.0, + ); true - }) - } - - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if let Some(target) = &self.target_player - && let Some(player) = target.get_player() - { - if !self.enderman.is_player_staring(player).await && !self.enderman.is_angry() { - return false; - } - let player_pos = player.get_entity().pos.load(); - let mut look_control = mob - .get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.look_at_with_range( - player_pos.x, - player_pos.y + PLAYER_EYE_HEIGHT, - player_pos.z, - 10.0, - 10.0, - ); - true - } else if self.target_player.is_some() { - false - } else if let Some(target) = &self.committed_target { - if !target.get_entity().is_alive() { - return false; - } - let mob_entity = mob.get_mob_entity(); - let dist_sq = mob_entity - .living_entity - .entity - .pos - .load() - .squared_distance_to_vec(&target.get_entity().pos.load()); - let follow_range = mob_entity - .living_entity - .get_attribute_value(&Attributes::FOLLOW_RANGE); - if dist_sq > follow_range * follow_range { - return false; - } - let needs_reset = mob_entity.target.lock().await.is_none(); - if needs_reset { - mob.set_mob_target(Some(target.clone())).await; - } - true - } else { - self.track_target_goal.should_continue(mob).await + } else if self.target_player.is_some() { + false + } else if let Some(target) = &self.committed_target { + if !target.get_entity().is_alive() { + return false; } - }) + let mob_entity = mob.get_mob_entity(); + let dist_sq = mob_entity + .living_entity + .entity + .pos + .load() + .squared_distance_to_vec(&target.get_entity().pos.load()); + let follow_range = mob_entity + .living_entity + .get_attribute_value(&Attributes::FOLLOW_RANGE); + if dist_sq > follow_range * follow_range { + return false; + } + let needs_reset = mob_entity.get_target().is_none(); + if needs_reset { + mob.set_mob_target(Some(target.clone())); + } + true + } else { + self.track_target_goal.should_continue(mob) + } } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup = to_goal_ticks(5); + fn start(&mut self, _mob: &dyn Mob) { + self.warmup = to_goal_ticks(5); + self.unseen_ticks = 0; + self.enderman.set_provoked(true); + } + + fn tick(&mut self, mob: &dyn Mob) { + let external_target = mob.get_mob_entity().get_target().clone(); + if external_target.is_none() + && self.target_player.is_none() + && self.committed_target.is_none() + { + return; + } + + if self.target_player.is_some() { + self.warmup -= 1; + if self.warmup <= 0 { + let target = self.target_player.take(); + self.committed_target.clone_from(&target); + self.enderman.set_target(target); + self.track_target_goal.start(mob); + } + return; + } + + let target = self.committed_target.clone().or(external_target); + let Some(target) = target else { + return; + }; + + let entity = &mob.get_mob_entity().living_entity.entity; + let pos = entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = pos.squared_distance_to_vec(&target_pos); + + if let Some(player) = target.get_player() + && self.enderman.is_player_staring(player) + { + if dist_sq < STARE_CLOSE_DISTANCE_SQ { + self.enderman.teleport_randomly(); + } self.unseen_ticks = 0; - self.enderman.set_provoked(true); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let external_target = mob.get_mob_entity().target.lock().await.clone(); - if external_target.is_none() - && self.target_player.is_none() - && self.committed_target.is_none() - { - return; - } - - if self.target_player.is_some() { - self.warmup -= 1; - if self.warmup <= 0 { - let target = self.target_player.take(); - self.committed_target.clone_from(&target); - self.enderman.set_target(target).await; - self.track_target_goal.start(mob).await; - } - return; - } - - let target = self.committed_target.clone().or(external_target); - let Some(target) = target else { - return; - }; - - let entity = &mob.get_mob_entity().living_entity.entity; - let pos = entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - let dist_sq = pos.squared_distance_to_vec(&target_pos); - - if let Some(player) = target.get_player() - && self.enderman.is_player_staring(player).await - { - if dist_sq < STARE_CLOSE_DISTANCE_SQ { - self.enderman.teleport_randomly(); - } + } else if dist_sq > TELEPORT_FAR_DISTANCE_SQ { + self.unseen_ticks += 1; + if self.unseen_ticks >= to_goal_ticks(30) { + self.enderman.teleport_towards(target.as_ref()); self.unseen_ticks = 0; - } else if dist_sq > TELEPORT_FAR_DISTANCE_SQ { - self.unseen_ticks += 1; - if self.unseen_ticks >= to_goal_ticks(30) { - self.enderman.teleport_towards(target.as_ref()); - self.unseen_ticks = 0; - } } - }) + } } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target_player = None; - self.committed_target = None; - self.enderman.set_target(None).await; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.target_player = None; + self.committed_target = None; + self.enderman.set_target(None); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/tempt.rs b/crates/pumpkin/src/entity/ai/goal/tempt.rs index 1b9ac040e..192e27325 100644 --- a/crates/pumpkin/src/entity/ai/goal/tempt.rs +++ b/crates/pumpkin/src/entity/ai/goal/tempt.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::EntityBase; use crate::entity::{ai::pathfinder::NavigatorGoal, mob::Mob, player::Player}; use pumpkin_data::item::Item; @@ -32,94 +32,82 @@ impl TemptGoal { stack.item_count > 0 && self.tempt_items.iter().any(|i| i.id == stack.item.id) } - async fn is_holding_tempt_item(&self, player: &Player) -> bool { - let main = player.inventory().held_item().await; + fn is_holding_tempt_item(&self, player: &Player) -> bool { + let main = player.inventory().held_item(); if self.is_tempt_item(&main) { return true; } - let off = player.inventory().off_hand_item().await; + let off = player.inventory().off_hand_item(); self.is_tempt_item(&off) } - async fn find_tempting_player(&self, mob: &dyn Mob) -> Option> { + fn find_tempting_player(&self, mob: &dyn Mob) -> Option> { let mob_entity = mob.get_mob_entity(); let pos = mob_entity.living_entity.entity.pos.load(); let world = mob_entity.living_entity.entity.world.load(); - for player in world.get_nearby_players(pos, TEMPT_RANGE) { - if self.is_holding_tempt_item(&player).await { - return Some(player); - } - } - None + world + .get_nearby_players(pos, TEMPT_RANGE) + .into_iter() + .find(|player| self.is_holding_tempt_item(player)) } - async fn is_player_still_tempting(&self, player: &Player, mob: &dyn Mob) -> bool { + fn is_player_still_tempting(&self, player: &Player, mob: &dyn Mob) -> bool { let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); let player_pos = player.get_entity().pos.load(); if mob_pos.squared_distance_to_vec(&player_pos) > TEMPT_RANGE * TEMPT_RANGE { return false; } - self.is_holding_tempt_item(player).await + self.is_holding_tempt_item(player) } } impl Goal for TemptGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if self.cooldown > 0 { - self.cooldown -= 1; - return false; - } - self.target_player = self.find_tempting_player(mob).await; - self.target_player.is_some() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if self.cooldown > 0 { + self.cooldown -= 1; + return false; + } + self.target_player = self.find_tempting_player(mob); + self.target_player.is_some() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if let Some(player) = &self.target_player { - self.is_player_still_tempting(player, mob).await - } else { - false - } - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.target_player + .as_ref() + .is_some_and(|player| self.is_player_still_tempting(player, mob)) } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(player) = &self.target_player { - let mob_entity = mob.get_mob_entity(); - let player_pos = player.get_entity().pos.load(); + fn tick(&mut self, mob: &dyn Mob) { + if let Some(player) = &self.target_player { + let mob_entity = mob.get_mob_entity(); + let player_pos = player.get_entity().pos.load(); - mob_entity - .look_control + mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at( + mob, + player_pos.x, + player.get_entity().get_eye_y(), + player_pos.z, + ); + + let mob_pos = mob_entity.living_entity.entity.pos.load(); + if mob_pos.squared_distance_to_vec(&player_pos) > STOP_DISTANCE * STOP_DISTANCE { + let mut navigator = mob_entity + .navigator .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at( - mob, - player_pos.x, - player.get_entity().get_eye_y(), - player_pos.z, - ); - - let mob_pos = mob_entity.living_entity.entity.pos.load(); - if mob_pos.squared_distance_to_vec(&player_pos) > STOP_DISTANCE * STOP_DISTANCE { - let mut navigator = mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(mob_pos, player_pos, self.speed)); - } + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.set_progress(NavigatorGoal::new(mob_pos, player_pos, self.speed)); } - }) + } } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target_player = None; - self.cooldown = 100; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.target_player = None; + self.cooldown = 100; } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/track_target.rs b/crates/pumpkin/src/entity/ai/goal/track_target.rs index 2d300ee7f..995de6868 100644 --- a/crates/pumpkin/src/entity/ai/goal/track_target.rs +++ b/crates/pumpkin/src/entity/ai/goal/track_target.rs @@ -1,5 +1,5 @@ use super::{Controls, Goal, to_goal_ticks}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::living::LivingEntity; use crate::entity::mob::Mob; @@ -66,7 +66,7 @@ impl TrackTargetGoal { } /// Equivalent to Vanilla's `canAttack` check inside `TargetGoal` - pub async fn can_track( + pub fn can_track( &self, mob: &dyn Mob, target: Option<&LivingEntity>, @@ -79,10 +79,7 @@ impl TrackTargetGoal { let mob_entity = mob.get_mob_entity(); let world = mob_entity.living_entity.entity.world.load(); - if !target_predicate - .test(&world, Some(&mob_entity.living_entity), target) - .await - { + if !target_predicate.test(&world, Some(&mob_entity.living_entity), target) { return false; } @@ -115,81 +112,71 @@ impl TrackTargetGoal { } impl Goal for TrackTargetGoal { - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let mob_entity = mob.get_mob_entity(); - let target_arc = mob_entity.target.lock().await.clone(); + fn should_continue(&self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + let target_arc = mob_entity.get_target(); - let Some(target_base) = target_arc else { - return false; - }; + let Some(target_base) = target_arc else { + return false; + }; - let Some(target) = target_base.get_living_entity() else { - return false; - }; + let Some(target) = target_base.get_living_entity() else { + return false; + }; - if !target.entity.is_alive() { + if !target.entity.is_alive() { + return false; + } + + if !self.can_track(mob, Some(target), &self.target_predicate) { + return false; + } + + // TODO: Team checks (return false if on the same team) + + let dist_sq = mob_entity + .living_entity + .entity + .pos + .load() + .squared_distance_to_vec(&target.entity.pos.load()); + + // Get follow range attribute value and check if target is within range + let follow_range = mob_entity + .living_entity + .get_attribute_value(&Attributes::FOLLOW_RANGE); + + if dist_sq > follow_range * follow_range { + return false; + } + + if self.check_visibility { + let world = mob_entity.living_entity.entity.world.load(); + let has_line_of_sight = world + .raycast( + mob_entity.living_entity.entity.get_eye_pos(), + target.entity.get_eye_pos(), + |block_pos, world| world.get_block_state(block_pos).is_solid(), + ) + .is_none(); + + if !self.remembers_visible_target(has_line_of_sight) { return false; } + } - if !self - .can_track(mob, Some(target), &self.target_predicate) - .await - { - return false; - } - - // TODO: Team checks (return false if on the same team) - - let dist_sq = mob_entity - .living_entity - .entity - .pos - .load() - .squared_distance_to_vec(&target.entity.pos.load()); - - // Get follow range attribute value and check if target is within range - let follow_range = mob_entity - .living_entity - .get_attribute_value(&Attributes::FOLLOW_RANGE); - - if dist_sq > follow_range * follow_range { - return false; - } - - if self.check_visibility { - let world = mob_entity.living_entity.entity.world.load(); - let has_line_of_sight = world - .raycast( - mob_entity.living_entity.entity.get_eye_pos(), - target.entity.get_eye_pos(), - async |block_pos, world| world.get_block_state(block_pos).is_solid(), - ) - .await - .is_none(); - - if !self.remembers_visible_target(has_line_of_sight) { - return false; - } - } - - mob.set_mob_target(Some(target_base.clone())).await; - true - }) + mob.set_mob_target(Some(target_base.clone())); + true } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.can_navigate_flag.store(UNSET, Ordering::Relaxed); - self.check_can_navigate_cooldown.store(0, Ordering::Relaxed); - self.time_without_visibility.store(0, Ordering::Relaxed); - }) + fn start(&mut self, _mob: &dyn Mob) { + self.can_navigate_flag.store(UNSET, Ordering::Relaxed); + self.check_can_navigate_cooldown.store(0, Ordering::Relaxed); + self.time_without_visibility.store(0, Ordering::Relaxed); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - mob.set_mob_target(None).await; - }) + fn stop(&mut self, mob: &dyn Mob) { + mob.set_mob_target(None); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/trade_with_player.rs b/crates/pumpkin/src/entity/ai/goal/trade_with_player.rs index a5774d14c..9192095e0 100644 --- a/crates/pumpkin/src/entity/ai/goal/trade_with_player.rs +++ b/crates/pumpkin/src/entity/ai/goal/trade_with_player.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use pumpkin_util::math::vector3::Vector3; -use super::{Controls, Goal, GoalFuture, to_goal_ticks}; +use super::{Controls, Goal, to_goal_ticks}; use crate::entity::{EntityBase, ai::pathfinder::NavigatorGoal, mob::Mob, player::Player}; pub struct TradeWithPlayerGoal { @@ -78,47 +78,37 @@ impl TradeWithPlayerGoal { } impl Goal for TradeWithPlayerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - self.player = Self::trading_player_in_range(mob); - self.player.is_some() + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.player = Self::trading_player_in_range(mob); + self.player.is_some() + } + + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(current) = Self::trading_player_in_range(mob) else { + return false; + }; + self.player.as_ref().is_some_and(|player| { + player.get_entity().entity_uuid == current.get_entity().entity_uuid }) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(current) = Self::trading_player_in_range(mob) else { - return false; - }; - self.player.as_ref().is_some_and(|player| { - player.get_entity().entity_uuid == current.get_entity().entity_uuid - }) - }) + fn start(&mut self, mob: &dyn Mob) { + self.update_countdown = 0; + self.follow_player(mob); } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.update_countdown = 0; - self.follow_player(mob); - }) + fn stop(&mut self, mob: &dyn Mob) { + self.player = None; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.player = None; - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.update_countdown -= 1; - self.follow_player(mob); - }) + fn tick(&mut self, mob: &dyn Mob) { + self.update_countdown -= 1; + self.follow_player(mob); } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/goal/try_find_water.rs b/crates/pumpkin/src/entity/ai/goal/try_find_water.rs index a4b3f9c6d..864a4f971 100644 --- a/crates/pumpkin/src/entity/ai/goal/try_find_water.rs +++ b/crates/pumpkin/src/entity/ai/goal/try_find_water.rs @@ -4,7 +4,7 @@ use pumpkin_data::fluid::Fluid; use pumpkin_data::tag::{self, Taggable}; use pumpkin_util::math::position::BlockPos; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::mob::Mob; use crate::world::World; @@ -50,53 +50,49 @@ impl TryFindWaterGoal { } impl Goal for TryFindWaterGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let entity = mob.get_entity(); - if !entity.on_ground.load(Ordering::Relaxed) { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let entity = mob.get_entity(); + if !entity.on_ground.load(Ordering::Relaxed) { + return false; + } - let world = entity.world.load(); - let block_pos = entity.block_pos.load(); - !Self::is_water(&world, &block_pos) - }) + let world = entity.world.load(); + let block_pos = entity.block_pos.load(); + !Self::is_water(&world, &block_pos) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let entity = mob.get_entity(); - let world = entity.world.load(); - let mob_pos = entity.pos.load(); + fn start(&mut self, mob: &dyn Mob) { + let entity = mob.get_entity(); + let world = entity.world.load(); + let mob_pos = entity.pos.load(); - let (min_pos, max_pos) = Self::find_water_range(mob_pos); - let mut water_pos: Option = None; + let (min_pos, max_pos) = Self::find_water_range(mob_pos); + let mut water_pos: Option = None; - 'outer: for x in min_pos.0.x..=max_pos.0.x { - for y in min_pos.0.y..=max_pos.0.y { - for z in min_pos.0.z..=max_pos.0.z { - let pos = BlockPos::new(x, y, z); - if Self::is_water(&world, &pos) { - water_pos = Some(pos); - break 'outer; - } + 'outer: for x in min_pos.0.x..=max_pos.0.x { + for y in min_pos.0.y..=max_pos.0.y { + for z in min_pos.0.z..=max_pos.0.z { + let pos = BlockPos::new(x, y, z); + if Self::is_water(&world, &pos) { + water_pos = Some(pos); + break 'outer; } } } + } - if let Some(pos) = water_pos { - mob.get_mob_entity() - .move_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_wanted_position( - f64::from(pos.0.x), - f64::from(pos.0.y), - f64::from(pos.0.z), - 1.0, - ); - } - }) + if let Some(pos) = water_pos { + mob.get_mob_entity() + .move_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_wanted_position( + f64::from(pos.0.x), + f64::from(pos.0.y), + f64::from(pos.0.z), + 1.0, + ); + } } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/wander_around.rs b/crates/pumpkin/src/entity/ai/goal/wander_around.rs index f073a12cb..cb71a26b1 100644 --- a/crates/pumpkin/src/entity/ai/goal/wander_around.rs +++ b/crates/pumpkin/src/entity/ai/goal/wander_around.rs @@ -1,4 +1,4 @@ -use super::{Controls, Goal, GoalFuture, to_goal_ticks}; +use super::{Controls, Goal, to_goal_ticks}; use crate::entity::{ai::pathfinder::NavigatorGoal, mob::Mob}; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; @@ -38,46 +38,38 @@ impl WanderAroundGoal { } impl Goal for WanderAroundGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - if mob.get_random().random_range(0..self.chance) != 0 { - return false; - } + fn can_start(&mut self, mob: &dyn Mob) -> bool { + if mob.get_random().random_range(0..self.chance) != 0 { + return false; + } - self.target = Some(Self::find_wander_target(mob)); - true - }) + self.target = Some(Self::find_wander_target(mob)); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let navigator = mob + fn should_continue(&self, mob: &dyn Mob) -> bool { + let navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + !navigator.is_idle() + } + + fn start(&mut self, mob: &dyn Mob) { + if let Some(target) = self.target { + let pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let mut navigator = mob .get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - !navigator.is_idle() - }) + navigator.set_progress(NavigatorGoal::new(pos, target, self.speed)); + } } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target) = self.target { - let pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let mut navigator = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - navigator.set_progress(NavigatorGoal::new(pos, target, self.speed)); - } - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.target = None; - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.target = None; } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/work_at_job_site.rs b/crates/pumpkin/src/entity/ai/goal/work_at_job_site.rs index aedc50a1d..df3d2fc9b 100644 --- a/crates/pumpkin/src/entity/ai/goal/work_at_job_site.rs +++ b/crates/pumpkin/src/entity/ai/goal/work_at_job_site.rs @@ -1,6 +1,6 @@ use pumpkin_util::math::position::BlockPos; -use super::{Controls, Goal, GoalFuture}; +use super::{Controls, Goal}; use crate::entity::{ai::pathfinder::NavigatorGoal, mob::Mob}; pub struct WorkAtJobSiteGoal { @@ -17,90 +17,87 @@ impl WorkAtJobSiteGoal { } } - async fn should_move_to_job_site(mob: &dyn Mob) -> bool { - if mob.is_job_site_pending().await { + fn should_move_to_job_site(mob: &dyn Mob) -> bool { + if mob.is_job_site_pending() { return true; } let world = mob.get_mob_entity().living_entity.entity.world.load(); - (2_000..9_000).contains(&world.level_time.lock().await.query_daytime()) + let daytime = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .query_daytime(); + (2_000..9_000).contains(&daytime) } } impl Goal for WorkAtJobSiteGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(target) = mob.get_job_site() else { - return false; - }; - if !Self::should_move_to_job_site(mob).await { - return false; - } - let position = mob.get_mob_entity().living_entity.entity.pos.load(); - if target.to_centered_f64().squared_distance_to_vec(&position) < 1.73f64.powi(2) { - return false; - } - self.target = Some(target); - true - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(target) = mob.get_job_site() else { + return false; + }; + if !Self::should_move_to_job_site(mob) { + return false; + } + let position = mob.get_mob_entity().living_entity.entity.pos.load(); + if target.to_centered_f64().squared_distance_to_vec(&position) < 1.73f64.powi(2) { + return false; + } + self.target = Some(target); + true } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(target) = self.target else { - return false; - }; - if mob.get_job_site() != Some(target) || !Self::should_move_to_job_site(mob).await { - return false; - } + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(target) = self.target else { + return false; + }; + if mob.get_job_site() != Some(target) || !Self::should_move_to_job_site(mob) { + return false; + } + let entity = &mob.get_mob_entity().living_entity.entity; + target + .to_centered_f64() + .squared_distance_to_vec(&entity.pos.load()) + >= 1.73f64.powi(2) + && !mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_idle() + } + + fn start(&mut self, mob: &dyn Mob) { + if let Some(target) = self.target { let entity = &mob.get_mob_entity().living_entity.entity; - target - .to_centered_f64() - .squared_distance_to_vec(&entity.pos.load()) - >= 1.73f64.powi(2) - && !mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .is_idle() - }) - } - - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target) = self.target { - let entity = &mob.get_mob_entity().living_entity.entity; - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_progress(NavigatorGoal::new( - entity.pos.load(), - target.to_centered_f64(), - self.speed, - )); - } - }) - } - - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(target) = self.target - && target - .to_centered_f64() - .squared_distance_to_vec(&mob.get_mob_entity().living_entity.entity.pos.load()) - >= 2.0f64.powi(2) - && mob.is_job_site_pending().await - { - mob.release_pending_job_site(target).await; - } - self.target = None; mob.get_mob_entity() .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - }) + .set_progress(NavigatorGoal::new( + entity.pos.load(), + target.to_centered_f64(), + self.speed, + )); + } + } + + fn stop(&mut self, mob: &dyn Mob) { + if let Some(target) = self.target + && target + .to_centered_f64() + .squared_distance_to_vec(&mob.get_mob_entity().living_entity.entity.pos.load()) + >= 2.0f64.powi(2) + && mob.is_job_site_pending() + { + mob.release_pending_job_site(target); + } + self.target = None; + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/ai/goal/zombie_attack.rs b/crates/pumpkin/src/entity/ai/goal/zombie_attack.rs index 2920ef6cb..b95b73f34 100644 --- a/crates/pumpkin/src/entity/ai/goal/zombie_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/zombie_attack.rs @@ -1,5 +1,5 @@ use super::{Controls, Goal}; -use crate::entity::ai::goal::GoalFuture; + use crate::entity::ai::goal::melee_attack::MeleeAttackGoal; use crate::entity::mob::Mob; @@ -19,40 +19,34 @@ impl ZombieAttackGoal { } impl Goal for ZombieAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.melee_attack_goal.can_start(mob).await }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + self.melee_attack_goal.can_start(mob) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { self.melee_attack_goal.should_continue(mob).await }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + self.melee_attack_goal.should_continue(mob) } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.melee_attack_goal.start(mob).await; - self.ticks = 0; - }) + fn start(&mut self, mob: &dyn Mob) { + self.melee_attack_goal.start(mob); + self.ticks = 0; } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.melee_attack_goal.stop(mob).await; + fn stop(&mut self, mob: &dyn Mob) { + self.melee_attack_goal.stop(mob); + mob.get_mob_entity().set_attacking(false); + } + + fn tick(&mut self, mob: &dyn Mob) { + self.melee_attack_goal.tick(mob); + self.ticks += 1; + if self.ticks >= 5 + && self.melee_attack_goal.cooldown < self.melee_attack_goal.get_max_cooldown() / 2 + { + mob.get_mob_entity().set_attacking(true); + } else { mob.get_mob_entity().set_attacking(false); - }) - } - - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - self.melee_attack_goal.tick(mob).await; - self.ticks += 1; - if self.ticks >= 5 - && self.melee_attack_goal.cooldown < self.melee_attack_goal.get_max_cooldown() / 2 - { - mob.get_mob_entity().set_attacking(true); - } else { - mob.get_mob_entity().set_attacking(false); - } - }) + } } fn should_run_every_tick(&self) -> bool { diff --git a/crates/pumpkin/src/entity/ai/pathfinder/mod.rs b/crates/pumpkin/src/entity/ai/pathfinder/mod.rs index cb8afb1c0..922a4b4ef 100644 --- a/crates/pumpkin/src/entity/ai/pathfinder/mod.rs +++ b/crates/pumpkin/src/entity/ai/pathfinder/mod.rs @@ -136,23 +136,18 @@ impl Navigator { self.mob_height = height; } - pub async fn can_reach_within( + pub fn can_reach_within( &mut self, entity: &LivingEntity, destination: Vector3, distance: f32, ) -> bool { self.compute_path(entity, destination) - .await .is_some_and(|path| path.can_reach() || path.get_dist_to_target() <= distance) } #[allow(clippy::too_many_lines)] - async fn compute_path( - &mut self, - entity: &LivingEntity, - destination: Vector3, - ) -> Option { + fn compute_path(&mut self, entity: &LivingEntity, destination: Vector3) -> Option { let start_pos_f = entity.entity.pos.load(); let start_block_vec = start_pos_f.to_i32(); let mob_position = Vector3::new(start_block_vec.x, start_block_vec.y, start_block_vec.z); @@ -173,7 +168,7 @@ impl Navigator { self.evaluator.prepare(context, mob_data); - let mut start_node = self.evaluator.get_start().await?; + let mut start_node = self.evaluator.get_start()?; let mut target = self.evaluator.get_target(destination.to_block_pos()); @@ -230,8 +225,7 @@ impl Navigator { self.neighbors_buf.clear(); self.evaluator - .get_neighbors(¤t, &mut self.neighbors_buf) - .await; + .get_neighbors(¤t, &mut self.neighbors_buf); for mut neighbor in self.neighbors_buf.drain(..) { let step_cost = current.distance(&neighbor); @@ -321,7 +315,7 @@ impl Navigator { } #[allow(clippy::too_many_lines)] - pub async fn tick(&mut self, entity: &LivingEntity) { + pub fn tick(&mut self, entity: &LivingEntity) { let Some(goal) = self.current_goal.take() else { // Idle: stop the mob self.is_idle.store(true, Ordering::Relaxed); @@ -342,7 +336,7 @@ impl Navigator { } if self.needs_new_path(&goal) { - self.current_path = self.compute_path(entity, goal.destination).await; + self.current_path = self.compute_path(entity, goal.destination); self.ticks_on_current_node = 0; self.last_node_index = 0; self.path_start_pos = Some(entity.entity.pos.load()); diff --git a/crates/pumpkin/src/entity/ai/pathfinder/node_evaluator.rs b/crates/pumpkin/src/entity/ai/pathfinder/node_evaluator.rs index 8fd87098e..3fc563238 100644 --- a/crates/pumpkin/src/entity/ai/pathfinder/node_evaluator.rs +++ b/crates/pumpkin/src/entity/ai/pathfinder/node_evaluator.rs @@ -10,24 +10,16 @@ use crate::entity::ai::pathfinder::{ pub trait NodeEvaluator { fn prepare(&mut self, context: PathfindingContext, mob_data: MobData); fn done(&mut self); - fn get_start(&mut self) -> impl std::future::Future> + Send; + fn get_start(&mut self) -> Option; fn get_target(&mut self, pos: BlockPos) -> Target; - fn get_neighbors( - &mut self, - current: &Node, - out: &mut Vec, - ) -> impl std::future::Future + Send; + fn get_neighbors(&mut self, current: &Node, out: &mut Vec); fn get_path_type_of_mob( &mut self, context: &mut PathfindingContext, pos: Vector3, mob_data: &MobData, - ) -> impl std::future::Future + Send; - fn get_path_type( - &mut self, - context: &mut PathfindingContext, - pos: Vector3, - ) -> impl std::future::Future + Send; + ) -> PathType; + fn get_path_type(&mut self, context: &mut PathfindingContext, pos: Vector3) -> PathType; fn set_can_pass_doors(&mut self, can_pass: bool); fn set_can_open_doors(&mut self, can_open: bool); fn set_can_float(&mut self, can_float: bool); diff --git a/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs b/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs index b4c00b310..e52ba791e 100644 --- a/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs +++ b/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs @@ -88,7 +88,8 @@ impl WalkNodeEvaluator { } /// Returns the best path node for the given position, handling step-ups, falls, and blocked nodes. - async fn find_accepted_node( + /// Returns the best path node for the given position, handling step-ups, falls, and blocked nodes. + fn find_accepted_node( &mut self, pos: Vector3, max_y_step: i32, @@ -101,7 +102,7 @@ impl WalkNodeEvaluator { return None; } - let path_type = self.get_cached_path_type(pos).await; + let path_type = self.get_cached_path_type(pos); let penalty = self.get_mob_penalty(path_type); let mut node = (penalty >= 0.0).then(|| { @@ -123,17 +124,21 @@ impl WalkNodeEvaluator { && path_type != PathType::Trapdoor && path_type != PathType::PowderSnow { - let jump_node = self - .get_jump_on_top_node(pos, max_y_step, last_feet_y, facing, current_path_type) - .await; + let jump_node = self.get_jump_on_top_node( + pos, + max_y_step, + last_feet_y, + facing, + current_path_type, + ); if jump_node.is_some() { node = jump_node; } } else if !self.is_amphibious() && path_type == PathType::Water && !self.base.can_float { - node = self.get_non_water_node_below(pos, node).await; + node = self.get_non_water_node_below(pos, node); } else if path_type == PathType::Open { - node = Some(self.get_open_node(pos).await); + node = Some(self.get_open_node(pos)); } else if Self::is_blocked_type(path_type) && node.is_none() { let mut n = self.base.get_node(pos.as_blockpos()); n.closed = true; @@ -147,7 +152,7 @@ impl WalkNodeEvaluator { } /// Tries stepping up one block at a time (up to `max_y_step`). - async fn get_jump_on_top_node( + fn get_jump_on_top_node( &mut self, pos: Vector3, max_y_step: i32, @@ -164,7 +169,7 @@ impl WalkNodeEvaluator { return None; } - let path_type = self.get_cached_path_type(step_pos).await; + let path_type = self.get_cached_path_type(step_pos); let penalty = self.get_mob_penalty(path_type); if penalty >= 0.0 @@ -187,7 +192,7 @@ impl WalkNodeEvaluator { } if path_type == PathType::Open { - return Some(self.get_open_node(step_pos).await); + return Some(self.get_open_node(step_pos)); } return None; @@ -197,7 +202,7 @@ impl WalkNodeEvaluator { } /// Searches downward for the first non-`OPEN` block, respecting safe fall distance. - async fn get_open_node(&mut self, pos: Vector3) -> Node { + fn get_open_node(&mut self, pos: Vector3) -> Node { let safe_fall_distance = self .base .mob_data @@ -216,9 +221,7 @@ impl WalkNodeEvaluator { return n; } - let path_type = self - .get_cached_path_type(Vector3::new(pos.x, check_y, pos.z)) - .await; + let path_type = self.get_cached_path_type(Vector3::new(pos.x, check_y, pos.z)); let penalty = self.get_mob_penalty(path_type); if path_type != PathType::Open { @@ -243,16 +246,14 @@ impl WalkNodeEvaluator { n } - async fn get_non_water_node_below( + fn get_non_water_node_below( &mut self, pos: Vector3, mut node: Option, ) -> Option { let mut y = pos.y - 1; while y > pos.y - 16 { - let path_type = self - .get_cached_path_type(Vector3::new(pos.x, y, pos.z)) - .await; + let path_type = self.get_cached_path_type(Vector3::new(pos.x, y, pos.z)); if path_type != PathType::Water { return node; } @@ -282,17 +283,17 @@ impl WalkNodeEvaluator { ) } - async fn get_cached_path_type(&mut self, pos: Vector3) -> PathType { + fn get_cached_path_type(&mut self, pos: Vector3) -> PathType { if let Some(&cached) = self.path_types_cache.get(&pos) { return cached; } // Temporarily take the context out to avoid overlapping borrows when calling - // the async helper which requires `&mut self` + // the helper which requires `&mut self` let path_type = if let Some(mut ctx) = self.base.context.take() && let Some(mob_data) = self.base.mob_data { - let res = self.get_path_type_of_mob(&mut ctx, pos, &mob_data).await; + let res = self.get_path_type_of_mob(&mut ctx, pos, &mob_data); self.base.context = Some(ctx); res } else { @@ -310,18 +311,18 @@ impl WalkNodeEvaluator { .is_some_and(|ctx| ctx.has_collisions(center)) } - async fn can_start_at(&mut self, pos: Vector3) -> bool { - let path_type = self.get_cached_path_type(pos).await; + fn can_start_at(&mut self, pos: Vector3) -> bool { + let path_type = self.get_cached_path_type(pos); path_type.is_passable() && !self.has_collisions(pos) } - async fn get_start_node(&mut self, pos: Vector3) -> Option { - if !self.can_start_at(pos).await { + fn get_start_node(&mut self, pos: Vector3) -> Option { + if !self.can_start_at(pos) { return None; } let mut node = self.base.get_node(pos.as_blockpos()); - let path_type = self.get_cached_path_type(pos).await; + let path_type = self.get_cached_path_type(pos); node.path_type = path_type; node.cost_malus = self.get_mob_penalty(path_type); @@ -346,7 +347,7 @@ impl NodeEvaluator for WalkNodeEvaluator { self.path_types_cache.clear(); } - async fn get_start(&mut self) -> Option { + fn get_start(&mut self) -> Option { let mob_data = self.base.mob_data.as_ref()?; let mob_x = mob_data.position.x; let mob_y_f64 = mob_data.position.y; @@ -361,13 +362,11 @@ impl NodeEvaluator for WalkNodeEvaluator { let bottom_y = start_y - 64; let mut found_y = start_y; for check_y in (bottom_y..start_y).rev() { - let path_type = self - .get_cached_path_type(Vector3::new( - mob_x.floor() as i32, - check_y, - mob_z.floor() as i32, - )) - .await; + let path_type = self.get_cached_path_type(Vector3::new( + mob_x.floor() as i32, + check_y, + mob_z.floor() as i32, + )); if path_type != PathType::Open && path_type != PathType::Water { found_y = check_y + 1; break; @@ -380,19 +379,19 @@ impl NodeEvaluator for WalkNodeEvaluator { let block_z = mob_z.floor() as i32; let start_pos = Vector3::new(block_x, y, block_z); - if let Some(node) = self.get_start_node(start_pos).await { + if let Some(node) = self.get_start_node(start_pos) { return Some(node); } for &(dx, dz) in &DIRECTIONS { let try_pos = Vector3::new(block_x + dx, y, block_z + dz); - if let Some(node) = self.get_start_node(try_pos).await { + if let Some(node) = self.get_start_node(try_pos) { return Some(node); } } let above_pos = Vector3::new(block_x, y + 1, block_z); - self.get_start_node(above_pos).await + self.get_start_node(above_pos) } fn get_target(&mut self, pos: BlockPos) -> Target { @@ -400,11 +399,9 @@ impl NodeEvaluator for WalkNodeEvaluator { Target::new(node) } - async fn get_neighbors(&mut self, current: &Node, out_neighbors: &mut Vec) { - let headroom_type = self - .get_cached_path_type(current.pos.0.add_raw(0, 1, 0)) - .await; - let current_type = self.get_cached_path_type(current.pos.0).await; + fn get_neighbors(&mut self, current: &Node, out_neighbors: &mut Vec) { + let headroom_type = self.get_cached_path_type(current.pos.0.add_raw(0, 1, 0)); + let current_type = self.get_cached_path_type(current.pos.0); let headroom_penalty = self.get_mob_penalty(headroom_type); let max_y_step = if headroom_penalty >= 0.0 && current_type != PathType::StickyHoney { @@ -422,15 +419,13 @@ impl NodeEvaluator for WalkNodeEvaluator { for (i, &(dx, dz)) in DIRECTIONS.iter().enumerate() { let neighbor_pos = current.pos.0.add_raw(dx, 0, dz); - let neighbor_opt = self - .find_accepted_node( - neighbor_pos, - max_y_step, - floor_level, - (dx, dz), - current.path_type, - ) - .await; + let neighbor_opt = self.find_accepted_node( + neighbor_pos, + max_y_step, + floor_level, + (dx, dz), + current.path_type, + ); if let Some(neighbor) = neighbor_opt { self.reusable_neighbors[i] = Some(neighbor); @@ -457,15 +452,13 @@ impl NodeEvaluator for WalkNodeEvaluator { ) { let diagonal_pos = current.pos.0.add_raw(dx, 0, dz); - let diagonal_opt = self - .find_accepted_node( - diagonal_pos, - max_y_step, - floor_level, - (dx, dz), - current.path_type, - ) - .await; + let diagonal_opt = self.find_accepted_node( + diagonal_pos, + max_y_step, + floor_level, + (dx, dz), + current.path_type, + ); if let Some(diagonal) = diagonal_opt && Self::is_diagonal_node_valid(Some(&diagonal)) @@ -476,8 +469,7 @@ impl NodeEvaluator for WalkNodeEvaluator { } } - #[allow(clippy::unused_async_trait_impl)] - async fn get_path_type_of_mob( + fn get_path_type_of_mob( &mut self, context: &mut PathfindingContext, pos: Vector3, @@ -557,12 +549,7 @@ impl NodeEvaluator for WalkNodeEvaluator { result } - #[allow(clippy::unused_async_trait_impl)] - async fn get_path_type( - &mut self, - context: &mut PathfindingContext, - pos: Vector3, - ) -> PathType { + fn get_path_type(&mut self, context: &mut PathfindingContext, pos: Vector3) -> PathType { context.get_path_type_from_state(pos) } diff --git a/crates/pumpkin/src/entity/ai/target_predicate.rs b/crates/pumpkin/src/entity/ai/target_predicate.rs index fa69f6141..25f5ded07 100644 --- a/crates/pumpkin/src/entity/ai/target_predicate.rs +++ b/crates/pumpkin/src/entity/ai/target_predicate.rs @@ -2,15 +2,11 @@ use pumpkin_util::Difficulty; use crate::entity::living::LivingEntity; use crate::world::World; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; const MIN_DISTANCE: f64 = 2.0; -pub type PredicateFn = dyn Fn(Arc, Arc) -> Pin + Send>> - + Send - + Sync; +pub type PredicateFn = dyn Fn(&LivingEntity, &World) -> bool + Send + Sync; pub struct TargetPredicate { pub attackable: bool, @@ -79,19 +75,14 @@ impl TargetPredicate { self } - pub fn set_predicate(&mut self, predicate: F) + pub fn set_predicate(&mut self, predicate: F) where - F: Fn(Arc, Arc) -> Fut + Send + Sync + 'static, - Fut: Future + Send + 'static, + F: Fn(&LivingEntity, &World) -> bool + Send + Sync + 'static, { - self.predicate = Some(Arc::new( - move |living_entity: Arc, world: Arc| { - Box::pin(predicate(living_entity, world)) - }, - )); + self.predicate = Some(Arc::new(predicate)); } - pub async fn test( + pub fn test( &self, world: &World, tester: Option<&LivingEntity>, @@ -137,9 +128,8 @@ impl TargetPredicate { .raycast( tester_ent.entity.get_eye_pos(), target.entity.get_eye_pos(), - async |block_pos, world| world.get_block_state(block_pos).is_solid(), + |block_pos, world| world.get_block_state(block_pos).is_solid(), ) - .await .is_some() { return false; diff --git a/crates/pumpkin/src/entity/area_effect_cloud.rs b/crates/pumpkin/src/entity/area_effect_cloud.rs index 7b0445362..3da01ae62 100644 --- a/crates/pumpkin/src/entity/area_effect_cloud.rs +++ b/crates/pumpkin/src/entity/area_effect_cloud.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture}, + entity::{Entity, EntityBase}, server::Server, }; use pumpkin_data::effect::StatusEffect; @@ -11,7 +11,6 @@ use pumpkin_util::math::vector3::Vector3; type EffectEntry = (&'static StatusEffect, i32, u8, bool, bool, bool); use pumpkin_data::item_stack::ItemStack; -use tokio::sync::Mutex; struct ParticleMeta<'a> { particle_id: pumpkin_protocol::codec::var_int::VarInt, @@ -57,6 +56,9 @@ pub struct AreaEffectCloudEntity { impl AreaEffectCloudEntity { #[allow(clippy::new_ret_no_self)] pub fn new(entity: Entity) -> Arc { + entity + .no_physics + .store(true, std::sync::atomic::Ordering::Relaxed); let cloud = Self { entity, item_stack: Mutex::new(ItemStack::new(0, &pumpkin_data::item::Item::GLASS_BOTTLE)), @@ -87,6 +89,9 @@ impl AreaEffectCloudEntity { radius_on_use_in: f32, duration_on_use_in: i32, ) -> Arc { + entity + .no_physics + .store(true, std::sync::atomic::Ordering::Relaxed); let cloud = Self { entity, item_stack: Mutex::new(item_stack), @@ -107,39 +112,33 @@ impl AreaEffectCloudEntity { } impl EntityBase for AreaEffectCloudEntity { - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - // Send initial radius and particle (color) so clients render correctly - let radius = *self.radius.lock().await; + fn init_data_tracker(&self) { + // Send initial radius and particle (color) so clients render correctly + let radius = *self + .radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // Compute particle color - let stack = self.item_stack.lock().await.clone(); - let effects = self.effects.lock().await.clone(); + // Compute particle color + let stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let effects = self + .effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); - // Use ARGB format - let mut color: i32 = (0xFFi32 << 24) | 0x385dc6; // default water-like color + // Use ARGB format + let mut color: i32 = (0xFFi32 << 24) | 0x385dc6; // default water-like color - if let Some(pc) = - stack.get_data_component::() - { - if let Some(c) = pc.custom_color { - color = c | (0xFFi32 << 24); - } else if !effects.is_empty() { - let mut r_sum = 0.0f32; - let mut g_sum = 0.0f32; - let mut b_sum = 0.0f32; - let count = effects.len() as f32; - for (eff, _, _, _, _, _) in &effects { - let c = eff.color; - r_sum += ((c >> 16) & 0xFF) as f32; - g_sum += ((c >> 8) & 0xFF) as f32; - b_sum += (c & 0xFF) as f32; - } - let r = (r_sum / count) as i32; - let g = (g_sum / count) as i32; - let b = (b_sum / count) as i32; - color = (0xFFi32 << 24) | (r << 16) | (g << 8) | b; - } + if let Some(pc) = + stack.get_data_component::() + { + if let Some(c) = pc.custom_color { + color = c | (0xFFi32 << 24); } else if !effects.is_empty() { let mut r_sum = 0.0f32; let mut g_sum = 0.0f32; @@ -156,100 +155,284 @@ impl EntityBase for AreaEffectCloudEntity { let b = (b_sum / count) as i32; color = (0xFFi32 << 24) | (r << 16) | (g << 8) | b; } + } else if !effects.is_empty() { + let mut r_sum = 0.0f32; + let mut g_sum = 0.0f32; + let mut b_sum = 0.0f32; + let count = effects.len() as f32; + for (eff, _, _, _, _, _) in &effects { + let c = eff.color; + r_sum += ((c >> 16) & 0xFF) as f32; + g_sum += ((c >> 8) & 0xFF) as f32; + b_sum += (c & 0xFF) as f32; + } + let r = (r_sum / count) as i32; + let g = (g_sum / count) as i32; + let b = (b_sum / count) as i32; + color = (0xFFi32 << 24) | (r << 16) | (g << 8) | b; + } - // Build raw particle option bytes for ENTITY_EFFECT - let data_bytes = color.to_be_bytes(); + // Build raw particle option bytes for ENTITY_EFFECT + let data_bytes = color.to_be_bytes(); - let meta = ParticleMeta { - particle_id: pumpkin_protocol::codec::var_int::VarInt( - pumpkin_data::particle::Particle::EntityEffect as i32, - ), - data: &data_bytes, - }; + let meta = ParticleMeta { + particle_id: pumpkin_protocol::codec::var_int::VarInt( + pumpkin_data::particle::Particle::EntityEffect as i32, + ), + data: &data_bytes, + }; - // Send initial particle and radius - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::PARTICLE, - &meta, - )], - None, - ); + // Send initial particle and radius + self.entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::area_effect_cloud::PARTICLE, + &meta, + )], + None, + ); - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::RADIUS, - radius, - )], - None, - ); + self.entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::area_effect_cloud::RADIUS, + radius, + )], + None, + ); - // Initial waiting flag - let wait_time = *self.wait_time.lock().await; - let is_waiting = 0 < wait_time; - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::WAITING, - is_waiting, - )], - None, - ); - }) + // Initial waiting flag + let wait_time = *self + .wait_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let is_waiting = 0 < wait_time; + self.entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::area_effect_cloud::WAITING, + is_waiting, + )], + None, + ); } #[allow(clippy::too_many_lines)] #[allow(clippy::semicolon_outside_block)] - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Age & duration handling - { - let mut age = self.age.lock().await; - *age += 1; - let duration = *self.duration.lock().await; - if *age > duration { - // Remove old entities - self.entity.remove().await; - return; - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { + // Age & duration handling + { + let mut age = self + .age + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *age += 1; + let duration = *self + .duration + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if *age > duration { + // Remove old entities + self.entity.remove(); + return; } + } - // Get current age and waiting period - let age = *self.age.lock().await; - let wait_time = *self.wait_time.lock().await; + // Get current age and waiting period + let age = *self + .age + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let wait_time = *self + .wait_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // When the waiting period ends, notify clients so they render full particles - if age == wait_time && wait_time > 0 { - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::WAITING, - false, - )], - None, - ); - } + // When the waiting period ends, notify clients so they render full particles + if age == wait_time && wait_time > 0 { + self.entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::area_effect_cloud::WAITING, + false, + )], + None, + ); + } - if age < wait_time { - // Respect waiting/grace period + if age < wait_time { + // Respect waiting/grace period + return; + } + + // Update radius + { + let mut radius = self + .radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let delta = *self + .radius_on_tick + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *radius += delta; + let current_radius = *radius; + if current_radius <= 0.0 { + self.entity.remove(); return; } - // Update radius + // Send new radius + drop(radius); + self.entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::area_effect_cloud::RADIUS, + current_radius, + )], + None, + ); + } + + // Tick down reapplication map + { + let mut map = self + .reapplication_map + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + map.retain(|_, v| { + *v -= 1; + *v > 0 + }); + } + + // Apply effects to nearby entities if eligible + let pos = self.entity.pos.load(); + let r = *self + .radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) as f64; + let min = Vector3::new(pos.x - r, pos.y - r, pos.z - r); + let max = Vector3::new(pos.x + r, pos.y + r, pos.z + r); + let aabb = BoundingBox::new(min, max); + let world = self.entity.world.load(); + + let mut candidates = world.get_entities_at_box(&aabb); + let players = world.get_players_at_box(&aabb); + for p in players { + candidates.push(p.clone() as Arc); + } + + for cand in candidates { + let cand_clone = cand.clone(); + + // Skip self and other `AreaEffectCloud` entities + if cand_clone.get_entity().entity_id == self.get_entity().entity_id { + continue; + } + if *cand_clone.get_entity().entity_type + == pumpkin_data::entity::EntityType::AREA_EFFECT_CLOUD { - let mut radius = self.radius.lock().await; - let delta = *self.radius_on_tick.lock().await; - *radius += delta; - let current_radius = *radius; - if current_radius <= 0.0 { - self.entity.remove().await; + continue; + } + + // Determine candidate id early + let ent_id = cand_clone.get_entity().entity_id; + + { + let map = self + .reapplication_map + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if map.contains_key(&ent_id) { + continue; + } + } + + let radius_f = *self + .radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + as f64; + let pos_e = cand_clone.get_entity().pos.load(); + let dx = pos_e.x - pos.x; + let dy = pos_e.y - pos.y; + let dz = pos_e.z - pos.z; + let dist = (dx * dx + dy * dy + dz * dz).sqrt(); + if dist > radius_f { + continue; + } + + let scale = 1.0f32 - (dist as f32 / radius_f as f32); + + // Decide whether this contact will actually apply an effect + let effs_clone = self + .effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut will_apply = false; + + // Only living entities can receive effects + if let Some(living_ref) = cand_clone.get_living_entity() { + for (eff, _, _, _, _, _) in &effs_clone { + // Instant effects always apply + let is_instant = eff.id + == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id + || eff.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id; + if is_instant { + will_apply = true; + break; + } + + // Only apply if entity does not already have that effect + if !living_ref.has_effect(eff) { + will_apply = true; + break; + } + } + } + + // If nothing would be applied, skip + if !will_apply { + continue; + } + + // Apply scaled effects inside a spawned task + if let Some(living) = cand_clone.get_living_entity() { + crate::item::potion::PotionContents::apply_effects_to( + living, + effs_clone, + scale, + crate::item::potion::PotionApplicationSource::AreaEffectCloud, + ); + } + + // Set reapplication delay for this entity + let delay = *self + .reapplication_delay + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + self.reapplication_map + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .insert(ent_id, delay); + + // Apply radius-on-use (shrink) + let radius_on_use = *self + .radius_on_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if radius_on_use != 0.0 { + let mut radius_lock = self + .radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *radius_lock += radius_on_use; + let current_radius = *radius_lock; + if current_radius < 0.5 { + drop(radius_lock); + self.entity.remove(); return; } + drop(radius_lock); - // Send new radius - drop(radius); + // Send updated radius to clients self.entity.send_meta_data( &[pumpkin_protocol::java::client::play::Metadata::new( pumpkin_data::tracked_data::area_effect_cloud::RADIUS, @@ -259,151 +442,26 @@ impl EntityBase for AreaEffectCloudEntity { ); } - // Tick down reapplication map - { - let map = self.reapplication_map.lock().await; - let keys: Vec = map.keys().copied().collect(); - drop(map); - for k in keys { - let mut map = self.reapplication_map.lock().await; - if let Some(v) = map.get_mut(&k) { - *v -= 1; - if *v <= 0 { - map.remove(&k); - } - } - } - } - - // Apply effects to nearby entities if eligible - let pos = self.entity.pos.load(); - let r = *self.radius.lock().await as f64; - let min = Vector3::new(pos.x - r, pos.y - r, pos.z - r); - let max = Vector3::new(pos.x + r, pos.y + r, pos.z + r); - let aabb = BoundingBox::new(min, max); - let world = self.entity.world.load(); - - let mut candidates = world.get_entities_at_box(&aabb); - let players = world.get_players_at_box(&aabb); - for p in players { - candidates.push(p.clone() as Arc); - } - - for cand in candidates { - let cand_clone = cand.clone(); - - // Skip self and other `AreaEffectCloud` entities - if cand_clone.get_entity().entity_id == self.get_entity().entity_id { - continue; - } - if *cand_clone.get_entity().entity_type - == pumpkin_data::entity::EntityType::AREA_EFFECT_CLOUD - { - continue; - } - - // Determine candidate id early - let ent_id = cand_clone.get_entity().entity_id; - - let radius_f = *self.radius.lock().await as f64; - let pos_e = cand_clone.get_entity().pos.load(); - let dx = pos_e.x - pos.x; - let dy = pos_e.y - pos.y; - let dz = pos_e.z - pos.z; - let dist = (dx * dx + dy * dy + dz * dz).sqrt(); - if dist > radius_f { - continue; - } - - let scale = 1.0f32 - (dist as f32 / radius_f as f32); - - // Decide whether this contact will actually apply an effect - let effs_clone = self.effects.lock().await.clone(); - let mut will_apply = false; - - // Only living entities can receive effects - if let Some(living_ref) = cand_clone.get_living_entity() { - for (eff, _, _, _, _, _) in &effs_clone { - // Instant effects always apply - let is_instant = eff.id - == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id - || eff.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id; - if is_instant { - will_apply = true; - break; - } - - // Only apply if entity does not already have that effect - if !living_ref.has_effect(eff).await { - will_apply = true; - break; - } - } - } - - // If nothing would be applied, skip - if !will_apply { - continue; - } - - // Apply scaled effects inside a spawned task - let cand_for_spawn = cand_clone.clone(); - let effs_for_spawn = effs_clone.clone(); - tokio::spawn(async move { - if let Some(living) = cand_for_spawn.get_living_entity() { - crate::item::potion::PotionContents::apply_effects_to( - living, - effs_for_spawn, - scale, - crate::item::potion::PotionApplicationSource::AreaEffectCloud, - ) - .await; - } - }); - - // Set reapplication delay for this entity - let mut map = self.reapplication_map.lock().await; - let delay = *self.reapplication_delay.lock().await; - map.insert(ent_id, delay); - - // Apply radius-on-use (shrink) - let radius_on_use = *self.radius_on_use.lock().await; - if radius_on_use != 0.0 { - let mut radius_lock = self.radius.lock().await; - *radius_lock += radius_on_use; - let current_radius = *radius_lock; - if current_radius < 0.5 { - drop(radius_lock); - self.entity.remove().await; + // Apply duration-on-use (shorten lifespan) + let duration_on_use = *self + .duration_on_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if duration_on_use != 0 { + let mut duration_lock = self + .duration + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if *duration_lock != -1 { + *duration_lock += duration_on_use; + if *duration_lock <= 0 { + drop(duration_lock); + self.entity.remove(); return; } - drop(radius_lock); - - // Send updated radius to clients - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::RADIUS, - current_radius, - )], - None, - ); - } - - // Apply duration-on-use (shorten lifespan) - let duration_on_use = *self.duration_on_use.lock().await; - if duration_on_use != 0 { - let mut duration_lock = self.duration.lock().await; - if *duration_lock != -1 { - *duration_lock += duration_on_use; - if *duration_lock <= 0 { - drop(duration_lock); - self.entity.remove().await; - return; - } - } } } - }) + } } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/attributes.rs b/crates/pumpkin/src/entity/attributes.rs index 864969e34..d94370cc5 100644 --- a/crates/pumpkin/src/entity/attributes.rs +++ b/crates/pumpkin/src/entity/attributes.rs @@ -89,7 +89,7 @@ impl AttributeInstance { } /// Send updates for multiple attributes in a single packet for the given living entity. -pub async fn send_attribute_updates_for_living( +pub fn send_attribute_updates_for_living( living: &crate::entity::living::LivingEntity, attributes: Vec, ) { @@ -176,8 +176,7 @@ pub async fn send_attribute_updates_for_living( .entity .world .load() - .broadcast_editioned(&je_packet, &be_packet) - .await; + .broadcast_editioned(&je_packet, &be_packet); } impl Clone for AttributeInstance { diff --git a/crates/pumpkin/src/entity/boss/ender_dragon.rs b/crates/pumpkin/src/entity/boss/ender_dragon.rs index 90b0568ba..44b0672d0 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon.rs @@ -9,7 +9,7 @@ use std::sync::atomic::Ordering; use tokio::sync::Mutex; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, living::LivingEntity, mob::{Mob, MobEntity}, player::Player, @@ -161,24 +161,17 @@ impl EntityBase for EnderDragonPart { &self.entity } - fn damage<'a>( - &'a self, - source: &'a dyn EntityBase, - amount: f32, - damage_type: DamageType, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let world = self.entity.world.load(); - if let Some(dragon_base) = world - .entities - .load() - .iter() - .find(|e| e.get_entity().entity_uuid == self.dragon_uuid) - { - return dragon_base.damage(source, amount, damage_type).await; - } - false - }) + fn damage(&self, source: &dyn EntityBase, amount: f32, damage_type: DamageType) -> bool { + let world = self.entity.world.load(); + if let Some(dragon_base) = world + .entities + .load() + .iter() + .find(|e| e.get_entity().entity_uuid == self.dragon_uuid) + { + return dragon_base.damage(source, amount, damage_type); + } + false } fn cast_any(&self) -> &dyn std::any::Any { @@ -230,7 +223,7 @@ pub struct EnderDragonEntity { impl EnderDragonEntity { pub fn new(entity: Entity) -> Arc { - entity.no_clip.store(true, Ordering::Relaxed); + entity.no_physics.store(true, Ordering::Relaxed); let base_id = entity.entity_id; let dragon_uuid = entity.entity_uuid; let world = entity.world.load(); @@ -280,10 +273,12 @@ impl EnderDragonEntity { }) } - pub async fn set_fight_origin(&self, pos: BlockPos) { - let mut initialized = self.nodes_initialized.lock().await; - let mut origin = self.fight_origin.lock().await; - if *origin != pos { + pub fn set_fight_origin(&self, pos: BlockPos) { + if let (Ok(mut initialized), Ok(mut origin)) = ( + self.nodes_initialized.try_lock(), + self.fight_origin.try_lock(), + ) && *origin != pos + { *origin = pos; *initialized = false; } @@ -569,7 +564,7 @@ impl EnderDragonEntity { player.get_entity().send_velocity(); if !self.phase.lock().await.is_sitting() { - player.damage(self, 5.0, DamageType::MOB_ATTACK).await; + player.damage(self, 5.0, DamageType::MOB_ATTACK); } } } @@ -622,9 +617,7 @@ impl EnderDragonEntity { && block != &Block::END_PORTAL && block != &Block::END_PORTAL_FRAME { - world - .set_block_state(&pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); } } } @@ -726,11 +719,9 @@ impl EnderDragonEntity { let world = self.mob_entity.living_entity.entity.world.load(); if let Some(ref fight_mutex) = world.dragon_fight { let living = &self.mob_entity.living_entity; - fight_mutex - .lock() - .await - .update_dragon(&world, living.health.load(), living.get_max_health()) - .await; + if let Ok(mut fight) = fight_mutex.lock() { + fight.update_dragon(&world, living.health.load(), living.get_max_health()); + } } } @@ -780,23 +771,33 @@ impl Mob for EnderDragonEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ai_step().await; - }) + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity_id = self.mob_entity.living_entity.entity.entity_id; + let world = self.mob_entity.living_entity.entity.world.load_full(); + tokio::spawn(async move { + let Some(entity) = world.get_entity_by_id(entity_id) else { + return; + }; + let Some(dragon) = entity.cast_any().downcast_ref::() else { + return; + }; + dragon.ai_step().await; + }); } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let living = &self.mob_entity.living_entity; - if living.health.load() <= 0.0 { - self.set_phase(EnderDragonPhase::Dying).await; - } - }) + fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { + let living = &self.mob_entity.living_entity; + if living.health.load() <= 0.0 { + let world = self.mob_entity.living_entity.entity.world.load_full(); + let entity_id = self.mob_entity.living_entity.entity.entity_id; + tokio::spawn(async move { + if let Some(entity) = world.get_entity_by_id(entity_id) + && let Some(dragon) = entity.cast_any().downcast_ref::() + { + dragon.set_phase(EnderDragonPhase::Dying).await; + } + }); + } } fn get_mob_gravity(&self) -> f64 { diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs index 626a53d75..2b018e34d 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs @@ -46,11 +46,13 @@ impl super::Phase for CirclingPhase { drop(clockwise); let world = dragon.mob_entity.living_entity.entity.world.load(); - let crystals_alive = if let Some(ref fight) = world.dragon_fight { - fight.lock().await.alive_crystals() > 0 - } else { - false - }; + let crystals_alive = world.dragon_fight.as_ref().is_some_and(|fight| { + fight + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .alive_crystals() + > 0 + }); let j = if crystals_alive { j.rem_euclid(12) as usize diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs index a2a576595..1afe09e83 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs @@ -54,7 +54,10 @@ impl super::Phase for DyingPhase { } let xp_count = if let Some(ref fight_mutex) = world.dragon_fight - && !fight_mutex.lock().await.has_previously_killed_dragon() + && !fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .has_previously_killed_dragon() { 12000 } else { @@ -66,8 +69,7 @@ impl super::Phase for DyingPhase { &world, entity.pos.load(), (xp_count as f32 * 0.08) as u32, - ) - .await; + ); } entity.velocity.store(Vector3::new(0.0, 0.1, 0.0)); @@ -77,20 +79,18 @@ impl super::Phase for DyingPhase { &world, entity.pos.load(), (xp_count as f32 * 0.2) as u32, - ) - .await; + ); if let Some(ref fight_mutex) = world.dragon_fight { fight_mutex .lock() - .await - .set_dragon_killed(&world, entity.entity_uuid) - .await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_dragon_killed(&world, entity.entity_uuid); } for part in &dragon.parts { - part.entity.remove().await; + part.entity.remove(); } - entity.remove().await; + entity.remove(); } }) } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs index 6d5f9b9f0..f6adca9b8 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs @@ -66,7 +66,7 @@ impl super::Phase for SitBreathingPhase { 0.5, // radius on use -100, // duration on use ); - world.spawn_entity(cloud).await; + world.spawn_entity(cloud); } }) } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs index 64e859c46..ffc735e09 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs @@ -153,7 +153,7 @@ impl super::Phase for StrafingPhase { 0.5, -100, ); - world.spawn_entity(cloud).await; + world.spawn_entity(cloud); dragon.path.lock().await.clear(); dragon.set_phase(EnderDragonPhase::Circling).await; diff --git a/crates/pumpkin/src/entity/boss/wither.rs b/crates/pumpkin/src/entity/boss/wither.rs index 8ef4f60b6..248cbc69c 100644 --- a/crates/pumpkin/src/entity/boss/wither.rs +++ b/crates/pumpkin/src/entity/boss/wither.rs @@ -26,7 +26,7 @@ use pumpkin_world::world::BlockFlags; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, revenge::RevengeGoal, @@ -190,7 +190,7 @@ impl WitherEntity { self.mob_entity.living_entity.entity.pos.load().z + (angle.sin() as f64) * 1.3 } - pub async fn perform_ranged_attack( + pub fn perform_ranged_attack( &self, head: usize, target_x: f64, @@ -229,7 +229,7 @@ impl WitherEntity { dangerous, normalized_dir, )); - world.spawn_entity(skull).await; + world.spawn_entity(skull); } fn make_bossbar(&self) -> Bossbar { @@ -280,7 +280,7 @@ impl WitherEntity { if let Some(p) = players.iter().find(|p| p.gameprofile.id == uid) { let mut bar = self.make_bossbar(); bar.health = progress; - p.send_bossbar(&bar).await; + p.send_bossbar(&bar); } bossbar_players.push(uid); } @@ -294,16 +294,14 @@ impl WitherEntity { for uid in &to_remove { if let Some(p) = players.iter().find(|p| &p.gameprofile.id == uid) { - p.remove_bossbar(self.bossbar_uuid).await; + p.remove_bossbar(self.bossbar_uuid); } bossbar_players.retain(|u| u != uid); } for player in players.iter() { if bossbar_players.contains(&player.gameprofile.id) { - player - .update_bossbar_health(&self.bossbar_uuid, progress) - .await; + player.update_bossbar_health(&self.bossbar_uuid, progress); } } } @@ -313,11 +311,266 @@ impl WitherEntity { let players = world.players.load(); for player in players.iter() { if bossbar_players.contains(&player.gameprofile.id) { - player.remove_bossbar(self.bossbar_uuid).await; + player.remove_bossbar(self.bossbar_uuid); } } bossbar_players.clear(); } + + #[expect(clippy::too_many_lines)] + async fn async_mob_tick(&self) { + let entity = &self.mob_entity.living_entity.entity; + let world = entity.world.load(); + + if world.level_info.load().difficulty == Difficulty::Peaceful { + self.remove_all_bossbar(&world).await; + entity.remove(); + return; + } + + if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { + self.remove_all_bossbar(&world).await; + if !self.dropped_loot.swap(true, Ordering::SeqCst) { + let pos = entity.block_pos.load(); + world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); + } + return; + } + + let invul = self.get_invulnerable_ticks(); + let tick_count = entity.age.load(Ordering::Relaxed); + + if invul > 0 { + let new_count = invul - 1; + let progress = (1.0 - (new_count as f32) / 220.0).clamp(0.0, 1.0); + self.update_bossbar(&world, progress).await; + + if new_count <= 0 { + let pos = entity.pos.load(); + let eye_y = pos.y + entity.get_eye_height(); + world + .explode( + Vector3::new(pos.x, eye_y, pos.z), + 7.0, + ExplosionInteraction::Mob, + ) + .await; + + if !entity.silent.load(Ordering::Relaxed) { + world.sync_world_event( + WorldEvent::SoundWitherBossSpawn, + entity.block_pos.load(), + 0, + ); + } + } + + self.set_invulnerable_ticks(new_count); + if tick_count % 10 == 0 { + self.mob_entity.living_entity.heal(10.0); + } + } else { + let living = &self.mob_entity.living_entity; + let max_health = living.get_max_health(); + let health = living.health.load(); + let progress = if max_health > 0.0 { + (health / max_health).clamp(0.0, 1.0) + } else { + 0.0 + }; + self.update_bossbar(&world, progress).await; + + if tick_count % 20 == 0 { + living.heal(1.0); + } + + // AI step - movement towards main target + let mut delta_movement = entity.velocity.load().multiply(1.0, 0.6, 1.0); + let target_opt = self.mob_entity.get_target(); + + if let Some(ref target) = target_opt { + if target.get_entity().is_alive() { + let target_pos = target.get_entity().pos.load(); + let wither_pos = entity.pos.load(); + let mut yd = delta_movement.y; + + if wither_pos.y < target_pos.y + || (!self.is_powered() && wither_pos.y < target_pos.y + 5.0) + { + yd = yd.max(0.0); + yd += 0.3 - yd * 0.6; + } + + delta_movement.y = yd; + let delta = Vector3::new( + target_pos.x - wither_pos.x, + 0.0, + target_pos.z - wither_pos.z, + ); + + if delta.horizontal_length_squared() > 9.0 { + let scale = delta.normalize(); + delta_movement.x += scale.x * 0.3 - delta_movement.x * 0.6; + delta_movement.z += scale.z * 0.3 - delta_movement.z * 0.6; + } + + self.set_alternative_target(0, target.get_entity().entity_id); + + // Main head attack + let dist_sq = (wither_pos - target_pos).length_squared(); + if dist_sq <= 400.0 { + let attack_timer = self.main_attack_timer.load(Ordering::Relaxed); + if attack_timer <= 0 { + self.main_attack_timer.store(40, Ordering::Relaxed); + let dangerous = rand::random_range(0.0..1.0) < 0.001; + let eye_h = target.get_entity().get_eye_height(); + self.perform_ranged_attack( + 0, + target_pos.x, + target_pos.y + eye_h * 0.5, + target_pos.z, + dangerous, + ); + } else { + self.main_attack_timer + .store(attack_timer - 1, Ordering::Relaxed); + } + } + } else { + self.set_alternative_target(0, 0); + self.mob_entity.set_target(None); + } + } else { + self.set_alternative_target(0, 0); + } + + entity.velocity.store(delta_movement); + if delta_movement.horizontal_length_squared() > 0.05 { + let yaw = (delta_movement.z.atan2(delta_movement.x).to_degrees() as f32) - 90.0; + entity.set_rotation(yaw, entity.pitch.load()); + } + + // Side heads attack logic + let difficulty = world.level_info.load().difficulty; + let wither_pos = entity.pos.load(); + + for i in 1..=2 { + let next_update = self.next_head_update[i - 1].load(Ordering::Relaxed); + if tick_count >= next_update { + let rand_delay = rand::random_range(0..10); + self.next_head_update[i - 1] + .store(tick_count + 10 + rand_delay, Ordering::Relaxed); + + if (difficulty == Difficulty::Normal || difficulty == Difficulty::Hard) + && self.idle_head_updates[i - 1].fetch_add(1, Ordering::Relaxed) > 15 + { + let (xt, yt, zt) = ( + wither_pos.x + rand::random_range(-10.0..10.0), + wither_pos.y + rand::random_range(-5.0..5.0), + wither_pos.z + rand::random_range(-10.0..10.0), + ); + self.perform_ranged_attack(i, xt, yt, zt, true); + self.idle_head_updates[i - 1].store(0, Ordering::Relaxed); + } + + let head_target_id = self.get_alternative_target(i); + if head_target_id > 0 { + let head_target = world.get_entity_by_id(head_target_id); + if let Some(target) = head_target { + let t_pos = target.get_entity().pos.load(); + if target.get_entity().is_alive() + && (wither_pos - t_pos).length_squared() <= 900.0 + { + let eye_h = target.get_entity().get_eye_height(); + self.perform_ranged_attack( + i, + t_pos.x, + t_pos.y + eye_h * 0.5, + t_pos.z, + false, + ); + let next_delay = 40 + rand::random_range(0..20); + self.next_head_update[i - 1] + .store(tick_count + next_delay, Ordering::Relaxed); + self.idle_head_updates[i - 1].store(0, Ordering::Relaxed); + } else { + self.set_alternative_target(i, 0); + } + } else { + self.set_alternative_target(i, 0); + } + } else { + let search_box = entity.bounding_box.load().expand(20.0, 8.0, 20.0); + let entities = world.get_entities_at_box(&search_box); + let candidates: Vec<_> = entities + .into_iter() + .filter(|e| { + e.get_entity().entity_id != entity.entity_id + && e.get_living_entity().is_some() + && e.get_entity().is_alive() + && !e + .get_entity() + .entity_type + .has_tag(&tag::EntityType::MINECRAFT_WITHER_FRIENDS) + }) + .collect(); + + if !candidates.is_empty() { + let idx = rand::random_range(0..candidates.len()); + self.set_alternative_target(i, candidates[idx].get_entity().entity_id); + } + } + } + } + + // Block destruction + let destroy_tick = self.destroy_blocks_tick.load(Ordering::Relaxed); + if destroy_tick > 0 { + let next_destroy = destroy_tick - 1; + self.destroy_blocks_tick + .store(next_destroy, Ordering::Relaxed); + + if next_destroy == 0 && world.level_info.load().game_rules.mob_griefing { + let bb = entity.bounding_box.load(); + let bb_width = bb.max.x - bb.min.x; + let bb_height = bb.max.y - bb.min.y; + let width = (bb_width as f32 / 2.0 + 1.0).floor() as i32; + let height = (bb_height as f32).floor() as i32; + let min_pos = entity.block_pos.load(); + let mut destroyed = false; + + for dx in -width..=width { + for dy in 0..=height { + for dz in -width..=width { + let bpos = BlockPos::new( + min_pos.0.x + dx, + min_pos.0.y + dy, + min_pos.0.z + dz, + ); + let block = world.get_block(&bpos); + if Self::can_destroy(block) { + world.set_block_state( + &bpos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + destroyed = true; + } + } + } + } + + if destroyed && !entity.silent.load(Ordering::Relaxed) { + world.sync_world_event( + WorldEvent::SoundWitherBlockBreak, + entity.block_pos.load(), + 0, + ); + } + } + } + } + } } impl Mob for WitherEntity { @@ -325,367 +578,106 @@ impl Mob for WitherEntity { &self.mob_entity } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[ - Metadata::new( - tracked_data::wither::DATA_TARGET_A, - VarInt(self.get_alternative_target(0)), - ), - Metadata::new( - tracked_data::wither::DATA_TARGET_B, - VarInt(self.get_alternative_target(1)), - ), - Metadata::new( - tracked_data::wither::DATA_TARGET_C, - VarInt(self.get_alternative_target(2)), - ), - Metadata::new( - tracked_data::wither::DATA_ID_INV, - VarInt(self.get_invulnerable_ticks()), - ), - ], - None, - ); - }) + fn mob_init_data_tracker(&self) { + let entity = &self.mob_entity.living_entity.entity; + entity.send_meta_data( + &[ + Metadata::new( + tracked_data::wither::DATA_TARGET_A, + VarInt(self.get_alternative_target(0)), + ), + Metadata::new( + tracked_data::wither::DATA_TARGET_B, + VarInt(self.get_alternative_target(1)), + ), + Metadata::new( + tracked_data::wither::DATA_TARGET_C, + VarInt(self.get_alternative_target(2)), + ), + Metadata::new( + tracked_data::wither::DATA_ID_INV, + VarInt(self.get_invulnerable_ticks()), + ), + ], + None, + ); } - #[expect(clippy::too_many_lines)] - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - let world = entity.world.load(); - - if world.level_info.load().difficulty == Difficulty::Peaceful { - self.remove_all_bossbar(&world).await; - entity.remove().await; + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity_id = self.mob_entity.living_entity.entity.entity_id; + let world = self.mob_entity.living_entity.entity.world.load_full(); + tokio::spawn(async move { + let Some(entity) = world.get_entity_by_id(entity_id) else { return; - } - - if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { - self.remove_all_bossbar(&world).await; - if !self.dropped_loot.swap(true, Ordering::SeqCst) { - let pos = entity.block_pos.load(); - world - .drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)) - .await; - } + }; + let Some(wither) = entity.cast_any().downcast_ref::() else { return; - } - - let invul = self.get_invulnerable_ticks(); - let tick_count = entity.age.load(Ordering::Relaxed); - - if invul > 0 { - let new_count = invul - 1; - let progress = (1.0 - (new_count as f32) / 220.0).clamp(0.0, 1.0); - self.update_bossbar(&world, progress).await; - - if new_count <= 0 { - let pos = entity.pos.load(); - let eye_y = pos.y + entity.get_eye_height(); - world - .explode( - Vector3::new(pos.x, eye_y, pos.z), - 7.0, - ExplosionInteraction::Mob, - ) - .await; - - if !entity.silent.load(Ordering::Relaxed) { - world.sync_world_event( - WorldEvent::SoundWitherBossSpawn, - entity.block_pos.load(), - 0, - ); - } - } - - self.set_invulnerable_ticks(new_count); - if tick_count % 10 == 0 { - self.mob_entity.living_entity.heal(10.0); - } - } else { - let living = &self.mob_entity.living_entity; - let max_health = living.get_max_health(); - let health = living.health.load(); - let progress = if max_health > 0.0 { - (health / max_health).clamp(0.0, 1.0) - } else { - 0.0 - }; - self.update_bossbar(&world, progress).await; - - if tick_count % 20 == 0 { - living.heal(1.0); - } - - // AI step - movement towards main target - let mut delta_movement = entity.velocity.load().multiply(1.0, 0.6, 1.0); - let target_opt = self.mob_entity.get_target().await; - - if let Some(ref target) = target_opt { - if target.get_entity().is_alive() { - let target_pos = target.get_entity().pos.load(); - let wither_pos = entity.pos.load(); - let mut yd = delta_movement.y; - - if wither_pos.y < target_pos.y - || (!self.is_powered() && wither_pos.y < target_pos.y + 5.0) - { - yd = yd.max(0.0); - yd += 0.3 - yd * 0.6; - } - - delta_movement.y = yd; - let delta = Vector3::new( - target_pos.x - wither_pos.x, - 0.0, - target_pos.z - wither_pos.z, - ); - - if delta.horizontal_length_squared() > 9.0 { - let scale = delta.normalize(); - delta_movement.x += scale.x * 0.3 - delta_movement.x * 0.6; - delta_movement.z += scale.z * 0.3 - delta_movement.z * 0.6; - } - - self.set_alternative_target(0, target.get_entity().entity_id); - - // Main head attack - let dist_sq = (wither_pos - target_pos).length_squared(); - if dist_sq <= 400.0 { - let attack_timer = self.main_attack_timer.load(Ordering::Relaxed); - if attack_timer <= 0 { - self.main_attack_timer.store(40, Ordering::Relaxed); - let dangerous = rand::random_range(0.0..1.0) < 0.001; - let eye_h = target.get_entity().get_eye_height(); - self.perform_ranged_attack( - 0, - target_pos.x, - target_pos.y + eye_h * 0.5, - target_pos.z, - dangerous, - ) - .await; - } else { - self.main_attack_timer - .store(attack_timer - 1, Ordering::Relaxed); - } - } - } else { - self.set_alternative_target(0, 0); - self.mob_entity.set_target(None).await; - } - } else { - self.set_alternative_target(0, 0); - } - - entity.velocity.store(delta_movement); - if delta_movement.horizontal_length_squared() > 0.05 { - let yaw = (delta_movement.z.atan2(delta_movement.x).to_degrees() as f32) - 90.0; - entity.set_rotation(yaw, entity.pitch.load()); - } - - // Side heads attack logic - let difficulty = world.level_info.load().difficulty; - let wither_pos = entity.pos.load(); - - for i in 1..=2 { - let next_update = self.next_head_update[i - 1].load(Ordering::Relaxed); - if tick_count >= next_update { - let rand_delay = rand::random_range(0..10); - self.next_head_update[i - 1] - .store(tick_count + 10 + rand_delay, Ordering::Relaxed); - - if (difficulty == Difficulty::Normal || difficulty == Difficulty::Hard) - && self.idle_head_updates[i - 1].fetch_add(1, Ordering::Relaxed) > 15 - { - let (xt, yt, zt) = ( - wither_pos.x + rand::random_range(-10.0..10.0), - wither_pos.y + rand::random_range(-5.0..5.0), - wither_pos.z + rand::random_range(-10.0..10.0), - ); - self.perform_ranged_attack(i, xt, yt, zt, true).await; - self.idle_head_updates[i - 1].store(0, Ordering::Relaxed); - } - - let head_target_id = self.get_alternative_target(i); - if head_target_id > 0 { - let head_target = world.get_entity_by_id(head_target_id); - if let Some(target) = head_target { - let t_pos = target.get_entity().pos.load(); - if target.get_entity().is_alive() - && (wither_pos - t_pos).length_squared() <= 900.0 - { - let eye_h = target.get_entity().get_eye_height(); - self.perform_ranged_attack( - i, - t_pos.x, - t_pos.y + eye_h * 0.5, - t_pos.z, - false, - ) - .await; - let next_delay = 40 + rand::random_range(0..20); - self.next_head_update[i - 1] - .store(tick_count + next_delay, Ordering::Relaxed); - self.idle_head_updates[i - 1].store(0, Ordering::Relaxed); - } else { - self.set_alternative_target(i, 0); - } - } else { - self.set_alternative_target(i, 0); - } - } else { - let search_box = entity.bounding_box.load().expand(20.0, 8.0, 20.0); - let entities = world.get_entities_at_box(&search_box); - let candidates: Vec<_> = entities - .into_iter() - .filter(|e| { - e.get_entity().entity_id != entity.entity_id - && e.get_living_entity().is_some() - && e.get_entity().is_alive() - && !e - .get_entity() - .entity_type - .has_tag(&tag::EntityType::MINECRAFT_WITHER_FRIENDS) - }) - .collect(); - - if !candidates.is_empty() { - let idx = rand::random_range(0..candidates.len()); - self.set_alternative_target( - i, - candidates[idx].get_entity().entity_id, - ); - } - } - } - } - - // Block destruction - let destroy_tick = self.destroy_blocks_tick.load(Ordering::Relaxed); - if destroy_tick > 0 { - let next_destroy = destroy_tick - 1; - self.destroy_blocks_tick - .store(next_destroy, Ordering::Relaxed); - - if next_destroy == 0 && world.level_info.load().game_rules.mob_griefing { - let bb = entity.bounding_box.load(); - let bb_width = bb.max.x - bb.min.x; - let bb_height = bb.max.y - bb.min.y; - let width = (bb_width as f32 / 2.0 + 1.0).floor() as i32; - let height = (bb_height as f32).floor() as i32; - let min_pos = entity.block_pos.load(); - let mut destroyed = false; - - for dx in -width..=width { - for dy in 0..=height { - for dz in -width..=width { - let bpos = BlockPos::new( - min_pos.0.x + dx, - min_pos.0.y + dy, - min_pos.0.z + dz, - ); - let block = world.get_block(&bpos); - if Self::can_destroy(block) { - world - .set_block_state( - &bpos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - destroyed = true; - } - } - } - } - - if destroyed && !entity.silent.load(Ordering::Relaxed) { - world.sync_world_event( - WorldEvent::SoundWitherBlockBreak, - entity.block_pos.load(), - 0, - ); - } - } - } - } - }) + }; + wither.async_mob_tick().await; + }); } - fn pre_damage<'a>( - &'a self, - damage_type: DamageType, - source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if damage_type.has_tag(&tag::DamageType::MINECRAFT_WITHER_IMMUNE_TO) { + fn pre_damage(&self, damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool { + if damage_type.has_tag(&tag::DamageType::MINECRAFT_WITHER_IMMUNE_TO) { + return false; + } + + if let Some(src) = source { + let src_type = src.get_entity().entity_type; + if src_type == &EntityType::WITHER { return false; } - - if let Some(src) = source { - let src_type = src.get_entity().entity_type; - if src_type == &EntityType::WITHER { - return false; - } - if src_type.has_tag(&tag::EntityType::MINECRAFT_WITHER_FRIENDS) { - return false; - } - if self.is_powered() - && (src_type == &EntityType::ARROW - || src_type == &EntityType::SPECTRAL_ARROW - || src_type == &EntityType::WIND_CHARGE - || src_type == &EntityType::BREEZE_WIND_CHARGE) - { - return false; - } + if src_type.has_tag(&tag::EntityType::MINECRAFT_WITHER_FRIENDS) { + return false; } - - if self.get_invulnerable_ticks() > 0 - && !damage_type.has_tag(&tag::DamageType::MINECRAFT_BYPASSES_INVULNERABILITY) + if self.is_powered() + && (src_type == &EntityType::ARROW + || src_type == &EntityType::SPECTRAL_ARROW + || src_type == &EntityType::WIND_CHARGE + || src_type == &EntityType::BREEZE_WIND_CHARGE) { return false; } + } - true - }) + if self.get_invulnerable_ticks() > 0 + && !damage_type.has_tag(&tag::DamageType::MINECRAFT_BYPASSES_INVULNERABILITY) + { + return false; + } + + true } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.destroy_blocks_tick.load(Ordering::Relaxed) <= 0 { - self.destroy_blocks_tick.store(20, Ordering::Relaxed); - } + fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { + if self.destroy_blocks_tick.load(Ordering::Relaxed) <= 0 { + self.destroy_blocks_tick.store(20, Ordering::Relaxed); + } - for idle in &self.idle_head_updates { - idle.fetch_add(3, Ordering::Relaxed); - } - }) + for idle in &self.idle_head_updates { + idle.fetch_add(3, Ordering::Relaxed); + } } - fn post_tick(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { - let world = entity.world.load(); - self.remove_all_bossbar(&world).await; - if !self.dropped_loot.swap(true, Ordering::SeqCst) { - let pos = entity.block_pos.load(); - world - .drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)) - .await; + fn post_tick(&self) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { + let entity_id = entity.entity_id; + let world = entity.world.load_full(); + tokio::spawn(async move { + let Some(entity) = world.get_entity_by_id(entity_id) else { + return; + }; + let Some(wither) = entity.cast_any().downcast_ref::() else { + return; + }; + wither.remove_all_bossbar(&world).await; + if !wither.dropped_loot.swap(true, Ordering::SeqCst) { + let pos = entity.get_entity().block_pos.load(); + world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); } - } - }) + }); + } } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { diff --git a/crates/pumpkin/src/entity/breath.rs b/crates/pumpkin/src/entity/breath.rs index a35fb62ad..be992fc8b 100644 --- a/crates/pumpkin/src/entity/breath.rs +++ b/crates/pumpkin/src/entity/breath.rs @@ -32,7 +32,7 @@ impl Default for BreathManager { } impl BreathManager { - pub async fn tick(&self, player: &Arc) { + pub fn tick(&self, player: &Arc) { let mode = player.gamemode.load(); if matches!(mode, GameMode::Creative | GameMode::Spectator) { @@ -51,7 +51,6 @@ impl BreathManager { if player .living_entity .has_effect(&StatusEffect::WATER_BREATHING) - .await { if self.air_supply.swap(MAX_AIR, Ordering::Relaxed) != MAX_AIR { self.send_air_supply(player); @@ -75,14 +74,9 @@ impl BreathManager { player.entity_id(), new_air, ); - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(server.plugin_manager.fire(&server, &mut event)); + tokio::spawn(async move { + server.plugin_manager.fire(&server, &mut event).await; }); - if event.cancelled { - self.air_supply.store(prev, Ordering::Relaxed); - return; - } } self.send_air_supply(player); } @@ -92,10 +86,11 @@ impl BreathManager { if t >= DROWNING_INTERVAL { self.drowning_tick.store(0, Ordering::Relaxed); - player - .living_entity - .damage(player.as_ref(), DROWNING_DAMAGE, DamageType::DROWN) - .await; + player.living_entity.damage( + player.as_ref(), + DROWNING_DAMAGE, + DamageType::DROWN, + ); } } } else { diff --git a/crates/pumpkin/src/entity/combat.rs b/crates/pumpkin/src/entity/combat.rs index f7df0f07a..f0da220f0 100644 --- a/crates/pumpkin/src/entity/combat.rs +++ b/crates/pumpkin/src/entity/combat.rs @@ -26,13 +26,13 @@ pub enum AttackType { } impl AttackType { - pub async fn new(player: &Player, attack_cooldown_progress: f32) -> Self { + pub fn new(player: &Player, attack_cooldown_progress: f32) -> Self { let entity = &player.get_entity(); let sprinting = entity.is_sprinting(); let on_ground = entity.on_ground.load(Ordering::Relaxed); let fall_distance = player.living_entity.fall_distance.load(); - let held_item = player.inventory().held_item().await; + let held_item = player.inventory().held_item(); let is_mace = held_item.item.id == pumpkin_data::item::Item::MACE.id; if is_mace && !on_ground && fall_distance > 1.5 { diff --git a/crates/pumpkin/src/entity/decoration/armor_stand.rs b/crates/pumpkin/src/entity/decoration/armor_stand.rs index 383f3cb3e..d618820b0 100644 --- a/crates/pumpkin/src/entity/decoration/armor_stand.rs +++ b/crates/pumpkin/src/entity/decoration/armor_stand.rs @@ -185,7 +185,7 @@ impl ArmorStandEntity { self.rotation.store(packed.to_owned()); } - async fn break_and_drop_items(&self) { + fn break_and_drop_items(&self) { let entity = self.get_entity(); //let name = entity.custom_name.unwrap_or(entity.get_name()); @@ -194,8 +194,7 @@ impl ArmorStandEntity { entity .world .load() - .drop_stack(&entity.block_pos.load(), armor_stand_item) - .await; + .drop_stack(&entity.block_pos.load(), armor_stand_item); Self::on_break(entity); } @@ -257,7 +256,7 @@ impl EntityBase for ArmorStandEntity { if let Some(invisible) = nbt.get_bool("Invisible") && invisible { - self.get_entity().set_invisible(invisible).await; + self.get_entity().set_invisible(invisible); } if let Some(small) = nbt.get_bool("Small") @@ -309,118 +308,120 @@ impl EntityBase for ArmorStandEntity { fn kill<'a>(&'a self, _caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { Box::pin(async move { - self.get_entity().remove().await; + self.get_entity().remove(); // TODO: emit GameEvent::ENTITY_DIE }) } - fn damage_with_context<'a>( - &'a self, - caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, damage_type: DamageType, _position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let entity = self.get_entity(); - if entity.is_removed() { - return false; - } + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + let entity = self.get_entity(); + if entity.is_removed() { + return false; + } - let world = entity.world.load(); + let world = entity.world.load(); - let mob_griefing_gamerule = { - let game_rules = &world.level_info.load().game_rules; - game_rules.mob_griefing - }; + let mob_griefing_gamerule = { + let game_rules = &world.level_info.load().game_rules; + game_rules.mob_griefing + }; - if !mob_griefing_gamerule && source.is_some_and(|source| source.get_player().is_none()) + if !mob_griefing_gamerule && source.is_some_and(|source| source.get_player().is_none()) { + return false; + } + + let bypasses_invulnerability = + damage_type == DamageType::OUT_OF_WORLD || damage_type == DamageType::GENERIC_KILL; + + if bypasses_invulnerability { + entity.remove(); + return false; + } + + if entity.is_invulnerable_to(&damage_type) || self.is_invisible() || self.is_marker() { + return false; + } + + let is_explosion = damage_type == DamageType::FIREWORKS + || damage_type == DamageType::EXPLOSION + || damage_type == DamageType::PLAYER_EXPLOSION + || damage_type == DamageType::BAD_RESPAWN_POINT; + + if is_explosion { + Self::on_break(entity); + entity.remove(); + return false; + } + + // TODO: IGNITES_ARMOR_STANDS (in_fire, campfire) - set on fire + // TODO: BURNS_ARMOR_STANDS (on_fire) - reduce health + + let can_break = damage_type == DamageType::PLAYER_EXPLOSION + || damage_type == DamageType::PLAYER_ATTACK + || damage_type == DamageType::SPEAR + || damage_type == DamageType::MACE_SMASH; + + let always_kills = damage_type == DamageType::ARROW + || damage_type == DamageType::TRIDENT + || damage_type == DamageType::FIREBALL + || damage_type == DamageType::WITHER_SKULL + || damage_type == DamageType::WIND_CHARGE; + + if !can_break && !always_kills { + return false; + } + + let attacker = cause.or(source); + if let Some(attacker) = attacker + && let Some(player) = attacker.get_player() + { + if !player + .abilities + .try_lock() + .is_ok_and(|a| a.allow_modify_world) { return false; - } - - let bypasses_invulnerability = - damage_type == DamageType::OUT_OF_WORLD || damage_type == DamageType::GENERIC_KILL; - - if bypasses_invulnerability { - entity.kill(caller).await; - return false; - } - - if entity.is_invulnerable_to(&damage_type).await - || self.is_invisible() - || self.is_marker() - { - return false; - } - - let is_explosion = damage_type == DamageType::FIREWORKS - || damage_type == DamageType::EXPLOSION - || damage_type == DamageType::PLAYER_EXPLOSION - || damage_type == DamageType::BAD_RESPAWN_POINT; - - if is_explosion { - Self::on_break(entity); - entity.kill(caller).await; - return false; - } - - // TODO: IGNITES_ARMOR_STANDS (in_fire, campfire) - set on fire - // TODO: BURNS_ARMOR_STANDS (on_fire) - reduce health - - let can_break = damage_type == DamageType::PLAYER_EXPLOSION - || damage_type == DamageType::PLAYER_ATTACK - || damage_type == DamageType::SPEAR - || damage_type == DamageType::MACE_SMASH; - - let always_kills = damage_type == DamageType::ARROW - || damage_type == DamageType::TRIDENT - || damage_type == DamageType::FIREBALL - || damage_type == DamageType::WITHER_SKULL - || damage_type == DamageType::WIND_CHARGE; - - if !can_break && !always_kills { - return false; - } - - let attacker = cause.or(source); - if let Some(attacker) = attacker - && let Some(player) = attacker.get_player() - { - if !player.abilities.lock().await.allow_modify_world { - return false; - } else if player.is_creative() { - Self::spawn_break_particles(entity); - entity.kill(caller).await; - return true; - } - } - - let time = world.level_time.lock().await.query_gametime(); - - if time - self.last_hit_time.load(Ordering::Relaxed) > 5 && !always_kills { - world.send_entity_status(entity, EntityStatus::ArmorstandWobble, None); - world.play_sound( - Sound::EntityArmorStandHit, - SoundCategory::Neutral, - &entity.block_pos.load().to_f64(), - ); - self.last_hit_time.store(time, Ordering::Relaxed); - } else { + } else if player.is_creative() { Self::spawn_break_particles(entity); - world.play_sound( - Sound::EntityArmorStandBreak, - SoundCategory::Neutral, - &entity.block_pos.load().to_f64(), - ); - self.break_and_drop_items().await; - entity.kill(caller).await; + entity.remove(); + return true; } + } - true - }) + let time = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .query_gametime(); + + if time - self.last_hit_time.load(Ordering::Relaxed) > 5 && !always_kills { + world.send_entity_status(entity, EntityStatus::ArmorstandWobble, None); + world.play_sound( + Sound::EntityArmorStandHit, + SoundCategory::Neutral, + &entity.block_pos.load().to_f64(), + ); + self.last_hit_time.store(time, Ordering::Relaxed); + } else { + Self::spawn_break_particles(entity); + world.play_sound( + Sound::EntityArmorStandBreak, + SoundCategory::Neutral, + &entity.block_pos.load().to_f64(), + ); + self.break_and_drop_items(); + entity.remove(); + } + + true } fn get_gravity(&self) -> f64 { diff --git a/crates/pumpkin/src/entity/decoration/display.rs b/crates/pumpkin/src/entity/decoration/display.rs index 2e4345736..6fae2cb3a 100644 --- a/crates/pumpkin/src/entity/decoration/display.rs +++ b/crates/pumpkin/src/entity/decoration/display.rs @@ -1,8 +1,7 @@ use std::sync::{ - Arc, + Arc, Mutex, atomic::{AtomicI8, AtomicI32, AtomicU8, Ordering}, }; -use tokio::sync::Mutex; use pumpkin_data::{damage::DamageType, item_stack::ItemStack}; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; @@ -14,7 +13,7 @@ use pumpkin_protocol::{ use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}, + entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}, server::Server, }; @@ -70,7 +69,7 @@ pub struct DisplayEntity { impl DisplayEntity { pub fn new(entity: Entity) -> Self { - entity.no_clip.store(true, Ordering::Relaxed); + entity.no_physics.store(true, Ordering::Relaxed); Self { entity, interpolation_start_delta_ticks: AtomicI32::new(0), @@ -138,12 +137,18 @@ impl DisplayEntity { ); } - pub async fn get_translation(&self) -> Vector3 { - *self.translation.lock().await + pub fn get_translation(&self) -> Vector3 { + *self + .translation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_translation(&self, translation: Vector3) { - *self.translation.lock().await = translation; + pub fn set_translation(&self, translation: Vector3) { + *self + .translation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = translation; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::TRANSLATION, @@ -153,12 +158,18 @@ impl DisplayEntity { ); } - pub async fn get_scale(&self) -> Vector3 { - *self.scale.lock().await + pub fn get_scale(&self) -> Vector3 { + *self + .scale + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_scale(&self, scale: Vector3) { - *self.scale.lock().await = scale; + pub fn set_scale(&self, scale: Vector3) { + *self + .scale + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = scale; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::SCALE, @@ -168,12 +179,18 @@ impl DisplayEntity { ); } - pub async fn get_left_rotation(&self) -> [f32; 4] { - *self.left_rotation.lock().await + pub fn get_left_rotation(&self) -> [f32; 4] { + *self + .left_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_left_rotation(&self, left_rotation: [f32; 4]) { - *self.left_rotation.lock().await = left_rotation; + pub fn set_left_rotation(&self, left_rotation: [f32; 4]) { + *self + .left_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = left_rotation; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::LEFT_ROTATION, @@ -188,12 +205,18 @@ impl DisplayEntity { ); } - pub async fn get_right_rotation(&self) -> [f32; 4] { - *self.right_rotation.lock().await + pub fn get_right_rotation(&self) -> [f32; 4] { + *self + .right_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_right_rotation(&self, right_rotation: [f32; 4]) { - *self.right_rotation.lock().await = right_rotation; + pub fn set_right_rotation(&self, right_rotation: [f32; 4]) { + *self + .right_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = right_rotation; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::RIGHT_ROTATION, @@ -238,12 +261,18 @@ impl DisplayEntity { ); } - pub async fn get_view_range(&self) -> f32 { - *self.view_range.lock().await + pub fn get_view_range(&self) -> f32 { + *self + .view_range + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_view_range(&self, view_range: f32) { - *self.view_range.lock().await = view_range; + pub fn set_view_range(&self, view_range: f32) { + *self + .view_range + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = view_range; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::VIEW_RANGE, @@ -253,12 +282,18 @@ impl DisplayEntity { ); } - pub async fn get_shadow_radius(&self) -> f32 { - *self.shadow_radius.lock().await + pub fn get_shadow_radius(&self) -> f32 { + *self + .shadow_radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_shadow_radius(&self, shadow_radius: f32) { - *self.shadow_radius.lock().await = shadow_radius; + pub fn set_shadow_radius(&self, shadow_radius: f32) { + *self + .shadow_radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = shadow_radius; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::SHADOW_RADIUS, @@ -268,12 +303,18 @@ impl DisplayEntity { ); } - pub async fn get_shadow_strength(&self) -> f32 { - *self.shadow_strength.lock().await + pub fn get_shadow_strength(&self) -> f32 { + *self + .shadow_strength + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_shadow_strength(&self, shadow_strength: f32) { - *self.shadow_strength.lock().await = shadow_strength; + pub fn set_shadow_strength(&self, shadow_strength: f32) { + *self + .shadow_strength + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = shadow_strength; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::SHADOW_STRENGTH, @@ -283,12 +324,18 @@ impl DisplayEntity { ); } - pub async fn get_display_width(&self) -> f32 { - *self.width.lock().await + pub fn get_display_width(&self) -> f32 { + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_display_width(&self, width: f32) { - *self.width.lock().await = width; + pub fn set_display_width(&self, width: f32) { + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = width; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::WIDTH, @@ -298,12 +345,18 @@ impl DisplayEntity { ); } - pub async fn get_display_height(&self) -> f32 { - *self.height.lock().await + pub fn get_display_height(&self) -> f32 { + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_display_height(&self, height: f32) { - *self.height.lock().await = height; + pub fn set_display_height(&self, height: f32) { + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = height; self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::display::HEIGHT, @@ -329,16 +382,43 @@ impl DisplayEntity { } #[allow(clippy::too_many_lines)] - pub async fn init_display_data_tracker(&self) { - let view_range = *self.view_range.lock().await; - let shadow_radius = *self.shadow_radius.lock().await; - let shadow_strength = *self.shadow_strength.lock().await; - let width = *self.width.lock().await; - let height = *self.height.lock().await; - let translation = *self.translation.lock().await; - let scale = *self.scale.lock().await; - let left_rotation = *self.left_rotation.lock().await; - let right_rotation = *self.right_rotation.lock().await; + pub fn init_display_data_tracker(&self) { + let view_range = *self + .view_range + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let shadow_radius = *self + .shadow_radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let shadow_strength = *self + .shadow_strength + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let width = *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let height = *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let translation = *self + .translation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let scale = *self + .scale + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let left_rotation = *self + .left_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let right_rotation = *self + .right_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); self.entity.send_meta_data( &[Metadata::new( @@ -457,7 +537,8 @@ impl DisplayEntity { ); } - pub async fn write_display_nbt(&self, nbt: &mut NbtCompound) { + #[expect(clippy::too_many_lines)] + pub fn write_display_nbt(&self, nbt: &mut NbtCompound) { nbt.put_int( "interpolation_duration", self.interpolation_duration.load(Ordering::Relaxed), @@ -466,14 +547,44 @@ impl DisplayEntity { "start_interpolation", self.interpolation_start_delta_ticks.load(Ordering::Relaxed), ); - nbt.put_float("view_range", *self.view_range.lock().await); - nbt.put_float("shadow_radius", *self.shadow_radius.lock().await); - nbt.put_float("shadow_strength", *self.shadow_strength.lock().await); - nbt.put_float("width", *self.width.lock().await); - nbt.put_float("height", *self.height.lock().await); + nbt.put_float( + "view_range", + *self + .view_range + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "shadow_radius", + *self + .shadow_radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "shadow_strength", + *self + .shadow_strength + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "width", + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "height", + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); nbt.put_int( - "glow_color_override", - self.glow_color_override.load(Ordering::Relaxed), + "teleport_duration", + self.teleport_duration.load(Ordering::Relaxed), ); let billboard_str = match self.billboard.load(Ordering::Relaxed) { @@ -482,24 +593,44 @@ impl DisplayEntity { 3 => "center", _ => "fixed", }; - nbt.put_string("billboard", billboard_str.to_string()); + nbt.put_string("billboard", billboard_str.into()); + + let brightness = self.brightness.load(Ordering::Relaxed); + if brightness != -1 { + let block = brightness & 0xF; + let sky = (brightness >> 4) & 0xF; + let mut b_compound = NbtCompound::new(); + b_compound.put_int("block", block); + b_compound.put_int("sky", sky); + nbt.put("brightness", NbtTag::Compound(b_compound)); + } + + let glow_color_override = self.glow_color_override.load(Ordering::Relaxed); + if glow_color_override != -1 { + nbt.put_int("glow_color_override", glow_color_override); + } let mut transform = NbtCompound::new(); - let translation = *self.translation.lock().await; + let trans = *self + .translation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); transform.put( "translation", - NbtTag::List(vec![ - translation.x.into(), - translation.y.into(), - translation.z.into(), - ]), + NbtTag::List(vec![trans.x.into(), trans.y.into(), trans.z.into()]), ); - let scale = *self.scale.lock().await; + let scale = *self + .scale + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); transform.put( "scale", NbtTag::List(vec![scale.x.into(), scale.y.into(), scale.z.into()]), ); - let left_rot = *self.left_rotation.lock().await; + let left_rot = *self + .left_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); transform.put( "left_rotation", NbtTag::List(vec![ @@ -509,7 +640,10 @@ impl DisplayEntity { left_rot[3].into(), ]), ); - let right_rot = *self.right_rotation.lock().await; + let right_rot = *self + .right_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); transform.put( "right_rotation", NbtTag::List(vec![ @@ -522,7 +656,8 @@ impl DisplayEntity { nbt.put("transformation", NbtTag::Compound(transform)); } - pub async fn read_display_nbt(&self, nbt: &NbtCompound) { + #[expect(clippy::too_many_lines)] + pub fn read_display_nbt(&self, nbt: &NbtCompound) { if let Some(dur) = nbt.get_int("interpolation_duration") { self.interpolation_duration.store(dur, Ordering::Relaxed); } @@ -531,19 +666,34 @@ impl DisplayEntity { .store(start, Ordering::Relaxed); } if let Some(vr) = nbt.get_float("view_range") { - *self.view_range.lock().await = vr; + *self + .view_range + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = vr; } if let Some(sr) = nbt.get_float("shadow_radius") { - *self.shadow_radius.lock().await = sr; + *self + .shadow_radius + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = sr; } if let Some(ss) = nbt.get_float("shadow_strength") { - *self.shadow_strength.lock().await = ss; + *self + .shadow_strength + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = ss; } if let Some(w) = nbt.get_float("width") { - *self.width.lock().await = w; + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = w; } if let Some(h) = nbt.get_float("height") { - *self.height.lock().await = h; + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = h; } if let Some(glow) = nbt.get_int("glow_color_override") { self.glow_color_override.store(glow, Ordering::Relaxed); @@ -558,6 +708,13 @@ impl DisplayEntity { self.billboard.store(mode, Ordering::Relaxed); } + if let Some(b) = nbt.get_compound("brightness") { + let block = b.get_int("block").unwrap_or(0); + let sky = b.get_int("sky").unwrap_or(0); + self.brightness + .store((sky << 4) | (block & 0xF), Ordering::Relaxed); + } + if let Some(transform) = nbt.get_compound("transformation") { if let Some(t_list) = transform.get_list("translation") && t_list.len() >= 3 @@ -565,7 +722,10 @@ impl DisplayEntity { let x = t_list[0].extract_float().unwrap_or(0.0); let y = t_list[1].extract_float().unwrap_or(0.0); let z = t_list[2].extract_float().unwrap_or(0.0); - *self.translation.lock().await = Vector3::new(x, y, z); + *self + .translation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Vector3::new(x, y, z); } if let Some(s_list) = transform.get_list("scale") && s_list.len() >= 3 @@ -573,7 +733,10 @@ impl DisplayEntity { let x = s_list[0].extract_float().unwrap_or(1.0); let y = s_list[1].extract_float().unwrap_or(1.0); let z = s_list[2].extract_float().unwrap_or(1.0); - *self.scale.lock().await = Vector3::new(x, y, z); + *self + .scale + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Vector3::new(x, y, z); } if let Some(lr_list) = transform.get_list("left_rotation") && lr_list.len() >= 4 @@ -582,7 +745,10 @@ impl DisplayEntity { let y = lr_list[1].extract_float().unwrap_or(0.0); let z = lr_list[2].extract_float().unwrap_or(0.0); let w = lr_list[3].extract_float().unwrap_or(1.0); - *self.left_rotation.lock().await = [x, y, z, w]; + *self + .left_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = [x, y, z, w]; } if let Some(rr_list) = transform.get_list("right_rotation") && rr_list.len() >= 4 @@ -591,7 +757,10 @@ impl DisplayEntity { let y = rr_list[1].extract_float().unwrap_or(0.0); let z = rr_list[2].extract_float().unwrap_or(0.0); let w = rr_list[3].extract_float().unwrap_or(1.0); - *self.right_rotation.lock().await = [x, y, z, w]; + *self + .right_rotation + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = [x, y, z, w]; } } } @@ -629,39 +798,31 @@ impl BlockDisplayEntity { impl EntityBase for BlockDisplayEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.write_display_nbt(nbt).await; + self.display.write_display_nbt(nbt); nbt.put_int("block_state", self.block_state.load(Ordering::Relaxed)); }) } fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.read_display_nbt(nbt).await; + self.display.read_display_nbt(nbt); if let Some(state) = nbt.get_int("block_state") { self.block_state.store(state, Ordering::Relaxed); } }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move {}) - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.display.init_display_data_tracker().await; - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::block_display::BLOCK_STATE, - VarInt(self.block_state.load(Ordering::Relaxed)), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.display.init_display_data_tracker(); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::block_display::BLOCK_STATE, + VarInt(self.block_state.load(Ordering::Relaxed)), + )], + None, + ); } fn get_entity(&self) -> &Entity { @@ -692,16 +853,16 @@ impl EntityBase for BlockDisplayEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { false }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + false } } @@ -720,12 +881,18 @@ impl ItemDisplayEntity { }) } - pub async fn get_item(&self) -> ItemStack { - self.item_stack.lock().await.clone() + pub fn get_item(&self) -> ItemStack { + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - pub async fn set_item(&self, item: ItemStack) { - *self.item_stack.lock().await = item.clone(); + pub fn set_item(&self, item: ItemStack) { + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = item.clone(); self.display.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::item_display::ITEM, @@ -754,7 +921,7 @@ impl ItemDisplayEntity { impl EntityBase for ItemDisplayEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.write_display_nbt(nbt).await; + self.display.write_display_nbt(nbt); let display_mode_str = match self.item_display.load(Ordering::Relaxed) { 1 => "thirdperson_lefthand", 2 => "thirdperson_righthand", @@ -772,7 +939,7 @@ impl EntityBase for ItemDisplayEntity { fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.read_display_nbt(nbt).await; + self.display.read_display_nbt(nbt); if let Some(mode_str) = nbt.get_string("item_display") { let mode = match mode_str { "thirdperson_lefthand" => 1, @@ -790,32 +957,29 @@ impl EntityBase for ItemDisplayEntity { }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move {}) - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.display.init_display_data_tracker().await; - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM, - ItemStackSerializer::from(self.item_stack.lock().await.clone()), - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, - self.item_display.load(Ordering::Relaxed), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.display.init_display_data_tracker(); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item_display::ITEM, + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), + )], + None, + ); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, + self.item_display.load(Ordering::Relaxed), + )], + None, + ); } fn get_entity(&self) -> &Entity { @@ -846,16 +1010,16 @@ impl EntityBase for ItemDisplayEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { false }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + false } } @@ -880,12 +1044,18 @@ impl TextDisplayEntity { }) } - pub async fn get_text(&self) -> TextComponent { - self.text.lock().await.clone() + pub fn get_text(&self) -> TextComponent { + self.text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - pub async fn set_text(&self, text: TextComponent) { - *self.text.lock().await = text.clone(); + pub fn set_text(&self, text: TextComponent) { + *self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = text.clone(); self.display.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::text_display::TEXT, @@ -1035,8 +1205,13 @@ impl TextDisplayEntity { impl EntityBase for TextDisplayEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.write_display_nbt(nbt).await; - let text_json_res = pumpkin_util::serde_json::to_string(&*self.text.lock().await); + self.display.write_display_nbt(nbt); + let text_json_res = pumpkin_util::serde_json::to_string( + &*self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); if let Ok(text_json) = text_json_res { nbt.put_string("text", text_json); } @@ -1061,11 +1236,14 @@ impl EntityBase for TextDisplayEntity { fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - self.display.read_display_nbt(nbt).await; + self.display.read_display_nbt(nbt); if let Some(text_json) = nbt.get_string("text") && let Ok(component) = pumpkin_util::serde_json::from_str(text_json) { - *self.text.lock().await = component; + *self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = component; } if let Some(lw) = nbt.get_int("line_width") { self.line_width.store(lw, Ordering::Relaxed); @@ -1098,54 +1276,50 @@ impl EntityBase for TextDisplayEntity { }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move {}) - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.display.init_display_data_tracker().await; - let text = self.text.lock().await.clone(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT, - text, - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::LINE_WIDTH, - VarInt(self.line_width.load(Ordering::Relaxed)), - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::BACKGROUND, - VarInt(self.background.load(Ordering::Relaxed)), - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_OPACITY, - self.text_opacity.load(Ordering::Relaxed) as u8, - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - self.flags.load(Ordering::Relaxed), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.display.init_display_data_tracker(); + let text = self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::text_display::TEXT, + text, + )], + None, + ); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::text_display::LINE_WIDTH, + VarInt(self.line_width.load(Ordering::Relaxed)), + )], + None, + ); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::text_display::BACKGROUND, + VarInt(self.background.load(Ordering::Relaxed)), + )], + None, + ); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::text_display::TEXT_OPACITY, + self.text_opacity.load(Ordering::Relaxed) as u8, + )], + None, + ); + self.display.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + self.flags.load(Ordering::Relaxed), + )], + None, + ); } fn get_entity(&self) -> &Entity { @@ -1176,15 +1350,15 @@ impl EntityBase for TextDisplayEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { false }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + false } } diff --git a/crates/pumpkin/src/entity/decoration/end_crystal.rs b/crates/pumpkin/src/entity/decoration/end_crystal.rs index 8223f9989..00f07ad72 100644 --- a/crates/pumpkin/src/entity/decoration/end_crystal.rs +++ b/crates/pumpkin/src/entity/decoration/end_crystal.rs @@ -1,6 +1,6 @@ use core::f32; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}; +use crate::entity::{Entity, EntityBase, living::LivingEntity}; use pumpkin_data::{ damage::DamageType, tag::{self, Taggable}, @@ -39,32 +39,28 @@ impl EntityBase for EndCrystalEntity { None } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - self.entity.remove().await; - if !damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) { - self.entity - .world - .load() - .explode( - self.entity.pos.load(), - 6.0, - crate::world::ExplosionInteraction::Block, - ) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + self.entity.remove(); + if !damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) { + let world = self.entity.world.load(); + let pos = self.entity.pos.load(); + tokio::spawn(async move { + world + .explode(pos, 6.0, crate::world::ExplosionInteraction::Block) .await; - } + }); + } - // TODO - true - }) + // TODO + true } fn cast_any(&self) -> &dyn std::any::Any { self diff --git a/crates/pumpkin/src/entity/decoration/item_frame.rs b/crates/pumpkin/src/entity/decoration/item_frame.rs index 4e778c968..1b888e810 100644 --- a/crates/pumpkin/src/entity/decoration/item_frame.rs +++ b/crates/pumpkin/src/entity/decoration/item_frame.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use std::sync::{Arc, Mutex}; use crate::entity::player::Player; use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}; @@ -15,7 +15,6 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; use pumpkin_protocol::java::client::play::{CSetEntityMetadata, Metadata}; use pumpkin_util::math::vector3::Vector3; -use tokio::sync::Mutex; /// An item frame or glow item frame. /// @@ -111,18 +110,24 @@ impl ItemFrameEntity { self.entity.data.store(i32::from(index), Ordering::Relaxed); } - pub async fn get_item(&self) -> ItemStack { - self.item_stack.lock().await.clone() + pub fn get_item(&self) -> ItemStack { + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - pub async fn set_item(&self, mut item_stack: ItemStack, update_neighbours: bool) { + pub fn set_item(&self, mut item_stack: ItemStack, update_neighbours: bool) { if !item_stack.is_empty() { item_stack.item_count = 1; } let play_sound = !item_stack.is_empty(); let item_serializer = ItemStackSerializer::from(item_stack.clone()); - *self.item_stack.lock().await = item_stack; + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = item_stack; self.entity.send_meta_data( &[Metadata::new( @@ -139,7 +144,7 @@ impl ItemFrameEntity { if update_neighbours { let world = self.entity.world.load(); let pos = self.entity.block_pos.load(); - world.update_neighbors(&pos, None).await; + world.update_neighbors(&pos, None); } } @@ -147,7 +152,7 @@ impl ItemFrameEntity { self.rotation.load(Ordering::Relaxed) % 8 } - pub async fn set_rotation(&self, rotation: u8, update_neighbours: bool) { + pub fn set_rotation(&self, rotation: u8, update_neighbours: bool) { let rot = rotation % 8; self.rotation.store(rot, Ordering::Relaxed); @@ -162,7 +167,7 @@ impl ItemFrameEntity { if update_neighbours { let world = self.entity.world.load(); let pos = self.entity.block_pos.load(); - world.update_neighbors(&pos, None).await; + world.update_neighbors(&pos, None); } } @@ -206,8 +211,8 @@ impl ItemFrameEntity { stack } - pub async fn get_pick_result(&self) -> ItemStack { - let framed_stack = self.get_item().await; + pub fn get_pick_result(&self) -> ItemStack { + let framed_stack = self.get_item(); if framed_stack.is_empty() { self.get_frame_item_stack_with_data() } else { @@ -218,21 +223,37 @@ impl ItemFrameEntity { /// The comparator signal this frame produces. /// /// Vanilla: `getItem().isEmpty() ? 0 : getRotation() % 8 + 1`. - pub async fn get_analog_output(&self) -> u8 { - if self.item_stack.lock().await.is_empty() { + pub fn get_analog_output(&self) -> u8 { + if self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() + { 0 } else { self.rotation.load(Ordering::Relaxed) % 8 + 1 } } - pub async fn drop_item(&self, caused_by: Option<&dyn EntityBase>, with_frame: bool) { + pub fn drop_item(&self, caused_by: Option<&dyn EntityBase>, with_frame: bool) { if self.is_fixed() { return; } - let item_stack = self.get_item().await; - self.set_item(ItemStack::EMPTY.clone(), true).await; + let item_stack = self.get_item(); + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = ItemStack::EMPTY.clone(); + let item_serializer = ItemStackSerializer::from(ItemStack::EMPTY.clone()); + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item_frame::ITEM, + &item_serializer, + )], + None, + ); let is_creative_player = caused_by.is_some_and(|s| { s.cast_any() @@ -248,15 +269,13 @@ impl ItemFrameEntity { let pos = self.entity.block_pos.load(); if with_frame { - world - .drop_stack(&pos, self.get_frame_item_stack_with_data()) - .await; + world.drop_stack(&pos, self.get_frame_item_stack_with_data()); } if !item_stack.is_empty() { let drop_chance = self.item_drop_chance.load(); if rand::random::() < drop_chance { - world.drop_stack(&pos, item_stack).await; + world.drop_stack(&pos, item_stack); } } } @@ -265,7 +284,10 @@ impl ItemFrameEntity { impl EntityBase for ItemFrameEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - let item = self.item_stack.lock().await; + let item = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if !item.is_empty() { let mut item_compound = NbtCompound::new(); item.write_item_stack(&mut item_compound); @@ -284,7 +306,10 @@ impl EntityBase for ItemFrameEntity { if let Some(item_compound) = nbt.get_compound("Item") && let Some(stack) = ItemStack::read_item_stack(item_compound) { - *self.item_stack.lock().await = stack; + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; } self.rotation.store( (nbt.get_byte("ItemRotation").unwrap_or(0) as u8) % 8, @@ -313,26 +338,29 @@ impl EntityBase for ItemFrameEntity { None } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async { - let item_serializer = ItemStackSerializer::from(self.item_stack.lock().await.clone()); - let rotation = self.get_rotation() as i32; + fn init_data_tracker(&self) { + let item_serializer = ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ); + let rotation = self.get_rotation() as i32; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ITEM, - &item_serializer, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ROTATION, - rotation, - )], - None, - ); - }) + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item_frame::ITEM, + &item_serializer, + )], + None, + ); + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item_frame::ROTATION, + rotation, + )], + None, + ); } fn send_java_spawn_packet<'a>( @@ -347,8 +375,12 @@ impl EntityBase for ItemFrameEntity { let ver = client.version.load(); if ver >= CURRENT_MC_VERSION { - let item_serializer = - ItemStackSerializer::from(self.item_stack.lock().await.clone()); + let item_serializer = ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ); let rotation = self.get_rotation() as i32; let mut data = Vec::new(); @@ -383,18 +415,18 @@ impl EntityBase for ItemFrameEntity { return false; } - let frame_has_item = !self.get_item().await.is_empty(); + let frame_has_item = !self.get_item().is_empty(); let has_held_item = !item_stack.is_empty(); if frame_has_item { let new_rot = self.get_rotation() + 1; - self.set_rotation(new_rot, true).await; + self.set_rotation(new_rot, true); self.entity.play_sound(self.get_rotate_item_sound()); true } else if has_held_item && !self.entity.removed.load(Ordering::Relaxed) { let mut new_stack = item_stack.clone(); new_stack.item_count = 1; - self.set_item(new_stack, true).await; + self.set_item(new_stack, true); if !player.is_creative() { item_stack.decrement(1); @@ -406,48 +438,46 @@ impl EntityBase for ItemFrameEntity { }) } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, damage_type: DamageType, _position: Option>, - source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let fixed = self.is_fixed(); - let is_creative_player = source.is_some_and(|s| { - s.cast_any() - .downcast_ref::() - .is_some_and(Player::is_creative) - }); - let bypasses_invuln = - damage_type == DamageType::OUT_OF_WORLD || damage_type == DamageType::GENERIC_KILL; + source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + let fixed = self.is_fixed(); + let is_creative_player = source.is_some_and(|s| { + s.cast_any() + .downcast_ref::() + .is_some_and(Player::is_creative) + }); + let bypasses_invuln = + damage_type == DamageType::OUT_OF_WORLD || damage_type == DamageType::GENERIC_KILL; - if fixed { - if !bypasses_invuln && !is_creative_player { - return false; - } - self.drop_item(source, true).await; - self.entity.remove().await; - return true; + if fixed { + if !bypasses_invuln && !is_creative_player { + return false; } + self.drop_item(source, true); + self.entity.remove(); + return true; + } - let has_item = !self.get_item().await.is_empty(); - let is_explosion = - damage_type == DamageType::EXPLOSION || damage_type == DamageType::PLAYER_EXPLOSION; + let has_item = !self.get_item().is_empty(); + let is_explosion = + damage_type == DamageType::EXPLOSION || damage_type == DamageType::PLAYER_EXPLOSION; - if !is_explosion && has_item { - self.drop_item(source, false).await; - self.entity.play_sound(self.get_remove_item_sound()); - } else { - self.drop_item(source, true).await; - self.entity.play_sound(self.get_break_sound()); - self.entity.remove().await; - } - true - }) + if !is_explosion && has_item { + self.drop_item(source, false); + self.entity.play_sound(self.get_remove_item_sound()); + } else { + self.drop_item(source, true); + self.entity.play_sound(self.get_break_sound()); + self.entity.remove(); + } + true } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/decoration/leash_knot.rs b/crates/pumpkin/src/entity/decoration/leash_knot.rs index bf4e7adf3..b5e72b993 100644 --- a/crates/pumpkin/src/entity/decoration/leash_knot.rs +++ b/crates/pumpkin/src/entity/decoration/leash_knot.rs @@ -26,11 +26,11 @@ impl LeashKnotEntity { self.pos } - pub async fn get_or_create(world: &Arc, pos: BlockPos) -> Arc { + pub fn get_or_create(world: &Arc, pos: BlockPos) -> Arc { if let Some(existing) = Self::get_knot(world, pos) { return existing; } - Self::create_knot(world, pos).await + Self::create_knot(world, pos) } pub fn get_knot(world: &Arc, pos: BlockPos) -> Option> { @@ -60,7 +60,7 @@ impl LeashKnotEntity { None } - pub async fn create_knot(world: &Arc, pos: BlockPos) -> Arc { + pub fn create_knot(world: &Arc, pos: BlockPos) -> Arc { let raw_pos = Vector3::new( f64::from(pos.0.x) + 0.5, f64::from(pos.0.y) + Self::OFFSET_Y, @@ -69,9 +69,7 @@ impl LeashKnotEntity { let entity = Entity::new(world.clone(), raw_pos, &EntityType::LEASH_KNOT); let knot = Arc::new(Self::new(entity, pos)); - world - .spawn_entity(knot.clone() as Arc) - .await; + world.spawn_entity(knot.clone() as Arc); world.play_sound(Sound::ItemLeadTied, SoundCategory::Neutral, &raw_pos); @@ -84,6 +82,8 @@ impl LeashKnotEntity { } } +use crate::server::Server; + impl EntityBase for LeashKnotEntity { fn get_entity(&self) -> &Entity { &self.entity @@ -93,53 +93,46 @@ impl EntityBase for LeashKnotEntity { None } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a crate::server::Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let world = self.entity.world.load(); - let block = world.get_block(&self.pos); - if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_FENCES) { - let knot_id = self.entity.entity_id; - let search_dim = EntityDimensions { - width: 32.0, - height: 32.0, - eye_height: 16.0, - }; - let pos = self.entity.pos.load(); - let search_box = BoundingBox::new_from_pos(pos.x, pos.y, pos.z, &search_dim); - let entities = world.get_entities_at_box(&search_box); + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { + let world = self.entity.world.load(); + let block = world.get_block(&self.pos); + if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_FENCES) { + let knot_id = self.entity.entity_id; + let search_dim = EntityDimensions { + width: 32.0, + height: 32.0, + eye_height: 16.0, + }; + let pos = self.entity.pos.load(); + let search_box = BoundingBox::new_from_pos(pos.x, pos.y, pos.z, &search_dim); + let entities = world.get_entities_at_box(&search_box); - for entity_base in entities { - let ent = entity_base.get_entity(); - let is_attached_to_knot = ent - .leashed_to - .try_lock() - .ok() - .and_then(|guard| { - guard - .as_ref() - .map(|holder| holder.get_entity().entity_id == knot_id) - }) - .unwrap_or(false); + for entity_base in entities { + let ent = entity_base.get_entity(); + let is_attached_to_knot = ent + .leashed_to + .try_lock() + .ok() + .and_then(|guard| { + guard + .as_ref() + .map(|holder| holder.get_entity().entity_id == knot_id) + }) + .unwrap_or(false); - if is_attached_to_knot { - ent.unleash().await; - let lead_item = pumpkin_data::item_stack::ItemStack::new( - 1, - &pumpkin_data::item::Item::LEAD, - ); - world.drop_stack(&ent.block_pos.load(), lead_item).await; - } + if is_attached_to_knot { + ent.unleash(); + let lead_item = pumpkin_data::item_stack::ItemStack::new( + 1, + &pumpkin_data::item::Item::LEAD, + ); + world.drop_stack(&ent.block_pos.load(), lead_item); } - - world.play_sound(Sound::ItemLeadUntied, SoundCategory::Neutral, &pos); - - self.entity.remove().await; } - }) + + world.play_sound(Sound::ItemLeadUntied, SoundCategory::Neutral, &pos); + self.entity.remove(); + } } fn interact<'a>( @@ -176,7 +169,7 @@ impl EntityBase for LeashKnotEntity { if let Some(self_knot) = Self::get_knot(&world, self.pos) { for mob in player_leashed_mobs { - mob.leash_to(self_knot.clone() as Arc).await; + mob.leash_to(self_knot.clone() as Arc); attached_mob = true; } } @@ -189,7 +182,7 @@ impl EntityBase for LeashKnotEntity { && let Some(holder) = guard.as_ref() && holder.get_entity().entity_id == knot_id { - ent.leash_to(player.clone() as Arc).await; + ent.leash_to(player.clone() as Arc); any_dropped = true; } } diff --git a/crates/pumpkin/src/entity/decoration/painting.rs b/crates/pumpkin/src/entity/decoration/painting.rs index 320ccd56e..a22aa76a7 100644 --- a/crates/pumpkin/src/entity/decoration/painting.rs +++ b/crates/pumpkin/src/entity/decoration/painting.rs @@ -1,7 +1,7 @@ use core::f32; use std::sync::atomic::Ordering; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}; +use crate::entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::vector3::Vector3; @@ -38,20 +38,18 @@ impl EntityBase for PaintingEntity { None } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async { - // TODO - self.entity.remove().await; - true - }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + // TODO + self.entity.remove(); + true } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/effect/infested.rs b/crates/pumpkin/src/entity/effect/infested.rs index a5c086676..b0eae2a1e 100644 --- a/crates/pumpkin/src/entity/effect/infested.rs +++ b/crates/pumpkin/src/entity/effect/infested.rs @@ -64,7 +64,7 @@ impl MobEffect for InfestedMobEffect { f64::from(rz), )); - world.spawn_entity(silver).await; + world.spawn_entity(silver); world.play_sound(Sound::EntitySilverfishHurt, SoundCategory::Hostile, ¢er); } } diff --git a/crates/pumpkin/src/entity/effect/oozing.rs b/crates/pumpkin/src/entity/effect/oozing.rs index 2a9037dc3..2c35b241d 100644 --- a/crates/pumpkin/src/entity/effect/oozing.rs +++ b/crates/pumpkin/src/entity/effect/oozing.rs @@ -40,7 +40,7 @@ impl MobEffect for OozingMobEffect { slime.set_size(2, true); } - world.spawn_entity(entity_arc).await; + world.spawn_entity(entity_arc); } }) } diff --git a/crates/pumpkin/src/entity/effect/poison.rs b/crates/pumpkin/src/entity/effect/poison.rs index deb7da758..2ad98f576 100644 --- a/crates/pumpkin/src/entity/effect/poison.rs +++ b/crates/pumpkin/src/entity/effect/poison.rs @@ -34,9 +34,7 @@ impl MobEffect for PoisonMobEffect { { let damage_amount = (current_health - 1.0).min(1.0); if damage_amount > 0.0 { - dyn_self - .damage(&*dyn_self, damage_amount, DamageType::MAGIC) - .await; + dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC); } } }) diff --git a/crates/pumpkin/src/entity/effect/raid_omen.rs b/crates/pumpkin/src/entity/effect/raid_omen.rs index 598e5b6b4..2dd9f327a 100644 --- a/crates/pumpkin/src/entity/effect/raid_omen.rs +++ b/crates/pumpkin/src/entity/effect/raid_omen.rs @@ -1,4 +1,3 @@ -use crate::entity::EntityBase; use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::living::LivingEntity; @@ -23,7 +22,10 @@ impl MobEffect for RaidOmenMobEffect { let raid_pos = player .get_raid_omen_position() .unwrap_or_else(|| living.entity.block_pos.load()); - let mut raids = world.raids.lock().await; + let mut raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); raids.create_or_extend_raid(player, raid_pos, &world); player.clear_raid_omen_position(); } diff --git a/crates/pumpkin/src/entity/effect/weaving.rs b/crates/pumpkin/src/entity/effect/weaving.rs index ff82a2292..771b80777 100644 --- a/crates/pumpkin/src/entity/effect/weaving.rs +++ b/crates/pumpkin/src/entity/effect/weaving.rs @@ -55,13 +55,11 @@ impl MobEffect for WeavingMobEffect { } for target_pos in positions_to_transform { - world - .set_block_state( - &target_pos, - Block::COBWEB.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &target_pos, + Block::COBWEB.default_state.id, + BlockFlags::NOTIFY_ALL, + ); world.sync_world_event(WorldEvent::AnimationSpawnCobweb, target_pos, 0); } }) diff --git a/crates/pumpkin/src/entity/effect/wither.rs b/crates/pumpkin/src/entity/effect/wither.rs index e0d023c22..de5d163e7 100644 --- a/crates/pumpkin/src/entity/effect/wither.rs +++ b/crates/pumpkin/src/entity/effect/wither.rs @@ -30,7 +30,7 @@ impl MobEffect for WitherMobEffect { .load() .get_entity_by_id(living.entity.entity_id); if let Some(dyn_self) = dyn_self { - dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER).await; + dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER); } }) } diff --git a/crates/pumpkin/src/entity/experience_orb.rs b/crates/pumpkin/src/entity/experience_orb.rs index 2f0d5b2b8..c04a72c74 100644 --- a/crates/pumpkin/src/entity/experience_orb.rs +++ b/crates/pumpkin/src/entity/experience_orb.rs @@ -7,7 +7,7 @@ use std::sync::{ use pumpkin_data::entity::EntityType; use pumpkin_util::math::vector3::Vector3; -use crate::{entity::EntityBaseFuture, server::Server, world::World}; +use crate::{server::Server, world::World}; use super::{Entity, EntityBase, living::LivingEntity, player::Player}; @@ -27,14 +27,14 @@ impl ExperienceOrbEntity { } } - pub async fn spawn(world: &Arc, position: Vector3, amount: u32) { + pub fn spawn(world: &Arc, position: Vector3, amount: u32) { let mut amount = amount; while amount > 0 { let i = Self::round_to_orb_size(amount); amount -= i; let entity = Entity::new(world.clone(), position, &EntityType::EXPERIENCE_ORB); let orb = Arc::new(Self::new(entity, i)); - world.spawn_entity(orb).await; + world.spawn_entity(orb); } } @@ -66,63 +66,65 @@ impl ExperienceOrbEntity { } impl EntityBase for ExperienceOrbEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - entity.tick(caller, server).await; - let bounding_box = entity.bounding_box.load(); + fn tick(&self, caller: &Arc, server: &Server) { + let entity = &self.entity; + entity.tick(caller, server); + let bounding_box = entity.bounding_box.load(); - let original_velo = entity.velocity.load(); + let original_velo = entity.velocity.load(); - let mut velo = original_velo; + let mut velo = original_velo; - let no_clip = !self - .entity - .world - .load() - .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)); - // TODO: isSubmergedIn - if !no_clip { - velo.y -= self.get_gravity(); - } + let no_physics = !self + .entity + .world + .load() + .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)); + self.entity.no_physics.store(no_physics, Ordering::Relaxed); + // TODO: isSubmergedIn + if !no_physics { + velo.y -= self.get_gravity(); + } - entity.velocity.store(velo); + entity.velocity.store(velo); - entity.move_entity(caller, velo).await; + entity.move_entity(caller, velo); - entity.tick_block_collisions(caller, server).await; + entity.tick_block_collisions(caller, server); - let age = self.orb_age.fetch_add(1, Ordering::Relaxed); - if age >= 6000 { - self.entity.remove().await; - } - }) + let age = self.orb_age.fetch_add(1, Ordering::Relaxed); + if age >= 6000 { + entity.remove(); + } } fn get_entity(&self) -> &Entity { &self.entity } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if player.living_entity.health.load() > 0.0 { - let mut delay = player.experience_pick_up_delay.lock().await; - if *delay == 0 { - *delay = 2; - player.living_entity.pickup(&self.entity, 1); - let remaining = player.apply_mending_from_xp(self.amount as i32).await; + fn on_player_collision(&self, player: &Arc) { + if player.living_entity.health.load() > 0.0 { + let can_pickup = if let Ok(mut delay) = player.experience_pick_up_delay.try_lock() + && *delay == 0 + { + *delay = 2; + true + } else { + false + }; + if can_pickup { + player.living_entity.pickup(&self.entity, 1); + self.entity.remove(); + let player_clone = player.clone(); + let amount = self.amount as i32; + tokio::spawn(async move { + let remaining = player_clone.apply_mending_from_xp(amount).await; if remaining > 0 { - player.add_experience_points(remaining).await; + player_clone.add_experience_points(remaining).await; } - // TODO: pickingCount for merging - self.entity.remove().await; - } + }); } - }) + } } fn get_living_entity(&self) -> Option<&LivingEntity> { diff --git a/crates/pumpkin/src/entity/falling.rs b/crates/pumpkin/src/entity/falling.rs index 7d9f5542e..a487b6976 100644 --- a/crates/pumpkin/src/entity/falling.rs +++ b/crates/pumpkin/src/entity/falling.rs @@ -8,7 +8,7 @@ use pumpkin_world::world::BlockFlags; use std::sync::{Arc, atomic::Ordering}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}, + entity::{Entity, EntityBase, living::LivingEntity}, server::Server, world::World, }; @@ -26,16 +26,14 @@ impl FallingEntity { } } - /// Replaced the current Block and Spawns a new Falling one - pub async fn replace_spawn(world: &Arc, position: BlockPos, block_state: BlockStateId) { + /// Replaced the current Block and Spawns a new Falling one (synchronous) + pub fn replace_spawn(world: &Arc, position: BlockPos, block_state: BlockStateId) { // Replace the original block, TODO: use fluid state - world - .set_block_state( - &position, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &position, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ); let position = position.0.to_f64().add_raw(0.5, 0.0, 0.5); let entity = Entity::new(world.clone(), position, &EntityType::FALLING_BLOCK); @@ -43,61 +41,46 @@ impl FallingEntity { .data .store(i32::from(block_state.as_u16()), Ordering::Relaxed); let entity = Arc::new(Self::new(entity, block_state)); - world.spawn_entity(entity).await; + world.spawn_entity_non_save(entity); } } impl EntityBase for FallingEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - entity.tick(caller, server).await; + fn tick(&self, caller: &Arc, server: &Server) { + let entity = &self.entity; + let mut velo = entity.velocity.load(); + velo.y -= self.get_gravity(); - let original_velo = entity.velocity.load(); - let mut velo = original_velo; - velo.y -= self.get_gravity(); + entity.velocity.store(velo); - entity.velocity.store(velo); + entity.move_entity(caller, velo); + entity.tick_block_collisions(caller, server); + if entity.on_ground.load(Ordering::Relaxed) { + entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); + entity.world.load().set_block_state( + &self.entity.block_pos.load(), + self.block_state_id, + BlockFlags::NOTIFY_ALL, + ); + self.entity.remove(); + } - 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 - .world - .load() - .set_block_state( - &self.entity.block_pos.load(), - self.block_state_id, - BlockFlags::NOTIFY_ALL, - ) - .await; - entity.remove().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.velocity_dirty.swap(false, Ordering::SeqCst) { - entity.send_pos_rot(); - entity.send_velocity(); - } - }) + if entity.velocity_dirty.swap(false, Ordering::SeqCst) { + entity.send_pos_rot(); + entity.send_velocity(); + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::falling_block::START_POS, - self.entity.block_pos.load(), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::falling_block::START_POS, + self.entity.block_pos.load(), + )], + None, + ); } fn get_entity(&self) -> &Entity { @@ -107,13 +90,8 @@ impl EntityBase for FallingEntity { fn get_living_entity(&self) -> Option<&LivingEntity> { None } - fn damage<'a>( - &'a self, - _caller: &'a dyn EntityBase, - _amount: f32, - _damage_type: DamageType, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { false }) + fn damage(&self, _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType) -> bool { + false } fn get_gravity(&self) -> f64 { diff --git a/crates/pumpkin/src/entity/hunger.rs b/crates/pumpkin/src/entity/hunger.rs index d41e142af..68d1d6e94 100644 --- a/crates/pumpkin/src/entity/hunger.rs +++ b/crates/pumpkin/src/entity/hunger.rs @@ -30,7 +30,7 @@ impl Default for HungerManager { } impl HungerManager { - pub async fn tick(&self, player: &Arc) { + pub fn tick(&self, player: &Arc) { let mut level = self.level.load(); let mut saturation = self.saturation.load(); let mut exhaustion = self.exhaustion.load(); @@ -102,19 +102,19 @@ impl HungerManager { } if needs_sync { - player.send_health().await; + player.send_health(); } if heal_amount > 0.0 { - player.heal(heal_amount).await; + player.heal(heal_amount); } if damage_amount > 0.0 { player - .damage(&**player, damage_amount, DamageType::STARVE) - .await; + .living_entity + .damage(player.as_ref(), damage_amount, DamageType::STARVE); } } - pub async fn eat(&self, player: &Player, food: u8, saturation_modifier: f32) { + pub fn eat(&self, player: &Player, food: u8, saturation_modifier: f32) { let added_saturation = f32::from(food) * saturation_modifier * 2.0; let current_level = self.level.load(); @@ -127,7 +127,7 @@ impl HungerManager { self.level.store(new_level); self.saturation.store(new_sat); - player.send_health().await; + player.send_health(); } /// Add exhaustion to trigger hunger decrease diff --git a/crates/pumpkin/src/entity/interaction.rs b/crates/pumpkin/src/entity/interaction.rs index 4d54d6102..9962e877f 100644 --- a/crates/pumpkin/src/entity/interaction.rs +++ b/crates/pumpkin/src/entity/interaction.rs @@ -1,8 +1,7 @@ use std::sync::{ - Arc, + Arc, Mutex, atomic::{AtomicBool, Ordering}, }; -use tokio::sync::Mutex; use uuid::Uuid; use pumpkin_data::{damage::DamageType, item_stack::ItemStack}; @@ -54,7 +53,7 @@ pub struct InteractionEntity { impl InteractionEntity { pub fn new(entity: Entity) -> Arc { - entity.no_clip.store(true, Ordering::Relaxed); + entity.no_physics.store(true, Ordering::Relaxed); let width = 1.0; let height = 1.0; let dimensions = EntityDimensions::new(width, height, height * 0.85); @@ -72,13 +71,19 @@ impl InteractionEntity { }) } - pub async fn get_width(&self) -> f32 { - *self.width.lock().await + pub fn get_width(&self) -> f32 { + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_width(&self, width: f32) { - *self.width.lock().await = width; - self.update_dimensions().await; + pub fn set_width(&self, width: f32) { + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = width; + self.update_dimensions(); self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::interaction::WIDTH, @@ -88,13 +93,19 @@ impl InteractionEntity { ); } - pub async fn get_height(&self) -> f32 { - *self.height.lock().await + pub fn get_height(&self) -> f32 { + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_height(&self, height: f32) { - *self.height.lock().await = height; - self.update_dimensions().await; + pub fn set_height(&self, height: f32) { + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = height; + self.update_dimensions(); self.entity.send_meta_data( &[Metadata::new( pumpkin_data::tracked_data::interaction::HEIGHT, @@ -119,37 +130,67 @@ impl InteractionEntity { ); } - pub async fn update_dimensions(&self) { - let width = *self.width.lock().await; - let height = *self.height.lock().await; + pub fn update_dimensions(&self) { + let width = *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let height = *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let dimensions = EntityDimensions::new(width, height, height * 0.85); let pos = self.entity.pos.load(); let aabb = BoundingBox::new_from_pos(pos.x, pos.y, pos.z, &dimensions); self.entity.bounding_box.store(aabb); } - pub async fn get_last_attacker(&self) -> Option { - *self.attack.lock().await + pub fn get_last_attacker(&self) -> Option { + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn get_target(&self) -> Option { - *self.interaction.lock().await + pub fn get_target(&self) -> Option { + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } } impl EntityBase for InteractionEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - nbt.put_float("width", *self.width.lock().await); - nbt.put_float("height", *self.height.lock().await); + nbt.put_float( + "width", + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "height", + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); nbt.put_bool("response", self.response.load(Ordering::Relaxed)); - let attack = *self.attack.lock().await; + let attack = *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(attack) = attack { nbt.put("attack", NbtTag::Compound(attack.to_nbt())); } - let interaction = *self.interaction.lock().await; + let interaction = *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(interaction) = interaction { nbt.put("interaction", NbtTag::Compound(interaction.to_nbt())); } @@ -162,61 +203,79 @@ impl EntityBase for InteractionEntity { let height = nbt.get_float("height").unwrap_or(1.0); let response = nbt.get_bool("response").unwrap_or(false); - *self.width.lock().await = width; - *self.height.lock().await = height; + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = width; + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = height; self.response.store(response, Ordering::Relaxed); - self.update_dimensions().await; + self.update_dimensions(); if let Some(attack_compound) = nbt.get_compound("attack") { - *self.attack.lock().await = PlayerAction::from_nbt(attack_compound); + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + PlayerAction::from_nbt(attack_compound); } else { - *self.attack.lock().await = None; + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } if let Some(interaction_compound) = nbt.get_compound("interaction") { - *self.interaction.lock().await = PlayerAction::from_nbt(interaction_compound); + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + PlayerAction::from_nbt(interaction_compound); } else { - *self.interaction.lock().await = None; + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move {}) - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let width = *self.width.lock().await; - let height = *self.height.lock().await; - let response = self.response.load(Ordering::Relaxed); + fn init_data_tracker(&self) { + let width = *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let height = *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let response = self.response.load(Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::WIDTH, - width, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::HEIGHT, - height, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::RESPONSE, - response, - )], - None, - ); - }) + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::interaction::WIDTH, + width, + )], + None, + ); + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::interaction::HEIGHT, + height, + )], + None, + ); + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::interaction::RESPONSE, + response, + )], + None, + ); } fn get_entity(&self) -> &Entity { @@ -247,28 +306,29 @@ impl EntityBase for InteractionEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let player = source - .or(cause) - .and_then(|e| e.cast_any().downcast_ref::()); - if let Some(player) = player { - let timestamp = self.entity.world.load().level_time.lock().await.world_age as i64; - *self.attack.lock().await = Some(PlayerAction { - player: player.gameprofile.id, - timestamp, - }); - } - false - }) + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + let player = source + .or(cause) + .and_then(|e| e.cast_any().downcast_ref::()); + if let Some(player) = player { + let timestamp = self.entity.world.load().get_world_age(); + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(PlayerAction { + player: player.gameprofile.id, + timestamp, + }); + } + false } fn interact<'a>( @@ -277,8 +337,11 @@ impl EntityBase for InteractionEntity { _item_stack: &'a mut ItemStack, ) -> EntityBaseFuture<'a, bool> { Box::pin(async move { - let timestamp = self.entity.world.load().level_time.lock().await.world_age as i64; - *self.interaction.lock().await = Some(PlayerAction { + let timestamp = self.entity.world.load().get_world_age(); + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(PlayerAction { player: player.gameprofile.id, timestamp, }); diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index e060b8f22..d6d239bd1 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -18,13 +18,12 @@ use pumpkin_util::math::vector3::Vector3; use std::sync::atomic::Ordering::{AcqRel, Relaxed}; use std::sync::{ - Arc, + Arc, Mutex, atomic::{ AtomicBool, AtomicU8, AtomicU32, Ordering::{self}, }, }; -use tokio::sync::Mutex; use super::{Entity, EntityBase, NbtFuture, living::LivingEntity, player::Player}; @@ -98,12 +97,12 @@ impl ItemEntity { /// Creates an `ItemEntity` for restoring from NBT without random velocity. /// The velocity and position will be set by `Entity::read_nbt_non_mut`. - pub fn new_for_restore(entity: Entity) -> Self { + pub fn new_empty(entity: Entity) -> Self { Self { entity, item_stack: Mutex::new(ItemStack::new(1, &pumpkin_data::item::Item::AIR)), item_age: AtomicU32::new(0), - pickup_delay: AtomicU8::new(10), + pickup_delay: AtomicU8::new(0), health: AtomicF32::new(5.0), never_despawn: AtomicBool::new(false), never_pickup: AtomicBool::new(false), @@ -114,37 +113,53 @@ impl ItemEntity { &self.item_stack } + pub fn get_pickup_delay(&self) -> u8 { + self.pickup_delay.load(Ordering::Relaxed) + } + + pub fn set_pickup_delay(&self, pickup_delay: u8) { + self.pickup_delay.store(pickup_delay, Ordering::Relaxed); + } + pub const fn get_entity(&self) -> &Entity { &self.entity } - async fn can_merge(&self) -> bool { - if self.never_pickup.load(Ordering::Relaxed) || self.entity.removed.load(Ordering::Relaxed) - { + pub fn can_merge(&self) -> bool { + let Ok(item_stack) = self.item_stack.try_lock() else { return false; - } - - let item_stack = self.item_stack.lock().await; + }; item_stack.item_count < item_stack.get_max_stack_size() } - async fn try_merge(&self) { + pub fn try_merge(&self) { + if !self.can_merge() || self.never_despawn.load(Ordering::Relaxed) { + return; + } + let bounding_box = self.entity.bounding_box.load().expand(0.5, 0.0, 0.5); let world = self.entity.world.load(); let entities = world.entities.load(); - let items = entities.iter().filter_map(|entity: &Arc| { - entity.clone().get_item_entity().filter(|item| { - item.entity.entity_id != self.entity.entity_id - && !item.never_despawn.load(Ordering::Relaxed) - && item.entity.bounding_box.load().intersects(&bounding_box) + let items: Vec> = entities + .iter() + .filter_map(|entity: &Arc| { + entity.clone().get_item_entity().filter(|item| { + item.entity.entity_id != self.entity.entity_id + && !item.never_despawn.load(Ordering::Relaxed) + && item.entity.bounding_box.load().intersects(&bounding_box) + }) }) - }); + .collect(); for item in items { - if item.can_merge().await { - self.try_merge_with(&item).await; + if item.can_merge() { + if let Some(this_base) = world.get_entity_by_id(self.entity.entity_id) + && let Some(this_item) = this_base.get_item_entity() + { + this_item.try_merge_with(&item); + } if self.entity.removed.load(Ordering::SeqCst) { break; @@ -153,7 +168,7 @@ impl ItemEntity { } } - async fn try_merge_with(&self, other: &Self) { + fn try_merge_with(&self, other: &Self) { // Always lock in entity_id order to prevent deadlock when two // items try to merge with each other concurrently. let (low, high) = if self.entity.entity_id < other.entity.entity_id { @@ -162,8 +177,14 @@ impl ItemEntity { (other, self) }; - let low_stack = low.item_stack.lock().await; - let high_stack = high.item_stack.lock().await; + let low_stack = low + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let high_stack = high + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let (self_stack, other_stack) = if self.entity.entity_id < other.entity.entity_id { (low_stack, high_stack) @@ -190,7 +211,7 @@ impl ItemEntity { cancelled: false, }; if let Some(server) = self.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; @@ -239,15 +260,15 @@ impl ItemEntity { } if empty1 { - target.entity.remove().await; + target.entity.remove(); } else { - target.init_data_tracker().await; + target.init_data_tracker(); } if empty2 { - source.entity.remove().await; + source.entity.remove(); } else { - source.init_data_tracker().await; + source.init_data_tracker(); } } @@ -281,19 +302,19 @@ impl ItemEntity { velo } - fn update_no_clip_and_push_out(&self) { + fn update_no_physics_and_push_out(&self) { let entity = &self.entity; let pos = entity.pos.load(); let bounding_box = entity.bounding_box.load(); - let no_clip = !entity + let no_physics = !entity .world .load() .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)); - entity.no_clip.store(no_clip, Ordering::Relaxed); + entity.no_physics.store(no_physics, Ordering::Relaxed); - if no_clip { + if no_physics { entity.push_out_of_blocks(Vector3::new( pos.x, f64::midpoint(bounding_box.min.y, bounding_box.max.y), @@ -302,7 +323,7 @@ impl ItemEntity { } } - async fn should_tick_move(&self, move_velo: Vector3) -> Option { + fn should_tick_move(&self, move_velo: Vector3) -> Option { let entity = &self.entity; let mut tick_move = !entity.on_ground.load(Ordering::SeqCst) @@ -310,7 +331,7 @@ impl ItemEntity { if !tick_move { let Ok(item_age) = i32::try_from(self.item_age.load(Ordering::Relaxed)) else { - entity.remove().await; + entity.remove(); return None; }; @@ -320,16 +341,16 @@ impl ItemEntity { Some(tick_move) } - async fn move_and_apply_friction<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, + fn move_and_apply_friction( + &self, + caller: &Arc, + server: &Server, move_velo: Vector3, ) { let entity = &self.entity; - entity.move_entity(caller, move_velo).await; - entity.tick_block_collisions(caller, server).await; + entity.move_entity(caller, move_velo); + entity.tick_block_collisions(caller, server); let mut friction = 0.98; let on_ground = entity.on_ground.load(Ordering::SeqCst); @@ -349,7 +370,7 @@ impl ItemEntity { entity.velocity.store(velo); } - async fn process_age_and_merge(&self) -> bool { + fn process_age_and_merge(&self) -> bool { if self.never_despawn.load(Ordering::Relaxed) { return true; } @@ -358,20 +379,26 @@ impl ItemEntity { let age = self.item_age.fetch_add(1, Ordering::Relaxed) + 1; if age >= 6000 { - let mut despawn_event = - crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new( - entity.entity_id, - ); - if let Some(server) = entity.world.load().server.upgrade() { - server - .plugin_manager - .fire(&server, &mut despawn_event) - .await; - } - if !despawn_event.cancelled { - entity.remove().await; - return false; - } + let entity_id = entity.entity_id; + let world = entity.world.load_full(); + tokio::spawn(async move { + let mut despawn_event = + crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new( + entity_id, + ); + if let Some(server) = world.server.upgrade() { + server + .plugin_manager + .fire(&server, &mut despawn_event) + .await; + } + if !despawn_event.cancelled + && let Some(e) = world.get_entity_by_id(entity_id) + { + e.get_entity().remove(); + } + }); + return false; } let n = if entity @@ -386,21 +413,17 @@ impl ItemEntity { 2 }; - if age.is_multiple_of(n) && self.can_merge().await { - self.try_merge().await; + if age.is_multiple_of(n) && self.can_merge() { + self.try_merge(); } true } - async fn sync_motion_if_dirty<'a>( - &'a self, - caller: &'a Arc, - original_velo: Vector3, - ) { + fn sync_motion_if_dirty(&self, caller: &Arc, original_velo: Vector3) { let entity = &self.entity; - entity.update_fluid_state(caller).await; + entity.update_fluid_state(caller); let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst) || entity.touching_water.load(Ordering::SeqCst) @@ -425,132 +448,133 @@ impl ItemEntity { } impl EntityBase for ItemEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - self.decrement_pickup_delay(); + fn tick(&self, caller: &Arc, server: &Server) { + let entity = &self.entity; + self.decrement_pickup_delay(); - let original_velo = entity.velocity.load(); - entity - .velocity - .store(self.apply_fluid_drag_or_gravity(original_velo)); + let original_velo = entity.velocity.load(); + entity + .velocity + .store(self.apply_fluid_drag_or_gravity(original_velo)); - self.update_no_clip_and_push_out(); + self.update_no_physics_and_push_out(); - let move_velo = entity.velocity.load(); // In case push_out_of_blocks modifies it + let move_velo = entity.velocity.load(); // In case push_out_of_blocks modifies it - let Some(tick_move) = self.should_tick_move(move_velo).await else { - return; - }; + let Some(tick_move) = self.should_tick_move(move_velo) else { + return; + }; - if tick_move { - self.move_and_apply_friction(caller, server, move_velo) - .await; - } + if tick_move { + self.move_and_apply_friction(caller, server, move_velo); + } - if self.process_age_and_merge().await { - self.sync_motion_if_dirty(caller, original_velo).await; - } - }) + if self.process_age_and_merge() { + self.sync_motion_if_dirty(caller, original_velo); + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item::ITEM, - &ItemStackSerializer::from(self.item_stack.lock().await.clone()), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::item::ITEM, + &ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), + )], + None, + ); } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, amount: f32, damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - // Check if entity is fire_immune - let is_fire_damage = damage_type == DamageType::IN_FIRE - || damage_type == DamageType::ON_FIRE - || damage_type == DamageType::LAVA; - if is_fire_damage && self.entity.fire_immune.load(Ordering::Relaxed) { - return false; - } + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + // Check if entity is fire_immune + let is_fire_damage = damage_type == DamageType::IN_FIRE + || damage_type == DamageType::ON_FIRE + || damage_type == DamageType::LAVA; + if is_fire_damage && self.entity.fire_immune.load(Ordering::Relaxed) { + return false; + } - loop { - let current = self.health.load(Relaxed); - let new = current - amount; - if self - .health - .compare_exchange(current, new, AcqRel, Relaxed) - .is_ok() - { - if new <= 0.0 { - self.entity.remove().await; - } - return true; + loop { + let current = self.health.load(Relaxed); + let new = current - amount; + if self + .health + .compare_exchange(current, new, AcqRel, Relaxed) + .is_ok() + { + if new <= 0.0 { + self.entity.remove(); } + return true; } - }) + } } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async { - if self.pickup_delay.load(Ordering::Relaxed) > 0 - || player.living_entity.health.load() <= 0.0 - || player.is_spectator() - { - return; + fn on_player_collision(&self, player: &Arc) { + if self.pickup_delay.load(Ordering::Relaxed) > 0 + || player.living_entity.health.load() <= 0.0 + || player.is_spectator() + { + return; + } + + let (item_id, count_before) = { + let stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (stack.item.id, stack.item_count) + }; + + let mut local_stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let inserted = player.inventory.insert_stack_anywhere(&mut local_stack); + let count_after = local_stack.item_count; + let is_empty = local_stack.is_empty(); + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = local_stack; + + if inserted || player.is_creative() { + player.inventory_changed.store(true, Ordering::Relaxed); + + let amount_picked_up = if player.is_creative() { + count_before + } else { + count_before - count_after + }; + + if amount_picked_up > 0 { + player.increment_stat( + StatisticCategory::PickedUp, + item_id as i32, + amount_picked_up as i32, + ); } - let (item_id, count_before) = { - let stack = self.item_stack.lock().await; - (stack.item.id, stack.item_count) - }; + player + .living_entity + .pickup(&self.entity, amount_picked_up.into()); - let inserted = { - let mut stack = self.item_stack.lock().await; - player.inventory.insert_stack_anywhere(&mut stack).await - }; - - if inserted || player.is_creative() { - let (count_after, is_empty) = { - let stack = self.item_stack.lock().await; - (stack.item_count, stack.is_empty()) - }; - - let amount_picked_up = if player.is_creative() { - count_before - } else { - count_before - count_after - }; - - if amount_picked_up > 0 { - player - .increment_stat( - StatisticCategory::PickedUp, - item_id as i32, - amount_picked_up as i32, - ) - .await; - } - - player - .living_entity - .pickup(&self.entity, amount_picked_up.into()); - - player + let player_clone = player.clone(); + tokio::spawn(async move { + player_clone .current_screen_handler .lock() .await @@ -558,14 +582,14 @@ impl EntityBase for ItemEntity { .await .send_content_updates() .await; + }); - if is_empty { - self.entity.remove().await; - } else { - self.init_data_tracker().await; - } + if is_empty { + self.entity.remove(); + } else { + self.init_data_tracker(); } - }) + } } fn get_entity(&self) -> &Entity { @@ -586,7 +610,10 @@ impl EntityBase for ItemEntity { fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { - let item = self.item_stack.lock().await; + let item = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut item_compound = NbtCompound::new(); item.write_item_stack(&mut item_compound); nbt.put_compound("Item", item_compound); @@ -606,7 +633,10 @@ impl EntityBase for ItemEntity { if let Some(item_compound) = nbt.get_compound("Item") && let Some(stack) = ItemStack::read_item_stack(item_compound) { - *self.item_stack.lock().await = stack; + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; } // Vanilla stores Age as a short @@ -636,17 +666,23 @@ impl EntityBase for ItemEntity { Box::pin(async move { let entity = &self.entity; let runtime_id = entity.entity_id as u64; - let item_stack = self.item_stack.lock().await; - let packet = CAddItemActor { - target_actor_id: VarLong(runtime_id as i64), - target_runtime_id: VarULong(runtime_id), - item: ItemStackWrapper::from(&*item_stack), - position: entity.pos.load().to_f32_lossy(), - velocity: entity.velocity.load().to_f32_lossy(), - entity_data: entity.bedrock_metadata(), - is_from_fishing: false, + let data = { + let item_stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let packet = CAddItemActor { + target_actor_id: VarLong(runtime_id as i64), + target_runtime_id: VarULong(runtime_id), + item: ItemStackWrapper::from(&*item_stack), + position: entity.pos.load().to_f32_lossy(), + velocity: entity.velocity.load().to_f32_lossy(), + entity_data: entity.bedrock_metadata(), + is_from_fishing: false, + }; + client.serialize_packet(&packet).ok() }; - if let Ok(data) = client.serialize_packet(&packet) { + if let Some(data) = data { client.send_game_packet(data).await; } }) @@ -665,7 +701,12 @@ impl EntityBase for ItemEntity { if client.version.load() >= CURRENT_MC_VERSION { let metadata = Metadata::new( pumpkin_data::tracked_data::item::ITEM, - ItemStackSerializer::from(self.item_stack.lock().await.clone()), + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), ); let mut data = Vec::new(); if metadata.write(&mut data, &client.version.load()).is_ok() { diff --git a/crates/pumpkin/src/entity/lightning.rs b/crates/pumpkin/src/entity/lightning.rs index 5524795ca..c851198e4 100644 --- a/crates/pumpkin/src/entity/lightning.rs +++ b/crates/pumpkin/src/entity/lightning.rs @@ -1,7 +1,8 @@ use std::collections::HashSet; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, Ordering}; -use tokio::sync::Mutex; +use std::sync::{ + Arc, Mutex, + atomic::{AtomicBool, AtomicI32, AtomicI64, Ordering}, +}; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::world::WorldEvent; @@ -15,7 +16,7 @@ use rand::RngExt; use crate::block::blocks::fire::FireBlockBase; use crate::entity::player::Player; -use crate::entity::{Entity, EntityBase, EntityBaseFuture}; +use crate::entity::{Entity, EntityBase}; use crate::server::Server; use crate::world::World; @@ -32,6 +33,7 @@ pub struct LightningBoltEntity { impl LightningBoltEntity { pub fn new(entity: Entity) -> Self { + entity.no_physics.store(true, Ordering::Relaxed); let seed = rand::rng().random::(); let flashes = rand::rng().random_range(1..=3); Self { @@ -54,12 +56,14 @@ impl LightningBoltEntity { self.visual_only.load(Ordering::Relaxed) } - pub async fn set_cause(&self, cause: Option>) { - *self.cause.lock().await = cause; + pub fn set_cause(&self, cause: Option>) { + if let Ok(mut c) = self.cause.lock() { + *c = cause; + } } - pub async fn get_cause(&self) -> Option> { - self.cause.lock().await.clone() + pub fn get_cause(&self) -> Option> { + self.cause.lock().ok().and_then(|c| c.clone()) } pub fn get_blocks_set_on_fire(&self) -> i32 { @@ -79,47 +83,44 @@ impl LightningBoltEntity { Vector3::new(pos.x, pos.y - 1.0e-6, pos.z).to_block_pos() } - async fn power_lightning_rod(&self, world: &Arc) { + fn power_lightning_rod(&self, world: &Arc) { let strike_pos = self.get_strike_position(); let block = world.get_block(&strike_pos); if block == &Block::LIGHTNING_ROD { crate::block::blocks::redstone::lightning_rod::LightningRodBlock::trigger( world, &strike_pos, - ) - .await; + ); } } - async fn spawn_fire(&self, world: &Arc, additional_sources: i32) { + fn spawn_fire(&self, world: &Arc, additional_sources: i32) { if self.visual_only.load(Ordering::Relaxed) { return; } let pos = self.entity.block_pos.load(); - let try_place = |p: BlockPos| async move { + let try_place = |p: BlockPos| { if world.get_block_state(&p).is_air() && FireBlockBase::can_place_at(world, &p) { let fire_block = FireBlockBase::get_fire_type(world, &p); - world - .set_block_state(&p, fire_block.default_state.id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&p, fire_block.default_state.id, BlockFlags::NOTIFY_ALL); self.blocks_set_on_fire.fetch_add(1, Ordering::Relaxed); } }; - try_place(pos).await; + try_place(pos); for _ in 0..additional_sources { let dx = rand::rng().random_range(-1..=1); let dy = rand::rng().random_range(-1..=1); let dz = rand::rng().random_range(-1..=1); let nearby_pos = pos.offset(Vector3::new(dx, dy, dz)); - try_place(nearby_pos).await; + try_place(nearby_pos); } } - async fn clear_copper_on_lightning_strike(&self, world: &Arc) { + fn clear_copper_on_lightning_strike(&self, world: &Arc) { let strike_pos = self.get_strike_position(); let struck_state = world.get_block_state(&strike_pos); let struck_block = struck_state.id.to_block(); @@ -137,23 +138,19 @@ impl LightningBoltEntity { let new_state_id = BlockStateId::new(first_block.default_state.id.as_u16() + offset) .unwrap_or(first_block.default_state.id); - world - .set_block_state(&strike_pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&strike_pos, new_state_id, BlockFlags::NOTIFY_ALL); } let strikes_count = rand::rng().random_range(3..=5); for _ in 0..strikes_count { let step_count = rand::rng().random_range(1..=8); - self.random_walk_cleaning_copper(world, &strike_pos, step_count) - .await; + Self::random_walk_cleaning_copper(world, &strike_pos, step_count); } } } - async fn random_walk_cleaning_copper( - &self, + fn random_walk_cleaning_copper( world: &Arc, original_strike_pos: &BlockPos, step_count: i32, @@ -161,7 +158,7 @@ impl LightningBoltEntity { let mut work_pos = *original_strike_pos; for _ in 0..step_count { - if let Some(next_pos) = self.random_step_cleaning_copper(world, &work_pos).await { + if let Some(next_pos) = Self::random_step_cleaning_copper(world, &work_pos) { work_pos = next_pos; } else { break; @@ -169,11 +166,7 @@ impl LightningBoltEntity { } } - async fn random_step_cleaning_copper( - &self, - world: &Arc, - pos: &BlockPos, - ) -> Option { + fn random_step_cleaning_copper(world: &Arc, pos: &BlockPos) -> Option { let candidates = random_in_cube(10, pos, 1); for candidate in candidates { let state = world.get_block_state(&candidate); @@ -189,9 +182,7 @@ impl LightningBoltEntity { .saturating_sub(block.default_state.id.as_u16()); let new_state_id = BlockStateId::new(prev_block.default_state.id.as_u16() + offset) .unwrap_or(prev_block.default_state.id); - world - .set_block_state(&candidate, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&candidate, new_state_id, BlockFlags::NOTIFY_ALL); world.sync_world_event(WorldEvent::ParticlesElectricSpark, candidate, -1); return Some(candidate); } @@ -201,92 +192,90 @@ impl LightningBoltEntity { } impl EntityBase for LightningBoltEntity { - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - let life = self.life.load(Ordering::Relaxed); + fn tick(&self, caller: &Arc, _server: &Server) { + let entity = &self.entity; + let life = self.life.load(Ordering::Relaxed); - if life == 2 { - let world = entity.world.load(); - let pos = entity.pos.load(); + if life == 2 { + let world = entity.world.load(); + let pos = entity.pos.load(); - let pitch_thunder = 0.8 + rand::rng().random::() * 0.2; - world.play_sound_fine( - Sound::EntityLightningBoltThunder, - SoundCategory::Weather, - &pos, - 10000.0, - pitch_thunder, - ); - let pitch_impact = 0.5 + rand::rng().random::() * 0.2; - world.play_sound_fine( - Sound::EntityLightningBoltImpact, - SoundCategory::Weather, - &pos, - 2.0, - pitch_impact, - ); + let pitch_thunder = 0.8 + rand::rng().random::() * 0.2; + world.play_sound_fine( + Sound::EntityLightningBoltThunder, + SoundCategory::Weather, + &pos, + 10000.0, + pitch_thunder, + ); + let pitch_impact = 0.5 + rand::rng().random::() * 0.2; + world.play_sound_fine( + Sound::EntityLightningBoltImpact, + SoundCategory::Weather, + &pos, + 2.0, + pitch_impact, + ); - let difficulty = world.level_info.load().difficulty; - if difficulty == Difficulty::Normal || difficulty == Difficulty::Hard { - self.spawn_fire(&world, 4).await; - } - - self.power_lightning_rod(&world).await; - self.clear_copper_on_lightning_strike(&world).await; + let difficulty = world.level_info.load().difficulty; + if difficulty == Difficulty::Normal || difficulty == Difficulty::Hard { + self.spawn_fire(&world, 4); } - let new_life = life - 1; - self.life.store(new_life, Ordering::Relaxed); + self.power_lightning_rod(&world); + self.clear_copper_on_lightning_strike(&world); + } - if new_life < 0 { - let flashes = self.flashes.load(Ordering::Relaxed); - if flashes == 0 { - entity.remove().await; - return; - } else if new_life < -rand::rng().random_range(0..10) { - self.flashes.store(flashes - 1, Ordering::Relaxed); - self.life.store(1, Ordering::Relaxed); - self.seed.store(rand::random::(), Ordering::Relaxed); - let world = entity.world.load(); - self.spawn_fire(&world, 0).await; - } - } + let new_life = life - 1; + self.life.store(new_life, Ordering::Relaxed); - let current_life = self.life.load(Ordering::Relaxed); - if current_life >= 0 && !self.visual_only.load(Ordering::Relaxed) { + if new_life < 0 { + let flashes = self.flashes.load(Ordering::Relaxed); + if flashes == 0 { + entity.remove(); + return; + } else if new_life < -rand::rng().random_range(0..10) { + self.flashes.store(flashes - 1, Ordering::Relaxed); + self.life.store(1, Ordering::Relaxed); + self.seed.store(rand::random::(), Ordering::Relaxed); let world = entity.world.load(); - let pos = entity.pos.load(); + self.spawn_fire(&world, 0); + } + } - let damage_box = BoundingBox::new( - Vector3::new(pos.x - 3.0, pos.y - 3.0, pos.z - 3.0), - Vector3::new(pos.x + 3.0, pos.y + 9.0, pos.z + 3.0), - ); + let current_life = self.life.load(Ordering::Relaxed); + if current_life >= 0 && !self.visual_only.load(Ordering::Relaxed) { + let world = entity.world.load(); + let pos = entity.pos.load(); - let entities = world.get_all_at_box(&damage_box); - let mut hit_guard = self.hit_entities.lock().await; + let damage_box = BoundingBox::new( + Vector3::new(pos.x - 3.0, pos.y - 3.0, pos.z - 3.0), + Vector3::new(pos.x + 3.0, pos.y + 9.0, pos.z + 3.0), + ); + let entities = world.get_all_at_box(&damage_box); + if let Ok(mut hit_guard) = self.hit_entities.lock() { for hit_entity in entities { if hit_entity.get_entity().entity_id == entity.entity_id { continue; } let hit_id = hit_entity.get_entity().entity_id; - hit_entity - .on_lightning_strike(hit_entity.as_ref(), self) - .await; - hit_guard.insert(hit_id); + if hit_guard.insert(hit_id) { + let caller_clone = caller.clone(); + let target = hit_entity.clone(); + tokio::spawn(async move { + if let Some(lightning) = caller_clone.cast_any().downcast_ref::() + { + target.on_lightning_strike(target.as_ref(), lightning).await; + } + }); + } } } - }) + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn init_data_tracker(&self) {} fn get_entity(&self) -> &Entity { &self.entity diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index fd2bd62a0..92f1973bb 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -31,7 +31,7 @@ use crate::entity::combat::knockback_after_resistance; use crate::entity::mob::equipment::DEFAULT_EQUIPMENT_DROP_CHANCE; use crate::entity::mob::slime::SlimeEntity; use crate::entity::player::statistics::{CustomStatistic, StatisticCategory}; -use crate::entity::{EntityBaseFuture, NBTStorage, NbtFuture}; +use crate::entity::{NBTStorage, NbtFuture}; use crate::server::Server; use crate::world::loot::{LootContextParameters, LootTableExt}; use crossbeam::atomic::AtomicCell; @@ -55,7 +55,7 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::{ - CEntityStatus, CHurtAnimation, CSetPlayerInventory, CTakeItemEntity, CUpdateMobEffect, + CHurtAnimation, CSetPlayerInventory, CTakeItemEntity, CUpdateMobEffect, }; use pumpkin_protocol::{ codec::item_stack_seralizer::ItemStackSerializer, @@ -67,7 +67,6 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; use rand::RngExt; use std::sync::RwLock; -use tokio::sync::Mutex; /// Represents a living entity within the game world. /// @@ -84,16 +83,16 @@ pub struct LivingEntity { /// The current absorption (yellow hearts) on the entity. pub absorption: AtomicCell, pub item_use_time: AtomicI32, - pub item_in_use: Mutex>, - pub active_hand: Mutex>, + pub item_in_use: std::sync::Mutex>, + pub active_hand: std::sync::Mutex>, pub death_time: AtomicU8, /// Indicates whether the entity is dead. (`on_death` called) pub dead: AtomicBool, /// The distance the entity has been falling. pub fall_distance: AtomicCell, - pub active_effects: Mutex>, - pub entity_equipment: Arc>, - pub equipment_drop_chances: Arc>>, + pub active_effects: std::sync::Mutex>, + pub entity_equipment: Arc>, + pub equipment_drop_chances: Arc>>, pub movement_input: AtomicCell>, pub equipment_slots: Arc>, @@ -206,12 +205,12 @@ impl LivingEntity { death_time: AtomicU8::new(0), dead: AtomicBool::new(false), item_use_time: AtomicI32::new(0), - item_in_use: Mutex::new(None), - active_hand: Mutex::new(None), + item_in_use: std::sync::Mutex::new(None), + active_hand: std::sync::Mutex::new(None), livings_flags: AtomicU8::new(0), - active_effects: Mutex::new(HashMap::new()), - entity_equipment: Arc::new(Mutex::new(EntityEquipment::new())), - equipment_drop_chances: Arc::new(Mutex::new(HashMap::new())), + active_effects: std::sync::Mutex::new(HashMap::new()), + entity_equipment: Arc::new(std::sync::Mutex::new(EntityEquipment::new())), + equipment_drop_chances: Arc::new(std::sync::Mutex::new(HashMap::new())), equipment_slots: Arc::new(build_equipment_slots()), jumping: AtomicBool::new(false), jumping_cooldown: AtomicU8::new(0), @@ -259,14 +258,11 @@ impl LivingEntity { selected_slot: 0, container_id: window_id, }; - self.entity - .world - .load() - .broadcast_packet_except_editioned_sync( - &[self.entity.entity_uuid], - &je_packet, - &be_packet, - ); + self.entity.world.load().broadcast_packet_except_editioned( + &[self.entity.entity_uuid], + &je_packet, + &be_packet, + ); sent_editioned = true; } } @@ -288,18 +284,16 @@ impl LivingEntity { stack_amount as u8, ); if let Some(server) = self.entity.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut pickup_event).await; - }); - }); + server + .plugin_manager + .fire_blocking(&server, &mut pickup_event); if pickup_event.cancelled { return; } } let chunk_pos = self.entity.chunk_pos.load(); - self.entity.world.load().broadcast_to_chunk_editioned_sync( + self.entity.world.load().broadcast_to_chunk_editioned( chunk_pos, &CTakeItemEntity::new( item.entity_id.into(), @@ -314,10 +308,16 @@ impl LivingEntity { } /// Sends the Hand animation to all others, used when Eating for example - pub async fn set_active_hand(&self, hand: Hand, stack: ItemStack, duration: i32) { + pub fn set_active_hand(&self, hand: Hand, stack: ItemStack, duration: i32) { self.item_use_time.store(duration, Ordering::Relaxed); - *self.item_in_use.lock().await = Some(stack); - *self.active_hand.lock().await = Some(hand); + *self + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(stack); + *self + .active_hand + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(hand); self.set_living_flag(Self::USING_ITEM_FLAG, true); self.set_living_flag(Self::OFF_HAND_ACTIVE_FLAG, hand == Hand::Left); } @@ -364,16 +364,25 @@ impl LivingEntity { ); } - pub async fn clear_active_hand(&self) { - *self.item_in_use.lock().await = None; - *self.active_hand.lock().await = None; + pub fn clear_active_hand(&self) { + *self + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + *self + .active_hand + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; self.item_use_time.store(0, Ordering::Relaxed); self.set_living_flag(Self::USING_ITEM_FLAG, false); } - pub async fn is_blocking(&self) -> bool { - let item_in_use = self.item_in_use.lock().await; + pub fn is_blocking(&self) -> bool { + let item_in_use = self + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(item) = item_in_use.as_ref() && item.get_data_component::().is_some() { @@ -407,11 +416,7 @@ impl LivingEntity { additional_health, ); if let Some(server) = self.entity.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut event).await; - }); - }); + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -440,7 +445,7 @@ impl LivingEntity { } /// Sets the maximum health for this entity - pub async fn set_max_health(&self, max_health: f32) { + pub fn set_max_health(&self, max_health: f32) { // Update base attribute self.set_attribute_base(&Attributes::MAX_HEALTH, max_health as f64); @@ -448,8 +453,7 @@ impl LivingEntity { crate::entity::attributes::send_attribute_updates_for_living( self, vec![Attributes::MAX_HEALTH], - ) - .await; + ); // Clamp current health to new max if needed and send metadata update let current_health = self.health.load(); @@ -464,7 +468,7 @@ impl LivingEntity { } /// Sets the current absorption amount for this entity (yellow hearts) - pub async fn set_absorption(&self, new_abs: f32) { + pub fn set_absorption(&self, new_abs: f32) { // Must be at least 0 let new_abs = new_abs.max(0.0); @@ -476,8 +480,7 @@ impl LivingEntity { crate::entity::attributes::send_attribute_updates_for_living( self, vec![Attributes::MAX_ABSORPTION], - ) - .await; + ); // Send absorption metadata for players (visual yellow hearts) if self.entity.entity_type == &EntityType::PLAYER { @@ -577,15 +580,18 @@ impl LivingEntity { } } - pub async fn reset_effects_and_attributes(&self) { + pub fn reset_effects_and_attributes(&self) { // Clear active effects and reset modified attributes let effects_to_remove: Vec<_> = { - let lock = self.active_effects.lock().await; + let lock = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); lock.keys().copied().collect() }; for effect_type in effects_to_remove { - self.remove_effect(effect_type).await; + self.remove_effect(effect_type); } } @@ -594,7 +600,7 @@ impl LivingEntity { } #[expect(clippy::too_many_lines)] - pub async fn add_effect(&self, effect: Effect) { + pub fn add_effect(&self, effect: Effect) { let mut effect_event = crate::plugin::api::events::entity::entity_potion_effect::EntityPotionEffectEvent::new( self.entity.entity_id, @@ -603,7 +609,9 @@ impl LivingEntity { effect.amplifier, ); if let Some(server) = self.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut effect_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut effect_event); } if effect_event.cancelled { return; @@ -621,16 +629,10 @@ impl LivingEntity { .load() .get_entity_by_id(self.entity.entity_id); if let Some(dyn_self) = dyn_self { - dyn_self - .damage(&*dyn_self, damage_amount, DamageType::MAGIC) - .await; + let _ = dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC); } } else { // Apply non-instant effects - self.active_effects - .lock() - .await - .insert(effect.effect_type, effect.clone()); // Effects that modify attributes (ex. speed) should also update the // entity's attribute instances (server-side) and then notify clients. @@ -667,8 +669,7 @@ impl LivingEntity { crate::entity::attributes::send_attribute_updates_for_living( self, touched_attrs, - ) - .await; + ); } } @@ -677,17 +678,17 @@ impl LivingEntity { let added = 4.0 * (effect.amplifier as f32 + 1.0); let max_abs = self.get_attribute_value(&Attributes::MAX_ABSORPTION) as f32; let new_abs = (self.absorption.load() + added).min(max_abs); - self.set_absorption(new_abs).await; + self.set_absorption(new_abs); } // Apply invisible effect if effect.effect_type == &StatusEffect::INVISIBILITY { - self.entity.set_invisible(true).await; + self.entity.set_invisible(true); } // Apply glowing effect if effect.effect_type == &StatusEffect::GLOWING { - self.entity.set_glowing(true).await; + self.entity.set_glowing(true); } } @@ -729,12 +730,23 @@ impl LivingEntity { self.entity .world .load() - .broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet); - self.sync_effect_particles().await; + .broadcast_to_chunk_editioned(chunk_pos, &je_packet, &be_packet); + if effect.effect_type != &StatusEffect::INSTANT_HEALTH + && effect.effect_type != &StatusEffect::INSTANT_DAMAGE + { + self.active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .insert(effect.effect_type, effect); + } + self.sync_effect_particles(); } - async fn sync_effect_particles(&self) { - let effects = self.active_effects.lock().await; + fn sync_effect_particles(&self) { + let effects = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let has_effects = !effects.is_empty(); let particles = EffectParticles( effects @@ -767,12 +779,12 @@ impl LivingEntity { } } - pub async fn remove_effect(&self, effect_type: &'static StatusEffect) -> bool { + pub fn remove_effect(&self, effect_type: &'static StatusEffect) -> bool { // Remove the effect let succeeded = self .active_effects .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .remove(&effect_type) .is_some(); @@ -805,14 +817,13 @@ impl LivingEntity { // Sync the clean state to the client if !touched_attrs.is_empty() { - crate::entity::attributes::send_attribute_updates_for_living(self, touched_attrs) - .await; + crate::entity::attributes::send_attribute_updates_for_living(self, touched_attrs); } } // If absorption effect removed, clear current absorption amount and notify clients if effect_type == &StatusEffect::ABSORPTION { - self.set_absorption(0.0).await; + self.set_absorption(0.0); } // If health boost effect removed, clamp current health to new max and notify clients @@ -826,29 +837,34 @@ impl LivingEntity { // If invisible effect removed, disable invisibility if effect_type == &StatusEffect::INVISIBILITY { - self.entity.set_invisible(false).await; + self.entity.set_invisible(false); } // If glowing effect removed, disable glowing if effect_type == &StatusEffect::GLOWING { - self.entity.set_glowing(false).await; + self.entity.set_glowing(false); } if succeeded { - self.sync_effect_particles().await; + self.sync_effect_particles(); } succeeded } - pub async fn has_effect(&self, effect: &'static StatusEffect) -> bool { - let effects = self.active_effects.lock().await; - effects.contains_key(&effect) + pub fn has_effect(&self, effect: &'static StatusEffect) -> bool { + self.active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .contains_key(&effect) } - pub async fn get_effect(&self, effect: &'static StatusEffect) -> Option { - let effects = self.active_effects.lock().await; - effects.get(&effect).cloned() + pub fn get_effect(&self, effect: &'static StatusEffect) -> Option { + self.active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(&effect) + .cloned() } pub fn is_in_fall_damage_resetting(&self) -> (bool, &Block) { @@ -862,14 +878,12 @@ impl LivingEntity { // Check if the entity is in water pub fn is_in_water(&self) -> bool { - let block_pos = self.entity.block_pos.load(); - self.entity.world.load().get_block(&block_pos) == &Block::WATER + self.entity.touching_water.load(Ordering::Relaxed) } // Check if the entity is in powder snow pub fn is_in_powder_snow(&self) -> bool { - let block_pos = self.entity.block_pos.load(); - self.entity.world.load().get_block(&block_pos) == &Block::POWDER_SNOW + self.entity.is_in_powder_snow.load(Ordering::Relaxed) } pub fn should_prevent_fall_damage(&self) -> bool { @@ -944,19 +958,17 @@ impl LivingEntity { .has_tag(&tag::EntityType::MINECRAFT_FALL_DAMAGE_IMMUNE) } - async fn get_effective_gravity(&self, caller: &Arc) -> f64 { + fn get_effective_gravity(&self, caller: &Arc) -> f64 { let final_gravity = caller.get_gravity(); - if self.entity.velocity.load().y <= 0.0 - && self.has_effect(&StatusEffect::SLOW_FALLING).await - { + if self.entity.velocity.load().y <= 0.0 && self.has_effect(&StatusEffect::SLOW_FALLING) { final_gravity.min(0.01) } else { final_gravity } } - pub async fn swing_hand(&self) { + pub fn swing_hand(&self) { let world = self.entity.world.load(); let entity_id = self.entity_id(); @@ -971,19 +983,15 @@ impl LivingEntity { swing_source: None, }; - world.broadcast_editioned(&je_packet, &be_packet).await; + world.broadcast_editioned(&je_packet, &be_packet); } - async fn tick_movement<'a>(&'a self, server: &'a Server, caller: &'a Arc) { + fn tick_movement(&self, server: &Server, caller: &Arc) { if self.jumping_cooldown.load(Relaxed) != 0 { self.jumping_cooldown.fetch_sub(1, Relaxed); } - let should_swim_in_fluids = if let Some(player) = caller.get_player() { - !player.is_flying().await - } else { - true - }; + let should_swim_in_fluids = caller.get_player().is_none_or(|player| !player.is_flying()); self.entity.check_zero_velo(); @@ -1023,7 +1031,7 @@ impl LivingEntity { } else if (on_ground || in_water && fluid_height <= swim_height) && self.jumping_cooldown.load(SeqCst) == 0 { - self.jump().await; + self.jump(); self.jumping_cooldown.store(10, SeqCst); } @@ -1031,8 +1039,8 @@ impl LivingEntity { self.jumping_cooldown.store(0, SeqCst); } - if self.has_effect(&StatusEffect::SLOW_FALLING).await - || self.has_effect(&StatusEffect::LEVITATION).await + if self.has_effect(&StatusEffect::SLOW_FALLING) + || self.has_effect(&StatusEffect::LEVITATION) { self.fall_distance.store(0.0); } @@ -1045,29 +1053,26 @@ impl LivingEntity { && should_swim_in_fluids && self.entity.entity_type != &EntityType::STRIDER { - self.travel_in_fluid(caller, touching_water).await; + self.travel_in_fluid(caller, touching_water); } else { // TODO: Gliding - self.travel_in_air(caller).await; + self.travel_in_air(caller); } - // 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); if suffocating { - self.damage(&**caller, 1.0, DamageType::IN_WALL).await; + caller.damage(&**caller, 1.0, DamageType::IN_WALL); } } - async fn travel_in_air<'a>(&'a self, caller: &'a Arc) { + fn travel_in_air<'a>(&'a self, caller: &'a Arc) { // applyMovementInput let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED); - let (speed, friction) = if self.entity.on_ground.load(SeqCst) { + let (speed, friction) = if self.entity.on_ground.load(Relaxed) { // getVelocityAffectingPos let slipperiness = f64::from( @@ -1082,13 +1087,9 @@ impl LivingEntity { (speed, slipperiness * 0.91) } else { - let speed = if let Some(player) = caller.get_player() { - player.get_off_ground_speed().await - } else { - // TODO: If the passenger is a player, ogs = movement_speed * 0.1 - - 0.02 - }; + let speed = caller + .get_player() + .map_or(0.02, super::player::Player::get_off_ground_speed); (speed, 0.91) }; @@ -1098,12 +1099,12 @@ impl LivingEntity { self.apply_climbing_speed(); - self.make_move(caller).await; + self.make_move(caller); let mut velo = self.entity.velocity.load(); let can_powder_snow_climb = if self.entity.was_in_powder_snow.load(Relaxed) { - crate::block::blocks::powder_snow::can_entity_walk_on_powder_snow(caller.as_ref()).await + crate::block::blocks::powder_snow::can_entity_walk_on_powder_snow(caller.as_ref()) } else { false }; @@ -1114,12 +1115,12 @@ impl LivingEntity { velo.y = 0.2; } - let levitation = self.get_effect(&StatusEffect::LEVITATION).await; + let levitation = self.get_effect(&StatusEffect::LEVITATION); 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); // TODO: If world is not loaded: replace effective gravity with: @@ -1143,11 +1144,11 @@ impl LivingEntity { self.entity.velocity.store(velo); } - async fn travel_in_fluid<'a>(&'a self, caller: &'a Arc, water: bool) { + 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); let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED); if water { @@ -1172,14 +1173,14 @@ impl LivingEntity { speed += (effective_speed - speed) * water_movement_efficiency; } - if self.has_effect(&StatusEffect::DOLPHINS_GRACE).await { + if self.has_effect(&StatusEffect::DOLPHINS_GRACE) { friction = 0.96; } self.entity .update_velocity_from_input(movement_input, speed); - self.make_move(caller).await; + self.make_move(caller); let mut velo = self.entity.velocity.load(); if self.entity.horizontal_collision.load(SeqCst) && self.climbing.load(Relaxed) { @@ -1193,7 +1194,7 @@ impl LivingEntity { } else { self.entity.update_velocity_from_input(movement_input, 0.02); - self.make_move(caller).await; + self.make_move(caller); let mut velo = self.entity.velocity.load(); @@ -1239,10 +1240,8 @@ impl LivingEntity { } } - async fn make_move<'a>(&'a self, caller: &'a Arc) { - self.entity - .move_entity(caller, self.entity.velocity.load()) - .await; + fn make_move<'a>(&'a self, caller: &'a Arc) { + self.entity.move_entity(caller, self.entity.velocity.load()); self.check_climbing(); } @@ -1364,8 +1363,8 @@ impl LivingEntity { } } - async fn jump(&self) { - let jump = self.get_jump_velocity(1.0).await; + fn jump(&self) { + let jump = self.get_jump_velocity(1.0); if jump <= 1.0e-5 { return; @@ -1387,18 +1386,18 @@ impl LivingEntity { self.entity.velocity_dirty.store(true, SeqCst); } - async fn get_jump_velocity(&self, mut strength: f64) -> f64 { + fn get_jump_velocity(&self, mut strength: f64) -> f64 { strength *= self.get_attribute_value(&Attributes::JUMP_STRENGTH); strength *= f64::from(self.entity.get_jump_velocity_multiplier()); - if let Some(effect) = self.get_effect(&StatusEffect::JUMP_BOOST).await { + if let Some(effect) = self.get_effect(&StatusEffect::JUMP_BOOST) { strength += 0.1 * f64::from(effect.amplifier + 1); } strength } - pub async fn fall( + pub fn fall( &self, - caller: Arc, + caller: &dyn EntityBase, height_difference: f64, ground: bool, dont_damage: bool, @@ -1417,15 +1416,13 @@ impl LivingEntity { let block = world.get_block(&self.entity.get_pos_with_y_offset(0.2).0); let pumpkin_block = world.block_registry.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_landed_upon(OnLandedUponArgs { - world: &world, - fall_distance, - entity: caller.as_ref(), - }) - .await; + pumpkin_block.on_landed_upon(OnLandedUponArgs { + world: &world, + fall_distance, + entity: caller, + }); } else { - self.handle_fall_damage(&*caller, fall_distance, 1.0).await; + self.handle_fall_damage(caller, fall_distance, 1.0); } } else if height_difference < 0.0 { let new_fall_distance = if !self.should_prevent_fall_damage() @@ -1440,17 +1437,15 @@ impl LivingEntity { } } - pub async fn handle_fall_damage( + pub fn handle_fall_damage( &self, caller: &dyn EntityBase, fall_distance: f32, damage_per_distance: f32, ) { - let may_fly = if let Some(player) = caller.get_player() { - player.abilities.lock().await.allow_flying - } else { - false - }; + let may_fly = caller + .get_player() + .is_some_and(|player| player.abilities.blocking_lock().allow_flying); if may_fly || self.is_immune_to_fall_damage() { return; } @@ -1461,7 +1456,7 @@ impl LivingEntity { let damage = (unsafe_fall_distance * damage_per_distance).floor(); if damage > 0.0 { - let check_damage = self.damage(caller, damage, DamageType::FALL).await; // Fall + let check_damage = self.damage(caller, damage, DamageType::FALL); // Fall if check_damage { self.entity .play_sound(Self::get_fall_sound(fall_distance as i32)); @@ -1541,7 +1536,7 @@ impl LivingEntity { self.jumping.store(false, Relaxed); // Statistics updates - self.update_death_stats(&*dyn_self, cause).await; + self.update_death_stats(&*dyn_self, cause); // Plays the death sound world.send_entity_status(&self.entity, EntityStatus::Death, Some(ActorEventID::Death)); @@ -1553,8 +1548,7 @@ impl LivingEntity { { let hand_stack = player .inventory() - .get_stack_in_hand(pumpkin_util::Hand::Right) - .await; + .get_stack_in_hand(pumpkin_util::Hand::Right); looting_level = hand_stack .get_enchantment_level(&Enchantment::LOOTING) .max(0) as u32; @@ -1568,8 +1562,8 @@ impl LivingEntity { None }; - let is_raining = world.is_raining().await; - let is_thundering = world.is_thundering().await; + let is_raining = world.is_raining(); + let is_thundering = world.is_thundering(); let params = LootContextParameters { killed_by_player: cause.map(|c| c.get_entity().entity_type == &EntityType::PLAYER), @@ -1592,7 +1586,7 @@ impl LivingEntity { }; // Drop loot - self.drop_loot(params.clone()).await; + self.drop_loot(params.clone()); // Award experience if params.killed_by_player.unwrap_or(false) @@ -1600,12 +1594,12 @@ impl LivingEntity { { let amount = dyn_self.get_experience_reward(cause); if amount > 0 { - ExperienceOrbEntity::spawn(&world, self.entity.pos.load(), amount).await; + ExperienceOrbEntity::spawn(&world, self.entity.pos.load(), amount); } } self.entity.pose.store(EntityPose::Dying); - self.drop_equipment(looting_level).await; + self.drop_equipment(looting_level); // Broadcast death message if it's a player and the gamerule is enabled self.broadcast_death_message(&*dyn_self, damage_type, source, cause) @@ -1613,7 +1607,10 @@ impl LivingEntity { // Trigger on_mob_death for active status effects let active_effects_vec: Vec<_> = { - let effects = self.active_effects.lock().await; + let effects = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); effects .values() .map(|e| (e.effect_type, e.amplifier)) @@ -1625,15 +1622,18 @@ impl LivingEntity { } } - self.reset_effects_and_attributes().await; + self.reset_effects_and_attributes(); } } - async fn drop_equipment(&self, looting_level: u32) { + fn drop_equipment(&self, looting_level: u32) { let world = self.entity.world.load(); let block_pos = self.entity.block_pos.load(); - let drop_chances = self.equipment_drop_chances.lock().await; + let drop_chances = self + .equipment_drop_chances + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let slots_to_drop: Vec = { let mut slots: Vec<_> = self.equipment_slots.values().cloned().collect(); @@ -1656,7 +1656,7 @@ impl LivingEntity { let mut item = self .entity_equipment .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .equipment .remove(slot) .unwrap_or_else(|| ItemStack::EMPTY.clone()); @@ -1672,7 +1672,7 @@ impl LivingEntity { let outer = rng.random_range(0..=inner); item.set_damage((max_damage - outer).max(0)); } - world.drop_stack(&block_pos, item).await; + world.drop_stack(&block_pos, item); } } @@ -1696,101 +1696,95 @@ impl LivingEntity { } } - async fn update_death_stats(&self, dyn_self: &dyn EntityBase, cause: Option<&dyn EntityBase>) { + fn update_death_stats(&self, dyn_self: &dyn EntityBase, cause: Option<&dyn EntityBase>) { if let Some(victim_player) = dyn_self.get_player() { - victim_player - .increment_stat(StatisticCategory::Custom, CustomStatistic::Deaths as i32, 1) - .await; - victim_player - .set_stat( - StatisticCategory::Custom, - CustomStatistic::TimeSinceDeath as i32, - 0, - ) - .await; + victim_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::Deaths as i32, + 1, + ); + victim_player.set_stat( + StatisticCategory::Custom, + CustomStatistic::TimeSinceDeath as i32, + 0, + ); if let Some(killer_entity) = cause.map(EntityBase::get_entity) { - victim_player - .increment_stat( - StatisticCategory::KilledBy, - killer_entity.entity_type.id as i32, - 1, - ) - .await; + victim_player.increment_stat( + StatisticCategory::KilledBy, + killer_entity.entity_type.id as i32, + 1, + ); } } if let Some(killer_player) = cause.and_then(|c| c.get_player()) { if dyn_self.get_player().is_some() { - killer_player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::PlayerKills as i32, - 1, - ) - .await; + killer_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::PlayerKills as i32, + 1, + ); } else { - killer_player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::MobKills as i32, - 1, - ) - .await; + killer_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::MobKills as i32, + 1, + ); let resource_name = self.entity.entity_type.resource_name; let criterion_key = format!("minecraft:{resource_name}"); - killer_player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::PlayerKilledEntity { - entity_type_resource: criterion_key, - } - ) - .await; + killer_player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::PlayerKilledEntity { + entity_type_resource: criterion_key, + }, + ); if resource_name == "skeleton" { let distance_sq = killer_player .position() .squared_distance_to_vec(&self.entity.pos.load()); if distance_sq >= 2500.0 { - killer_player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::SniperDuel).await; + killer_player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::SniperDuel); } } if resource_name == "phantom" { - killer_player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::TwoBirdsOneArrow).await; + killer_player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::TwoBirdsOneArrow); } - let held_item = killer_player.inventory().held_item().await; + let held_item = killer_player.inventory().held_item(); let is_crossbow = held_item.item.registry_key == "crossbow"; if is_crossbow { - killer_player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::Arbalistic).await; + killer_player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::Arbalistic, + ); } } - killer_player - .increment_stat( - StatisticCategory::Killed, - self.entity.entity_type.id as i32, - 1, - ) - .await; + killer_player.increment_stat( + StatisticCategory::Killed, + self.entity.entity_type.id as i32, + 1, + ); } } - async fn drop_loot(&self, params: LootContextParameters) { + fn drop_loot(&self, params: LootContextParameters) { if let Some(loot_table) = &self.get_entity().entity_type.loot_table { let pos = self.entity.block_pos.load(); for stack in loot_table.get_loot(params) { - self.entity.world.load().drop_stack(&pos, stack).await; + self.entity.world.load().drop_stack(&pos, stack); } } } - async fn tick_effects(&self) { + fn tick_effects(&self) { let mut effects_to_remove = Vec::new(); let mut effects_to_apply = Vec::new(); { - let mut effects = self.active_effects.lock().await; + let Ok(mut effects) = self.active_effects.try_lock() else { + return; + }; let entity_age = self.entity.age.load(Relaxed); for effect in effects.values_mut() { if effect.duration == 0 { @@ -1818,18 +1812,27 @@ impl LivingEntity { // Call the central removal function for each expired effect for effect_type in effects_to_remove { - self.remove_effect(effect_type).await; + self.remove_effect(effect_type); } for (mob_effect, amplifier) in effects_to_apply { - mob_effect.apply_effect_tick(self, amplifier).await; + let entity_id = self.entity.entity_id; + let world = self.entity.world.load_full(); + tokio::spawn(async move { + if let Some(entity) = world.get_entity_by_id(entity_id) + && let Some(living) = entity.get_living_entity() + { + mob_effect.apply_effect_tick(living, amplifier).await; + } + }); } } /// Tries to use a totem of undying from the entity's hands. If successful, applies the totem effects and returns true. + #[allow(dead_code)] async fn try_use_death_protector(&self, caller: &dyn EntityBase) -> bool { for hand in Hand::all() { - let mut stack = self.get_stack_in_hand(caller, hand).await; + let mut stack = self.get_stack_in_hand(caller, hand); // Clear the stack and use the totem of undying if stack.get_data_component::().is_some() { @@ -1857,13 +1860,13 @@ impl LivingEntity { .inventory() .entity_equipment .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .equipment .insert(slot, stack); } else { self.entity_equipment .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .equipment .insert(slot, stack); } @@ -1883,8 +1886,7 @@ impl LivingEntity { show_particles: true, show_icon: true, blend: false, - }) - .await; + }); self.add_effect(Effect { effect_type: &StatusEffect::REGENERATION, duration: 900, @@ -1893,8 +1895,7 @@ impl LivingEntity { show_particles: true, show_icon: true, blend: false, - }) - .await; + }); self.add_effect(Effect { effect_type: &StatusEffect::FIRE_RESISTANCE, duration: 800, @@ -1903,8 +1904,7 @@ impl LivingEntity { show_particles: true, show_icon: true, blend: false, - }) - .await; + }); return true; } @@ -1913,6 +1913,7 @@ impl LivingEntity { false } + #[allow(dead_code)] async fn damage_armor_items(&self, caller: &dyn EntityBase, damage_amount: f32) { // Formula: armor loses floor(incoming_damage / 4) durability, minimum 1. let armor_damage = (damage_amount / 4.0).floor().max(1.0) as i32; @@ -1922,7 +1923,10 @@ impl LivingEntity { // TODO: Implement DAMAGE_RESISTANT component checks (e.g. netherite vs fire). let armor_slots: Vec<(usize, ItemStack, EquipmentSlot)> = { - let equipment_lock = self.entity_equipment.lock().await; + let equipment_lock = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); self.equipment_slots .iter() .filter(|(_, slot)| slot.is_armor_slot()) @@ -1974,11 +1978,14 @@ impl LivingEntity { } } - pub async fn held_item(&self, caller: &dyn EntityBase) -> ItemStack { + pub fn held_item(&self, caller: &dyn EntityBase) -> ItemStack { if let Some(player) = caller.get_player() { - return player.inventory.held_item().await; + return player.inventory.held_item(); } - let equipment = self.entity_equipment.lock().await; + let equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment .equipment .get(&EquipmentSlot::MAIN_HAND) @@ -1986,22 +1993,25 @@ impl LivingEntity { .unwrap_or_else(|| ItemStack::EMPTY.clone()) } - pub async fn get_stack_in_hand(&self, caller: &dyn EntityBase, hand: Hand) -> ItemStack { + pub fn get_stack_in_hand(&self, caller: &dyn EntityBase, hand: Hand) -> ItemStack { match hand { - Hand::Left => self.off_hand_item(caller).await, - Hand::Right => self.held_item(caller).await, + Hand::Left => self.off_hand_item(caller), + Hand::Right => self.held_item(caller), } } /// getOffHandStack in source - pub async fn off_hand_item(&self, caller: &dyn EntityBase) -> ItemStack { + pub fn off_hand_item(&self, caller: &dyn EntityBase) -> ItemStack { if let Some(player) = caller.get_player() { - return player.inventory.off_hand_item().await; + return player.inventory.off_hand_item(); } let Some(slot) = self.equipment_slots.get(&PlayerInventory::OFF_HAND_SLOT) else { return ItemStack::EMPTY.clone(); }; - let equipment = self.entity_equipment.lock().await; + let equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment .equipment .get(slot) @@ -2017,8 +2027,8 @@ impl LivingEntity { !self.is_spectator() && self.entity.is_alive() } - pub async fn reset_state(&self) { - self.entity.reset_state().await; + pub fn reset_state(&self) { + self.entity.reset_state(); // Restore to maximum health for this entity type let max_health = self.get_max_health(); @@ -2034,14 +2044,14 @@ impl LivingEntity { None, ); - self.reset_effects_and_attributes().await; + self.reset_effects_and_attributes(); // Give a short grace period of invulnerability after respawn self.hurt_cooldown.store(20, Relaxed); self.last_damage_taken.store(0f32); self.entity.portal_cooldown.store(0, Relaxed); - *self.entity.portal_manager.lock().await = None; + *self.entity.portal_manager.blocking_lock() = None; // Clear fall/fire state self.fall_distance.store(0f32); @@ -2099,11 +2109,17 @@ impl LivingEntity { nbt.put_short("DeathTime", i16::from(self.death_time.load(Relaxed))); nbt.put_bool("FallFlying", self.entity.is_fall_flying()); { - let effects = self.active_effects.lock().await; - if !effects.is_empty() { + let effects_vec: Vec = { + let effects = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + effects.values().cloned().collect() + }; + if !effects_vec.is_empty() { // Iterate effects and create Box<[NbtTag]> - let mut effects_list = Vec::with_capacity(effects.len()); - for effect in effects.values() { + let mut effects_list = Vec::with_capacity(effects_vec.len()); + for effect in effects_vec { let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); effect.write_nbt(&mut effect_nbt).await; effects_list.push(NbtTag::Compound(effect_nbt)); @@ -2147,547 +2163,483 @@ impl LivingEntity { .fall_flying .store(nbt.get_bool("FallFlying").unwrap_or(false), Relaxed); { - let mut active_effects = self.active_effects.lock().await; let nbt_effects = nbt.get_list("active_effects"); if let Some(nbt_effects) = nbt_effects { + let mut read_effects = Vec::new(); for effect in nbt_effects { if let NbtTag::Compound(effect_nbt) = effect { if let Some(mut effect) = Effect::create_from_nbt(&mut effect_nbt.clone()).await { effect.blend = true; // TODO: change, is taken from effect give command - active_effects.insert(effect.effect_type, effect); + read_effects.push(effect); } else { warn!("Unable to read effect from nbt"); } } } + if !read_effects.is_empty() { + let mut active_effects = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for effect in read_effects { + active_effects.insert(effect.effect_type, effect); + } + } } } }) // todo more... } -} -impl EntityBase for LivingEntity { #[allow(clippy::too_many_lines)] - fn damage_with_context<'a>( - &'a self, - caller: &'a dyn EntityBase, + pub fn damage_with_context( + &self, + caller: &dyn EntityBase, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let mut amount = amount; + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + let mut amount = amount; - // Check invulnerability before applying damage - if self.entity.is_invulnerable_to(&damage_type).await { - return false; - } + // Check invulnerability before applying damage + if self.entity.is_invulnerable_to(&damage_type) { + return false; + } - if self.health.load() <= 0.0 || self.dead.load(Relaxed) { - return false; // Dying or dead - } + if self.health.load() <= 0.0 || self.dead.load(Relaxed) { + return false; // Dying or dead + } - if amount < 0.0 { - return false; - } + if amount < 0.0 { + return false; + } - let mut damage_event = - crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new( - self.entity.entity_id, - damage_type, - amount, - ); - if let Some(server) = self.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut damage_event).await; - } - if damage_event.cancelled { - return false; - } - amount = damage_event.damage; + let mut damage_event = + crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new( + self.entity.entity_id, + damage_type, + amount, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server + .plugin_manager + .fire_blocking(&server, &mut damage_event); + } + if damage_event.cancelled { + return false; + } + amount = damage_event.damage; - let world = self.entity.world.load(); - let is_fire_damage = damage_type == DamageType::IN_FIRE - || damage_type == DamageType::ON_FIRE - || damage_type == DamageType::LAVA - || damage_type == DamageType::HOT_FLOOR; + let world = self.entity.world.load(); + let is_fire_damage = damage_type == DamageType::IN_FIRE + || damage_type == DamageType::ON_FIRE + || damage_type == DamageType::LAVA + || damage_type == DamageType::HOT_FLOOR; - // Fire damage can be prevented by either game rules or fire resistance - if is_fire_damage { - // Check game rule for fire damage (only for players) - if self.entity.entity_type == &EntityType::PLAYER - && !world.level_info.load().game_rules.fire_damage - { - return false; - } - - // Check for fire resistance effect - if self.has_effect(&StatusEffect::FIRE_RESISTANCE).await { - return false; - } - } - - // Vanilla parity: entities in FREEZE_HURTS_EXTRA_TYPES take 5x freezing damage. - if damage_type == DamageType::FREEZE - && self - .entity - .entity_type - .has_tag(&tag::EntityType::MINECRAFT_FREEZE_HURTS_EXTRA_TYPES) + // Fire damage can be prevented by either game rules or fire resistance + if is_fire_damage { + // Check game rule for fire damage (only for players) + if self.entity.entity_type == &EntityType::PLAYER + && !world.level_info.load().game_rules.fire_damage { - amount *= 5.0; + return false; } - // These damage types bypass the hurt cooldown and death protection - let bypasses_cooldown_protection = - damage_type == DamageType::GENERIC_KILL || damage_type == DamageType::OUT_OF_WORLD; + // Check for fire resistance effect + if self.has_effect(&StatusEffect::FIRE_RESISTANCE) { + return false; + } + } - let mut damage_after_armor = amount; - if !bypasses_armor_durability(&damage_type) { - let mut armor = 0.0f32; - let mut toughness = 0.0f32; - { - let equipment_lock = self.entity_equipment.lock().await; - for slot in [ - EquipmentSlot::HEAD, - EquipmentSlot::CHEST, - EquipmentSlot::LEGS, - EquipmentSlot::FEET, - ] { - if let Some(stack) = equipment_lock.equipment.get(&slot) - && !stack.is_empty() - && let Some(modifiers) = - stack.get_data_component::() - { - for modifier in modifiers.attribute_modifiers.iter() { - if modifier.r#type == &Attributes::ARMOR { - armor += modifier.amount as f32; - } else if modifier.r#type == &Attributes::ARMOR_TOUGHNESS { - toughness += modifier.amount as f32; - } + // Vanilla parity: entities in FREEZE_HURTS_EXTRA_TYPES take 5x freezing damage. + if damage_type == DamageType::FREEZE + && self + .entity + .entity_type + .has_tag(&tag::EntityType::MINECRAFT_FREEZE_HURTS_EXTRA_TYPES) + { + amount *= 5.0; + } + + // These damage types bypass the hurt cooldown and death protection + let bypasses_cooldown_protection = + damage_type == DamageType::GENERIC_KILL || damage_type == DamageType::OUT_OF_WORLD; + + let mut damage_after_armor = amount; + if !bypasses_armor_durability(&damage_type) { + let mut armor = 0.0f32; + let mut toughness = 0.0f32; + { + let equipment_lock = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for slot in [ + EquipmentSlot::HEAD, + EquipmentSlot::CHEST, + EquipmentSlot::LEGS, + EquipmentSlot::FEET, + ] { + if let Some(stack) = equipment_lock.equipment.get(&slot) + && !stack.is_empty() + && let Some(modifiers) = + stack.get_data_component::() + { + for modifier in modifiers.attribute_modifiers.iter() { + if modifier.r#type == &Attributes::ARMOR { + armor += modifier.amount as f32; + } else if modifier.r#type == &Attributes::ARMOR_TOUGHNESS { + toughness += modifier.amount as f32; } } } } - let value = 2.0f32 + toughness / 4.0; - let clamped_armor = (armor - damage_after_armor / value) - .max(armor / 5.0) - .min(20.0); - damage_after_armor *= 1.0 - clamped_armor / 25.0; } + let value = 2.0f32 + toughness / 4.0; + let clamped_armor = (armor - damage_after_armor / value) + .max(armor / 5.0) + .min(20.0); + damage_after_armor *= 1.0 - clamped_armor / 25.0; + } - let mut damage_after_enchantments = damage_after_armor; - if damage_type != DamageType::OUT_OF_WORLD { - let mut epf = 0i32; - { - let equipment_lock = self.entity_equipment.lock().await; - for slot in [ - EquipmentSlot::HEAD, - EquipmentSlot::CHEST, - EquipmentSlot::LEGS, - EquipmentSlot::FEET, - ] { - if let Some(stack) = equipment_lock.equipment.get(&slot) - && !stack.is_empty() - && let Some(enchantments) = - stack.get_data_component::() - { - for (enchantment, level) in enchantments.enchantment.iter() { - let mut factor = 0; - let enc = *enchantment; - if enc == &Enchantment::PROTECTION { - if damage_type != DamageType::DROWN - && damage_type != DamageType::STARVE - && damage_type != DamageType::GENERIC_KILL - { - factor = *level; - } - } else if enc == &Enchantment::FIRE_PROTECTION { - if is_fire_damage { - factor = *level * 2; - } - } else if enc == &Enchantment::BLAST_PROTECTION { - if damage_type == DamageType::EXPLOSION - || damage_type == DamageType::PLAYER_EXPLOSION - { - factor = *level * 2; - } - } else if enc == &Enchantment::PROJECTILE_PROTECTION { - if damage_type == DamageType::ARROW - || damage_type == DamageType::MOB_PROJECTILE - || damage_type == DamageType::THROWN - { - factor = (*level) * 2; - } - } else if enc == &Enchantment::FEATHER_FALLING - && damage_type == DamageType::FALL + let mut damage_after_enchantments = damage_after_armor; + if damage_type != DamageType::OUT_OF_WORLD { + let mut epf = 0i32; + { + let equipment_lock = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for slot in [ + EquipmentSlot::HEAD, + EquipmentSlot::CHEST, + EquipmentSlot::LEGS, + EquipmentSlot::FEET, + ] { + if let Some(stack) = equipment_lock.equipment.get(&slot) + && !stack.is_empty() + && let Some(enchantments) = stack.get_data_component::() + { + for (enchantment, level) in enchantments.enchantment.iter() { + let mut factor = 0; + let enc = *enchantment; + if enc == &Enchantment::PROTECTION { + if damage_type != DamageType::DROWN + && damage_type != DamageType::STARVE + && damage_type != DamageType::GENERIC_KILL { - factor = (*level) * 4; + factor = *level; } - epf += factor; - } - } - } - } - epf = epf.min(20); - if epf > 0 { - damage_after_enchantments *= 1.0 - (epf as f32 * 0.04); - } - } - - // Apply Resistance effect reduction (20% per level), excluding bypasses_cooldown_protection and starvation damage - let resistance_reduction = - if !bypasses_cooldown_protection && damage_type != DamageType::STARVE { - self.get_effect(&StatusEffect::RESISTANCE) - .await - .map_or(0.0, |e| 0.2 * (e.amplifier + 1) as f32) - } else { - 0.0 - }; - - // Total damage after reductions - let effective_amount = damage_after_enchantments * (1.0 - resistance_reduction); - - if resistance_reduction > 0.0 { - let resisted = damage_after_enchantments * resistance_reduction; - if let Some(player) = caller.get_player() { - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageResisted as i32, - (resisted * 10.0) as i32, - ) - .await; - } - if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { - attacker_player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageDealtResisted as i32, - (resisted * 10.0) as i32, - ) - .await; - } - } - - // Check for shield blocking - if self.is_blocking().await - && !damage_type.has_tag(&tag::DamageType::MINECRAFT_BYPASSES_SHIELD) - && let Some(pos) = position - { - let player_pos = self.entity.pos.load(); - let look_vec = Vector3::rotation_vector(0.0, self.entity.yaw.load() as f64); - let mut source_to_player = (player_pos - pos).normalize(); - source_to_player.y = 0.0; - - if source_to_player.dot(&look_vec) < 0.0 { - world.play_sound(Sound::ItemShieldBlock, SoundCategory::Players, &player_pos); - - if let Some(player) = caller.get_player() { - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageBlockedByShield as i32, - (effective_amount * 10.0) as i32, - ) - .await; - - player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::DeflectedDamage).await; - } - - if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { - let held_item = attacker_player.inventory().held_item().await; - let is_axe = held_item.is_axe(); - if is_axe { - let mut disable_chance = 0.25; - let is_sprinting = attacker_player - .living_entity - .entity - .sprinting - .load(Ordering::Relaxed); - if is_sprinting { - disable_chance = 1.0; - } - - if rand::random::() < disable_chance - && let Some(victim_player) = caller.get_player() + } else if enc == &Enchantment::FIRE_PROTECTION { + if is_fire_damage { + factor = *level * 2; + } + } else if enc == &Enchantment::BLAST_PROTECTION { + if damage_type == DamageType::EXPLOSION + || damage_type == DamageType::PLAYER_EXPLOSION + { + factor = *level * 2; + } + } else if enc == &Enchantment::PROJECTILE_PROTECTION { + if damage_type == DamageType::ARROW + || damage_type == DamageType::MOB_PROJECTILE + || damage_type == DamageType::THROWN + { + factor = (*level) * 2; + } + } else if enc == &Enchantment::FEATHER_FALLING + && damage_type == DamageType::FALL { - victim_player - .start_cooldown("minecraft:shield".to_string(), 100) - .await; - self.clear_active_hand().await; - - world.broadcast_packet_all(&CEntityStatus::new( - self.entity.entity_id, - 30, - )); + factor = (*level) * 4; } + epf += factor; } } + } + } + epf = epf.min(20); + if epf > 0 { + damage_after_enchantments *= 1.0 - (epf as f32 * 0.04); + } + } - let active_hand = self.active_hand.lock().await; - if let Some(hand) = *active_hand { - let slot = if hand == Hand::Left { - EquipmentSlot::MAIN_HAND - } else { - EquipmentSlot::OFF_HAND - }; + // Apply Resistance effect reduction (20% per level) + let resistance_reduction = + if !bypasses_cooldown_protection && damage_type != DamageType::STARVE { + self.get_effect(&StatusEffect::RESISTANCE) + .map_or(0.0, |e| 0.2 * (e.amplifier + 1) as f32) + } else { + 0.0 + }; - let mut equipment_guard = self.entity_equipment.lock().await; - if let Some(stack) = equipment_guard.equipment.get_mut(&slot) { - let durability_damage = (amount / 1.0).floor().max(1.0) as i32; - if stack.damage_item(durability_damage) == DamageResult::Broken { - if let Some(player) = caller.get_player() { - player - .increment_stat( - StatisticCategory::Broken, - stack.item.id as i32, - 1, - ) - .await; - } - world.send_entity_status( - &self.entity, - crate::entity::equipment_break_status(&slot), - None, + // Total damage after reductions + let effective_amount = damage_after_enchantments * (1.0 - resistance_reduction); + + if resistance_reduction > 0.0 { + let resisted = damage_after_enchantments * resistance_reduction; + if let Some(player) = caller.get_player() { + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageResisted as i32, + (resisted * 10.0) as i32, + ); + } + if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { + attacker_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageDealtResisted as i32, + (resisted * 10.0) as i32, + ); + } + } + + // Check for shield blocking + if self.is_blocking() + && !damage_type.has_tag(&tag::DamageType::MINECRAFT_BYPASSES_SHIELD) + && let Some(pos) = position + { + let player_pos = self.entity.pos.load(); + let look_vec = Vector3::rotation_vector(0.0, self.entity.yaw.load() as f64); + let mut source_to_player = (player_pos - pos).normalize(); + source_to_player.y = 0.0; + + if source_to_player.dot(&look_vec) < 0.0 { + world.play_sound(Sound::ItemShieldBlock, SoundCategory::Players, &player_pos); + + if let Some(player) = caller.get_player() { + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageBlockedByShield as i32, + (effective_amount * 10.0) as i32, + ); + } + + let active_hand = self + .active_hand + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(hand) = *active_hand { + let slot = if hand == Hand::Left { + EquipmentSlot::MAIN_HAND + } else { + EquipmentSlot::OFF_HAND + }; + + let mut equipment_guard = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(stack) = equipment_guard.equipment.get_mut(&slot) { + let durability_damage = (amount / 1.0).floor().max(1.0) as i32; + if stack.damage_item(durability_damage) == DamageResult::Broken { + if let Some(player) = caller.get_player() { + player.increment_stat( + StatisticCategory::Broken, + stack.item.id as i32, + 1, ); - *stack = ItemStack::EMPTY.clone(); - let broken_stack = stack.clone(); - drop(equipment_guard); - - self.send_equipment_changes(&[(slot, broken_stack)]); - self.clear_active_hand().await; } + world.send_entity_status( + &self.entity, + crate::entity::equipment_break_status(&slot), + None, + ); + *stack = ItemStack::EMPTY.clone(); + let broken_stack = stack.clone(); + drop(equipment_guard); + + self.send_equipment_changes(&[(slot, broken_stack)]); + self.clear_active_hand(); } } + } + return false; + } + } + + // Apply hurt cooldown logic + let last_damage = self.last_damage_taken.load(); + let (damage_amount, play_sound) = + if self.hurt_cooldown.load(Relaxed) > 10 && !bypasses_cooldown_protection { + if effective_amount <= last_damage { return false; } - } - - // Apply hurt cooldown logic - let last_damage = self.last_damage_taken.load(); - let (damage_amount, play_sound) = - if self.hurt_cooldown.load(Relaxed) > 10 && !bypasses_cooldown_protection { - if effective_amount <= last_damage { - return false; - } - (effective_amount - last_damage, false) - } else { - self.hurt_cooldown.store(20, Relaxed); - (effective_amount, true) - }; - - // Finalize state - self.last_damage_taken.store(amount); - let damage_amount = damage_amount.max(0.0); - - let Some(server) = world.server.upgrade() else { - return false; + (effective_amount - last_damage, false) + } else { + self.hurt_cooldown.store(20, Relaxed); + (effective_amount, true) }; - let config = &server.advanced_config.pvp; - if config.hurt_animation { - let entity_id = self.entity.entity_id; - let hurt_yaw = source.map_or(0.0, |source| { - let src = source.get_entity().pos.load(); - let tgt = self.entity.pos.load(); - (src.z - tgt.z).atan2(src.x - tgt.x).to_degrees() as f32 - - self.entity.yaw.load() - }); - let hurt_event = SActorEvent { - target_runtime_id: VarULong(entity_id as u64), - event_id: ActorEventID::Hurt, - data: VarInt(0), - fire_at_position: None, - }; - world - .broadcast_editioned( - &CHurtAnimation::new(VarInt(entity_id), hurt_yaw), - &hurt_event, - ) - .await; - world.broadcast_packet_all(&CEntityStatus::new(entity_id, 2)); - } + // Finalize state + self.last_damage_taken.store(amount); + let damage_amount = damage_amount.max(0.0); - world.broadcast_packet_all(&CDamageEvent::new( - self.entity.entity_id.into(), - damage_type.id.into(), - source.map(|e| e.get_entity().entity_id.into()), - cause.map(|e| e.get_entity().entity_id.into()), - position, - )); + let Some(server) = world.server.upgrade() else { + return false; + }; + let config = &server.advanced_config.pvp; - // Trigger on_mob_hurt for active status effects - let active_effects_vec: Vec<_> = { - let effects = self.active_effects.lock().await; - effects - .values() - .map(|e| (e.effect_type, e.amplifier)) - .collect() + if config.hurt_animation { + let entity_id = self.entity.entity_id; + let hurt_yaw = source.map_or(0.0, |source| { + let src = source.get_entity().pos.load(); + let tgt = self.entity.pos.load(); + (src.z - tgt.z).atan2(src.x - tgt.x).to_degrees() as f32 - self.entity.yaw.load() + }); + let hurt_event = SActorEvent { + target_runtime_id: VarULong(entity_id as u64), + event_id: ActorEventID::Hurt, + data: VarInt(0), + fire_at_position: None, }; - for (effect_type, amplifier) in active_effects_vec { - if let Some(mob_effect) = crate::entity::effect::get_mob_effect(effect_type) { - mob_effect - .on_mob_hurt(self, amplifier, &damage_type, amount) - .await; - } - } + world.broadcast_to_chunk( + self.entity.chunk_pos.load(), + &CHurtAnimation::new(entity_id.into(), hurt_yaw), + ); + world.broadcast_to_chunk_bedrock(self.entity.chunk_pos.load(), &hurt_event); + } - if play_sound { - world.play_sound( - self.hurt_sound(), - SoundCategory::Players, - &self.entity.pos.load(), + world.broadcast_packet_all(&CDamageEvent::new( + self.entity.entity_id.into(), + damage_type.id.into(), + source.map(|e| e.get_entity().entity_id.into()), + cause.map(|e| e.get_entity().entity_id.into()), + position, + )); + + if play_sound { + world.play_sound( + self.hurt_sound(), + SoundCategory::Players, + &self.entity.pos.load(), + ); + + if let Some(source) = source { + let source_pos = source.get_entity().pos.load(); + let target_pos = self.entity.pos.load(); + let dx = source_pos.x - target_pos.x; + let dz = source_pos.z - target_pos.z; + let resistance = self.get_attribute_value(&Attributes::KNOCKBACK_RESISTANCE); + self.entity + .apply_knockback(knockback_after_resistance(0.4, resistance), dx, dz); + self.entity.send_velocity(); + } + } + + // Consume absorption first, then apply remaining damage to health + let mut remaining = damage_amount; + let current_abs = self.absorption.load(); + if current_abs > 0.0 { + let absorbed = current_abs.min(remaining); + if let Some(player) = caller.get_player() { + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageAbsorbed as i32, + (absorbed * 10.0) as i32, ); - - if let Some(source) = source { - let source_pos = source.get_entity().pos.load(); - let target_pos = self.entity.pos.load(); - let dx = source_pos.x - target_pos.x; - let dz = source_pos.z - target_pos.z; - let resistance = self.get_attribute_value(&Attributes::KNOCKBACK_RESISTANCE); - self.entity.apply_knockback( - knockback_after_resistance(0.4, resistance), - dx, - dz, - ); - self.entity.send_velocity(); - } } - // Consume absorption first, then apply remaining damage to health - let mut remaining = damage_amount; - let current_abs = self.absorption.load(); - if current_abs > 0.0 { - let absorbed = current_abs.min(remaining); - if let Some(player) = caller.get_player() { - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageAbsorbed as i32, - (absorbed * 10.0) as i32, - ) - .await; - } - - if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { - attacker_player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageDealtAbsorbed as i32, - (absorbed * 10.0) as i32, - ) - .await; - } - - if current_abs >= remaining { - let new_abs = current_abs - remaining; - self.set_absorption(new_abs).await; - remaining = 0.0; - } else { - remaining -= current_abs; - self.set_absorption(0.0).await; - } - - // Track attacker for RevengeGoal (only after confirming damage) - if let Some(attacker) = cause.or(source) { - self.last_attacker_id - .store(attacker.get_entity().entity_id, Relaxed); - self.last_attacked_time - .store(self.entity.age.load(Relaxed), Relaxed); - } + if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { + attacker_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageDealtAbsorbed as i32, + (absorbed * 10.0) as i32, + ); } - // Apply remaining damage to health (clamped) - let max_h = self.get_max_health(); - let new_health = self.health.load() - remaining; - let clamped_health = new_health.max(0.0).min(max_h); - if remaining > 0.0 { - self.set_health(clamped_health); - - // Statistics updates - if let Some(player) = caller.get_player() { - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageTaken as i32, - (remaining * 10.0) as i32, - ) - .await; - } - - if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { - attacker_player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::DamageDealt as i32, - (remaining * 10.0) as i32, - ) - .await; - } - - // Track attacker for RevengeGoal (only after confirming damage) - if let Some(attacker) = cause.or(source) { - self.last_attacker_id - .store(attacker.get_entity().entity_id, Relaxed); - self.last_attacked_time - .store(self.entity.age.load(Relaxed), Relaxed); - } + if current_abs >= remaining { + let new_abs = current_abs - remaining; + self.set_absorption(new_abs); + remaining = 0.0; + } else { + remaining -= current_abs; + self.set_absorption(0.0); } - // Check if the entity died and isn't protected by a death protection mechanic (ex. totem of undying) - if clamped_health <= 0.0 - && (bypasses_cooldown_protection || !self.try_use_death_protector(caller).await) - { - let mut death_event = - crate::plugin::api::events::entity::entity_death::EntityDeathEvent::new( - self.entity.entity_id, - 0, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut death_event).await; - } - if let Some(player) = caller.get_player() - && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) - { - let mut player_death_event = - crate::plugin::api::events::entity::entity_death::PlayerDeathEvent::new( - player_arc, - pumpkin_util::text::TextComponent::text("Died"), - 0, - ); - if let Some(server) = world.server.upgrade() { - server - .plugin_manager - .fire(&server, &mut player_death_event) - .await; - } - } + if let Some(attacker) = cause.or(source) { + self.last_attacker_id + .store(attacker.get_entity().entity_id, Relaxed); + self.last_attacked_time + .store(self.entity.age.load(Relaxed), Relaxed); + } + } - self.on_death(damage_type, source, cause).await; + let max_h = self.get_max_health(); + let new_health = self.health.load() - remaining; + let clamped_health = new_health.max(0.0).min(max_h); + if remaining > 0.0 { + self.set_health(clamped_health); + + if let Some(player) = caller.get_player() { + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageTaken as i32, + (remaining * 10.0) as i32, + ); } - // Armor durability is based on incoming raw damage, not post-absorption remaining. - // Armor loses floor(raw_damage / 4) durability, minimum 1. - // Not applied when the source is in `#minecraft:bypasses_armor`. - if damage_amount > 0.0 && !bypasses_armor_durability(&damage_type) { - self.damage_armor_items(caller, damage_amount).await; + if let Some(attacker_player) = cause.and_then(|c| c.get_player()) { + attacker_player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::DamageDealt as i32, + (remaining * 10.0) as i32, + ); } - true - }) + if let Some(attacker) = cause.or(source) { + self.last_attacker_id + .store(attacker.get_entity().entity_id, Relaxed); + self.last_attacked_time + .store(self.entity.age.load(Relaxed), Relaxed); + } + } + + if clamped_health <= 0.0 { + let mut death_event = + crate::plugin::api::events::entity::entity_death::EntityDeathEvent::new( + self.entity.entity_id, + 0, + ); + if let Some(server) = world.server.upgrade() { + server + .plugin_manager + .fire_blocking(&server, &mut death_event); + } + world.send_entity_status( + &self.entity, + pumpkin_data::entity::EntityStatus::Death, + None, + ); + } + + true } - fn tick_in_void<'a>(&'a self, dyn_self: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - dyn_self - .damage(dyn_self, 4.0, DamageType::OUT_OF_WORLD) - .await; - }) + pub fn damage(&self, caller: &dyn EntityBase, amount: f32, damage_type: DamageType) -> bool { + self.damage_with_context(caller, amount, damage_type, None, None, None) + } +} + +impl EntityBase for LivingEntity { + fn damage_with_context( + &self, + caller: &dyn EntityBase, + amount: f32, + damage_type: DamageType, + position: Option>, + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + self.damage_with_context(caller, amount, damage_type, position, source, cause) + } + + fn tick_in_void(&self, dyn_self: &dyn EntityBase) { + dyn_self.damage(dyn_self, 4.0, DamageType::OUT_OF_WORLD); } fn get_gravity(&self) -> f64 { @@ -2695,227 +2647,224 @@ impl EntityBase for LivingEntity { } #[allow(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.entity.tick(caller, server).await; + fn tick(&self, caller: &Arc, server: &Server) { + self.entity.tick(caller, server); - // Only tick movement if the entity is alive. This prevents a dead "corpse" - // from continuing to be simulated (accumulating fall_distance/velocity). - // We allow movement during death animation (20 ticks) so knockback is applied. - let is_alive = !self.dead.load(Relaxed) && self.health.load() > 0.0; - 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).await; - // Vanilla-like order: freeze logic runs after movement/collisions. - self.entity.tick_frozen(caller.as_ref()).await; - } + // Only tick movement if the entity is alive. This prevents a dead "corpse" + // from continuing to be simulated (accumulating fall_distance/velocity). + // We allow movement during death animation (20 ticks) so knockback is applied. + let is_alive = !self.dead.load(Relaxed) && self.health.load() > 0.0; + 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); + // Vanilla-like order: freeze logic runs after movement/collisions. + self.entity.tick_frozen(caller.as_ref()); + } - // TODO - let player = caller.get_player(); - let is_player = player.is_some(); + // TODO + let player = caller.get_player(); + let is_player = player.is_some(); - if !is_player { - self.entity.send_pos_rot(); - } + if !is_player { + self.entity.send_pos_rot(); + } - // Fetch supporting blocks for players or other entities - let supporting_pos = caller.get_player().map_or_else( - || self.entity.get_supporting_block_pos(), - super::player::Player::get_supporting_block_pos, + // Fetch supporting blocks for players or other entities + let supporting_pos = caller.get_player().map_or_else( + || self.entity.get_supporting_block_pos(), + super::player::Player::get_supporting_block_pos, + ); + + // Notify the block under the entity each tick if a supporting block position is found + if self.entity.is_affected_by_blocks() + && let Some(supporting) = supporting_pos + { + let world = self.entity.world.load_full(); + let (block, state) = world.get_block_and_state(&supporting); + + world.block_registry.on_entity_step( + block, + &world, + caller.as_ref() as &dyn EntityBase, + &supporting, + state, + false, ); - // Notify the block under the entity each tick if a supporting block position is found - if let Some(supporting) = supporting_pos { - let world = self.entity.world.load(); - let (block, state) = world.get_block_and_state(&supporting); + // Check slightly below supporting_pos for additional supporting blocks (blocks under carpets and the like) + if !block.is_solid() { + let below_supporting = supporting.down(); + let (below_block, below_state) = world.get_block_and_state(&below_supporting); - world - .block_registry - .on_entity_step( - block, - &world, - caller.as_ref() as &dyn EntityBase, - &supporting, - state, - false, - ) - .await; - - // Check slightly below supporting_pos for additional supporting blocks (blocks under carpets and the like) - if !block.is_solid() { - let below_supporting = supporting.down(); - let (below_block, below_state) = world.get_block_and_state(&below_supporting); - - // If block is not air, notify it as well - world - .block_registry - .on_entity_step( - below_block, - &world, - caller.as_ref() as &dyn EntityBase, - &below_supporting, - below_state, - true, // below supporting block - ) - .await; - } + // If block is not air, notify it as well + world.block_registry.on_entity_step( + below_block, + &world, + caller.as_ref() as &dyn EntityBase, + &below_supporting, + below_state, + true, // below supporting block + ); } + } - self.tick_effects().await; + self.tick_effects(); - // Current active item - { - let item_in_use = self.item_in_use.lock().await.clone(); - if let Some(item) = item_in_use.as_ref() - && self.item_use_time.fetch_sub(1, Ordering::Relaxed) <= 0 + // Current active item + if self.item_use_time.load(Ordering::Relaxed) > 0 + && self.item_use_time.fetch_sub(1, Ordering::Relaxed) <= 1 + { + let caller_clone = caller.clone(); + let entity_id = self.entity.entity_id; + let world = self.entity.world.load_full(); + tokio::spawn(async move { + if let Some(entity) = world.get_entity_by_id(entity_id) + && let Some(living) = entity.get_living_entity() { - // Consume item - let mut is_potion = false; - if let Some(food) = item.get_data_component::() - && let Some(player) = caller.get_player() - { - player - .hunger_manager - .eat(player, food.nutrition as u8, food.saturation) - .await; - self.entity.world.load().play_bedrock_level_sound( - "burp", - &self.entity.pos.load(), - -1, - ); - } - - self.apply_consumable_effects(caller, item).await; - - // Handle potion consumption - if item.get_data_component::().is_some() { - let effects = crate::item::potion::PotionContents::read_potion_effects(item); - crate::item::potion::PotionContents::apply_effects_to(self, effects, 1.0, crate::item::potion::PotionApplicationSource::Normal).await; - is_potion = true; - } - - if let Some(player) = caller.get_player() { - player - .trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::ConsumeItem { - item_id: format!("minecraft:{}", item.item.registry_key), - }) - .await; - - // Prefer modifying the exact stack that matches the consumed item: - // 1) selected hotbar (held_item) - // 2) off-hand - // 3) fallback to active_hand if the above didn't match - let mut handled = false; - - // Check main hand (hotbar selected) - let mut held = player.inventory.held_item().await; - if held.are_items_and_components_equal(item) { - if is_potion { - if player.gamemode.load() != GameMode::Creative { - held.decrement(1); - if held.is_empty() { - held = ItemStack::new(1, &Item::GLASS_BOTTLE); - } - } - } else { - held.decrement_unless_creative(player.gamemode.load(), 1); - } - player.inventory.set_held_item(held).await; - handled = true; + let item_in_use = living + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + if let Some(item) = item_in_use.as_ref() { + // Consume item + let mut is_potion = false; + if let Some(food) = item.get_data_component::() + && let Some(player) = caller_clone.get_player() + { + player.hunger_manager.eat( + player, + food.nutrition as u8, + food.saturation, + ); } - if !handled { - // Check off-hand - let mut off_hand = player.inventory.off_hand_item().await; - if off_hand.are_items_and_components_equal(item) { + living.apply_consumable_effects(&caller_clone, item).await; + + // Handle potion consumption + if item.get_data_component::().is_some() { + let effects = crate::item::potion::PotionContents::read_potion_effects(item); + crate::item::potion::PotionContents::apply_effects_to(living, effects, 1.0, crate::item::potion::PotionApplicationSource::Normal); + is_potion = true; + } + + if let Some(player) = caller_clone.get_player() { + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::ConsumeItem { + item_id: format!("minecraft:{}", item.item.registry_key), + }, + ); + + // Prefer modifying the exact stack that matches the consumed item: + // 1) selected hotbar (held_item) + // 2) off-hand + // 3) fallback to active_hand if the above didn't match + let mut handled = false; + + // Check main hand (hotbar selected) + let mut held = player.inventory.held_item(); + if held.are_items_and_components_equal(item) { if is_potion { if player.gamemode.load() != GameMode::Creative { - off_hand.decrement(1); - if off_hand.is_empty() { - off_hand = ItemStack::new(1, &Item::GLASS_BOTTLE); + held.decrement(1); + if held.is_empty() { + held = ItemStack::new(1, &Item::GLASS_BOTTLE); } } } else { - off_hand.decrement_unless_creative(player.gamemode.load(), 1); + held.decrement_unless_creative(player.gamemode.load(), 1); + } + player.inventory.set_held_item(held); + handled = true; + } + + if !handled { + // Check off-hand + let mut off_hand = player.inventory.off_hand_item(); + if off_hand.are_items_and_components_equal(item) { + if is_potion { + if player.gamemode.load() != GameMode::Creative { + off_hand.decrement(1); + if off_hand.is_empty() { + off_hand = ItemStack::new(1, &Item::GLASS_BOTTLE); + } + } + } else { + off_hand + .decrement_unless_creative(player.gamemode.load(), 1); + } + player.inventory.set_stack_in_hand(Hand::Left, off_hand); + handled = true; + } + } + + if !handled { + // Use stored active_hand (as a fallback) + let active_hand = *living + .active_hand + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let hand_to_modify = active_hand.unwrap_or(Hand::Right); + let mut item_stack = + living.get_stack_in_hand(caller_clone.as_ref(), hand_to_modify); + + if is_potion { + if player.gamemode.load() != GameMode::Creative { + item_stack.decrement(1); + if item_stack.is_empty() { + item_stack = ItemStack::new(1, &Item::GLASS_BOTTLE); + } + } + } else { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); } player .inventory - .set_stack_in_hand(Hand::Left, off_hand) + .set_stack_in_hand(hand_to_modify, item_stack); + } + + if let Some(cooldown) = item.get_use_cooldown() { + let group = cooldown + .cooldown_group + .clone() + .unwrap_or_else(|| item.item.registry_key.to_string()); + player + .start_cooldown(group, (cooldown.seconds * 20.0) as i32) .await; - handled = true; } } - if !handled { - // Use stored active_hand (as a fallback) - let active_hand = *self.active_hand.lock().await; - let hand_to_modify = active_hand.unwrap_or(Hand::Right); - let mut item_stack = self - .get_stack_in_hand(caller.as_ref(), hand_to_modify) - .await; - - if is_potion { - if player.gamemode.load() != GameMode::Creative { - item_stack.decrement(1); - if item_stack.is_empty() { - item_stack = ItemStack::new(1, &Item::GLASS_BOTTLE); - } - } - } else { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); - } - player - .inventory - .set_stack_in_hand(hand_to_modify, item_stack) - .await; - } - - if let Some(cooldown) = item.get_use_cooldown() { - let group = cooldown - .cooldown_group - .clone() - .unwrap_or_else(|| item.item.registry_key.to_string()); - player - .start_cooldown(group, (cooldown.seconds * 20.0) as i32) - .await; - } + living.clear_active_hand(); } + } + }); + } - self.clear_active_hand().await; - } + if self.hurt_cooldown.load(Relaxed) > 0 { + self.hurt_cooldown.fetch_sub(1, Relaxed); + } + if self.health.load() <= 0.0 { + let time = self + .death_time + .fetch_update(Relaxed, Relaxed, |time| Some(time.saturating_add(1))) + .unwrap_or_else(|time| time) + .saturating_add(1); + // Players remain part of the world until their client requests a + // respawn. Removing one here breaks reconnecting while dead. + if self.entity.entity_type == &EntityType::PLAYER { + return; } - - if self.hurt_cooldown.load(Relaxed) > 0 { - self.hurt_cooldown.fetch_sub(1, Relaxed); + // Only send death particles once (on the exact tick death_time reaches 20) + // and then remove the entity, preventing entity_event spam. + if time == 20 && !self.entity.removed.swap(true, Ordering::Relaxed) { + self.entity.world.load().send_entity_status( + &self.entity, + EntityStatus::Death, + Some(ActorEventID::Death), + ); + self.entity.remove(); } - if self.health.load() <= 0.0 { - let time = self - .death_time - .fetch_update(Relaxed, Relaxed, |time| Some(time.saturating_add(1))) - .unwrap_or_else(|time| time) - .saturating_add(1); - // Players remain part of the world until their client requests a - // respawn. Removing one here breaks reconnecting while dead. - if self.entity.entity_type == &EntityType::PLAYER { - return; - } - // Only send death particles once (on the exact tick death_time reaches 20) - // and then remove the entity, preventing entity_event spam. - if time == 20 && !self.entity.removed.swap(true, Ordering::Relaxed) { - self.entity.world.load().send_entity_status( - &self.entity, - EntityStatus::Death, - Some(ActorEventID::Death), - ); - self.entity.remove().await; - } - } - }) + } } fn get_entity(&self) -> &Entity { @@ -2968,17 +2917,16 @@ impl LivingEntity { show_particles: effect.show_particles, show_icon: effect.show_icon, blend: false, - }) - .await; + }); } } ConsumeEffect::ClearAllEffects => { - self.reset_effects_and_attributes().await; + self.reset_effects_and_attributes(); } ConsumeEffect::RemoveEffects(idset) => { if let pumpkin_data::data_component_impl::IDSet::IDs(ids) = idset { for effect_type in ids.iter() { - self.remove_effect(effect_type).await; + self.remove_effect(effect_type); } } } @@ -2990,7 +2938,7 @@ impl LivingEntity { .get_entity() .remove_passenger_before_teleport(caller.get_entity().entity_id) .await; - if caller.get_entity().has_vehicle().await { + if caller.get_entity().has_vehicle() { continue; } } diff --git a/crates/pumpkin/src/entity/marker.rs b/crates/pumpkin/src/entity/marker.rs index 8e1ce40bd..3fe1dbc8f 100644 --- a/crates/pumpkin/src/entity/marker.rs +++ b/crates/pumpkin/src/entity/marker.rs @@ -17,7 +17,7 @@ pub struct MarkerEntity { impl MarkerEntity { pub fn new(entity: Entity) -> Arc { - entity.no_clip.store(true, Ordering::Relaxed); + entity.no_physics.store(true, Ordering::Relaxed); Arc::new(Self { entity, data: Mutex::new(NbtCompound::new()), @@ -43,17 +43,9 @@ impl EntityBase for MarkerEntity { }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move {}) - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move {}) - } + fn init_data_tracker(&self) {} fn get_entity(&self) -> &Entity { &self.entity @@ -83,16 +75,16 @@ impl EntityBase for MarkerEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { false }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + false } fn send_java_spawn_packet<'a>(&'a self, _client: &'a JavaClient) -> EntityBaseFuture<'a, ()> { diff --git a/crates/pumpkin/src/entity/mob/bat.rs b/crates/pumpkin/src/entity/mob/bat.rs index 8fdf98c80..adb79cb7f 100644 --- a/crates/pumpkin/src/entity/mob/bat.rs +++ b/crates/pumpkin/src/entity/mob/bat.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed}; use pumpkin_data::damage::DamageType; @@ -11,10 +12,9 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::chunk::ChunkHeightmapType; use rand::RngExt; -use tokio::sync::Mutex; use crate::entity::mob::{Mob, MobEntity}; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture}; +use crate::entity::{Entity, EntityBase, NbtFuture}; use crate::world::World; const ROOSTING_FLAG: u8 = 1; @@ -131,9 +131,12 @@ impl BatEntity { } } - async fn tick_flying(&self, world: &World, above_pos: &BlockPos, pos: &Vector3) { + fn tick_flying(&self, world: &World, above_pos: &BlockPos, pos: &Vector3) { let entity = &self.mob_entity.living_entity.entity; - let mut hanging_pos = self.hanging_position.lock().await; + let mut hanging_pos = self + .hanging_position + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(hp) = *hanging_pos { let hp_state = world.get_block_state(&hp); @@ -205,15 +208,13 @@ impl BatEntity { } impl Mob for BatEntity { - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; - entity.send_meta_data( - &[Metadata::new(tracked_data::bat::DATA_ID_FLAGS, flags)], - None, - ); - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; + entity.send_meta_data( + &[Metadata::new(tracked_data::bat::DATA_ID_FLAGS, flags)], + None, + ); } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { @@ -235,34 +236,30 @@ impl Mob for BatEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - let block_pos = entity.block_pos.load(); - let above_pos = BlockPos::new(block_pos.0.x, block_pos.0.y + 1, block_pos.0.z); - let world = entity.world.load(); - let pos = entity.pos.load(); + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + let block_pos = entity.block_pos.load(); + let above_pos = BlockPos::new(block_pos.0.x, block_pos.0.y + 1, block_pos.0.z); + let world = entity.world.load(); + let pos = entity.pos.load(); - self.tick_ambient_sound(&world, &pos); + self.tick_ambient_sound(&world, &pos); - if self.is_roosting() { - self.tick_roosting(&world, &above_pos, &pos); - } else { - self.tick_flying(&world, &above_pos, &pos).await; - } - }) + if self.is_roosting() { + self.tick_roosting(&world, &above_pos, &pos); + } else { + self.tick_flying(&world, &above_pos, &pos); + } } - fn post_tick(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - if self.is_roosting() { - let entity = &self.mob_entity.living_entity.entity; - entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); - let pos = entity.pos.load(); - let snapped_y = (pos.y.floor()) + 1.0 - f64::from(entity.height()); - entity.set_pos(Vector3::new(pos.x, snapped_y, pos.z)); - } - }) + fn post_tick(&self) { + if self.is_roosting() { + let entity = &self.mob_entity.living_entity.entity; + entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); + let pos = entity.pos.load(); + let snapped_y = (pos.y.floor()) + 1.0 - f64::from(entity.height()); + entity.set_pos(Vector3::new(pos.x, snapped_y, pos.z)); + } } fn get_mob_gravity(&self) -> f64 { @@ -273,24 +270,18 @@ impl Mob for BatEntity { Some(0.6) } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.is_roosting() { - self.set_roosting(false); - let entity = &self.mob_entity.living_entity.entity; - let pos = entity.pos.load(); - entity.world.load().play_sound_fine( - Sound::EntityBatTakeoff, - SoundCategory::Ambient, - &pos, - 0.1, - 0.95, - ); - } - }) + fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { + if self.is_roosting() { + self.set_roosting(false); + let entity = &self.mob_entity.living_entity.entity; + let pos = entity.pos.load(); + entity.world.load().play_sound_fine( + Sound::EntityBatTakeoff, + SoundCategory::Ambient, + &pos, + 0.1, + 0.95, + ); + } } } diff --git a/crates/pumpkin/src/entity/mob/creaking.rs b/crates/pumpkin/src/entity/mob/creaking.rs index dc0f77ec5..1e9cf382c 100644 --- a/crates/pumpkin/src/entity/mob/creaking.rs +++ b/crates/pumpkin/src/entity/mob/creaking.rs @@ -18,7 +18,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::block::entities::creaking_heart::CreakingHeartBlockEntity; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal, @@ -227,14 +227,14 @@ impl CreakingEntity { } } - pub async fn activate(&self, player: &Arc) { - *self.mob_entity.target.lock().await = Some(player.clone()); + pub fn activate(&self, player: &Arc) { + self.mob_entity.set_target(Some(player.clone())); self.play_sound(Sound::EntityCreakingActivate); self.set_is_active(true); } - pub async fn deactivate(&self) { - *self.mob_entity.target.lock().await = None; + pub fn deactivate(&self) { + self.mob_entity.set_target(None); self.play_sound(Sound::EntityCreakingDeactivate); self.set_is_active(false); } @@ -324,7 +324,7 @@ impl CreakingEntity { false } - pub async fn check_can_move(&self) -> bool { + pub fn check_can_move(&self) -> bool { let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -333,7 +333,7 @@ impl CreakingEntity { let players = world.get_nearby_players(pos, 32.0); if players.is_empty() { if active { - self.deactivate().await; + self.deactivate(); } return true; } @@ -353,7 +353,7 @@ impl CreakingEntity { let target_pos = player.get_entity().pos.load(); let dist_sq = pos.squared_distance_to(target_pos.x, target_pos.y, target_pos.z); if dist_sq < ACTIVATION_RANGE_SQ { - self.activate(&player).await; + self.activate(&player); return false; } } @@ -361,13 +361,13 @@ impl CreakingEntity { } if !has_potential_target && active { - self.deactivate().await; + self.deactivate(); } true } - pub async fn tear_down(&self) { + pub fn tear_down(&self) { let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -381,7 +381,7 @@ impl CreakingEntity { ); self.play_sound(Sound::EntityCreakingDeath); - entity.remove().await; + entity.remove(); } pub fn creaking_death_effects(&self) { @@ -425,118 +425,109 @@ impl Mob for CreakingEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() { + return; + } + if self + .invulnerability_animation_remaining_ticks + .load(Ordering::Relaxed) + > 0 + { + self.invulnerability_animation_remaining_ticks + .fetch_sub(1, Ordering::Relaxed); + } + if self + .attack_animation_remaining_ticks + .load(Ordering::Relaxed) + > 0 + { + self.attack_animation_remaining_ticks + .fetch_sub(1, Ordering::Relaxed); + } + + let can_move = self.can_move(); + let now_can_move = self.check_can_move(); + + if now_can_move != can_move { + let world = entity.world.load(); + if now_can_move { + world.play_sound_fine( + Sound::EntityCreakingUnfreeze, + SoundCategory::Hostile, + &entity.pos.load(), + 1.0, + 1.0, + ); + } else { + self.stop_in_place(); + world.play_sound_fine( + Sound::EntityCreakingFreeze, + SoundCategory::Hostile, + &entity.pos.load(), + 1.0, + 1.0, + ); + } + self.set_can_move(now_can_move); + } + + // Home Creaking Heart check + if let Some(home_pos) = self.get_home_pos() { + let world = entity.world.load(); + let has_protection = world.get_block_entity(&home_pos).is_some_and(|be| { + be.as_any() + .downcast_ref::() + .is_some_and(|heart_be| heart_be.is_protector(entity.entity_uuid)) + }); + + if !has_protection { + self.mob_entity.living_entity.health.store(0.0); + } + } + + // Teardown / death tick + if self.is_heart_bound() && self.is_tearing_down() { + let death_time = self.death_time.fetch_add(1, Ordering::Relaxed) + 1; + if death_time > TWITCH_DEATH_DURATION && !entity.is_removed() { + self.tear_down(); + } + } + } + + fn pre_damage(&self, damage_type: DamageType, _source: Option<&dyn EntityBase>) -> bool { + let entity = &self.mob_entity.living_entity.entity; + + if self.is_heart_bound() && damage_type != DamageType::OUT_OF_WORLD { if self .invulnerability_animation_remaining_ticks .load(Ordering::Relaxed) - > 0 + <= 0 + && entity.is_alive() { self.invulnerability_animation_remaining_ticks - .fetch_sub(1, Ordering::Relaxed); - } - if self - .attack_animation_remaining_ticks - .load(Ordering::Relaxed) - > 0 - { - self.attack_animation_remaining_ticks - .fetch_sub(1, Ordering::Relaxed); - } + .store(8, Ordering::Relaxed); - let can_move = self.can_move(); - let now_can_move = self.check_can_move().await; - - if now_can_move != can_move { let world = entity.world.load(); - if now_can_move { - world.play_sound_fine( - Sound::EntityCreakingUnfreeze, - SoundCategory::Hostile, - &entity.pos.load(), - 1.0, - 1.0, - ); - } else { - self.stop_in_place(); - world.play_sound_fine( - Sound::EntityCreakingFreeze, - SoundCategory::Hostile, - &entity.pos.load(), - 1.0, - 1.0, - ); - } - self.set_can_move(now_can_move); - } + world.broadcast_to_chunk( + entity.chunk_pos.load(), + &CEntityStatus::new(entity.entity_id, 66), + ); - // Home Creaking Heart check - if let Some(home_pos) = self.get_home_pos() { - let world = entity.world.load(); - let has_protection = world.get_block_entity(&home_pos).is_some_and(|be| { - be.as_any() - .downcast_ref::() - .is_some_and(|heart_be| heart_be.is_protector(entity.entity_uuid)) - }); - - if !has_protection { - self.mob_entity.living_entity.health.store(0.0); - } - } - - // Teardown / death tick - if self.is_heart_bound() && self.is_tearing_down() { - let death_time = self.death_time.fetch_add(1, Ordering::Relaxed) + 1; - if death_time > TWITCH_DEATH_DURATION && !entity.is_removed() { - self.tear_down().await; - } - } - }) - } - - fn pre_damage<'a>( - &'a self, - damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - - if self.is_heart_bound() && damage_type != DamageType::OUT_OF_WORLD { - if self - .invulnerability_animation_remaining_ticks - .load(Ordering::Relaxed) - <= 0 - && entity.is_alive() + if let Some(home_pos) = self.get_home_pos() + && let Some(be) = world.get_block_entity(&home_pos) + && let Some(heart_be) = be.as_any().downcast_ref::() + && heart_be.is_protector(entity.entity_uuid) { - self.invulnerability_animation_remaining_ticks - .store(8, Ordering::Relaxed); - - let world = entity.world.load(); - world.broadcast_to_chunk( - entity.chunk_pos.load(), - &CEntityStatus::new(entity.entity_id, 66), - ); - - if let Some(home_pos) = self.get_home_pos() - && let Some(be) = world.get_block_entity(&home_pos) - && let Some(heart_be) = - be.as_any().downcast_ref::() - && heart_be.is_protector(entity.entity_uuid) - { - heart_be.creaking_hurt(&world); - self.play_sound(Sound::EntityCreakingSway); - } + heart_be.creaking_hurt(&world); + self.play_sound(Sound::EntityCreakingSway); } - // Return false so heart-bound Creaking does not lose health from normal damage - return false; } - true - }) + // Return false so heart-bound Creaking does not lose health from normal damage + return false; + } + true } } diff --git a/crates/pumpkin/src/entity/mob/creeper.rs b/crates/pumpkin/src/entity/mob/creeper.rs index 405e74db0..80795334a 100644 --- a/crates/pumpkin/src/entity/mob/creeper.rs +++ b/crates/pumpkin/src/entity/mob/creeper.rs @@ -123,7 +123,7 @@ impl CreeperEntity { ) .await; // TODO: spawn area effect cloud with potion effects - entity.remove().await; + entity.remove(); } } @@ -183,45 +183,48 @@ impl Mob for CreeperEntity { }) } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + fn mob_tick<'a>(&'a self, caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() { + return; + } - self.last_fuse_time.store( - self.current_fuse_time.load(Ordering::Relaxed), - Ordering::Relaxed, + self.last_fuse_time.store( + self.current_fuse_time.load(Ordering::Relaxed), + Ordering::Relaxed, + ); + + if self.ignited.load(Ordering::Relaxed) { + self.set_fuse_speed(1); + } + + let fuse_speed = self.fuse_speed.load(Ordering::Relaxed); + let current = self.current_fuse_time.load(Ordering::Relaxed); + + if fuse_speed > 0 && current == 0 { + let world = entity.world.load(); + world.play_sound_fine( + Sound::EntityCreeperPrimed, + SoundCategory::Hostile, + &entity.pos.load(), + 1.0, + 0.5, ); + } - if self.ignited.load(Ordering::Relaxed) { - self.set_fuse_speed(1); - } + let fuse_time = self.fuse_time.load(Ordering::Relaxed); + let new_fuse = (current + fuse_speed).max(0); + self.current_fuse_time.store(new_fuse, Ordering::Relaxed); - let fuse_speed = self.fuse_speed.load(Ordering::Relaxed); - let current = self.current_fuse_time.load(Ordering::Relaxed); - - if fuse_speed > 0 && current == 0 { - let world = entity.world.load(); - world.play_sound_fine( - Sound::EntityCreeperPrimed, - SoundCategory::Hostile, - &entity.pos.load(), - 1.0, - 0.5, - ); - } - - let fuse_time = self.fuse_time.load(Ordering::Relaxed); - let new_fuse = (current + fuse_speed).max(0); - self.current_fuse_time.store(new_fuse, Ordering::Relaxed); - - if new_fuse >= fuse_time { - self.current_fuse_time.store(fuse_time, Ordering::Relaxed); - self.explode().await; - } - }) + if new_fuse >= fuse_time { + self.current_fuse_time.store(fuse_time, Ordering::Relaxed); + let caller_clone = caller.clone(); + tokio::spawn(async move { + if let Some(creeper) = caller_clone.cast_any().downcast_ref::() { + creeper.explode().await; + } + }); + } } fn mob_interact<'a>( @@ -231,7 +234,7 @@ impl Mob for CreeperEntity { ) -> EntityBaseFuture<'a, bool> { Box::pin(async move { if item_stack.item.id != Item::FLINT_AND_STEEL.id { - return self.mob_entity.mob_interact(player, item_stack).await; + return self.mob_entity.mob_interact(player, item_stack); } let entity = &self.mob_entity.living_entity.entity; diff --git a/crates/pumpkin/src/entity/mob/enderman.rs b/crates/pumpkin/src/entity/mob/enderman.rs index 7db9cb7b1..76b3006cf 100644 --- a/crates/pumpkin/src/entity/mob/enderman.rs +++ b/crates/pumpkin/src/entity/mob/enderman.rs @@ -30,7 +30,7 @@ use crate::entity::{ Entity, EntityBase, NbtFuture, ai::{ goal::{ - GoalFuture, active_target::ActiveTargetGoal, chase_player::ChasePlayerGoal, + active_target::ActiveTargetGoal, chase_player::ChasePlayerGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, pick_up_block::PickUpBlockGoal, place_block::PlaceBlockGoal, revenge::RevengeGoal, swim::SwimGoal, @@ -233,10 +233,7 @@ impl EndermanEntity { origin, new_pos, ); - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(server.plugin_manager.fire(&server, &mut event)); - }); + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return false; } @@ -265,12 +262,17 @@ impl EndermanEntity { true } - pub async fn set_target(&self, target: Option>) { - let mut mob_target = self.mob_entity.target.lock().await; - (*mob_target).clone_from(&target); + pub fn set_target(&self, target: Option>) { + let is_some = target.is_some(); + let mut mob_target = self + .mob_entity + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *mob_target = target; drop(mob_target); - if target.is_some() { + if is_some { self.set_angry(true); // Use attribute modifier instead of direct speed arithmetic if !self.speed_boosted.swap(true, Ordering::Relaxed) { @@ -288,8 +290,7 @@ impl EndermanEntity { crate::entity::attributes::send_attribute_updates_for_living( living, vec![Attributes::MOVEMENT_SPEED], - ) - .await; + ); } } else { self.set_angry(false); @@ -304,8 +305,7 @@ impl EndermanEntity { crate::entity::attributes::send_attribute_updates_for_living( living, vec![Attributes::MOVEMENT_SPEED], - ) - .await; + ); } } } @@ -352,7 +352,7 @@ impl EndermanEntity { self.carried_block.load() } - pub async fn is_player_staring(&self, player: &Player) -> bool { + pub fn is_player_staring(&self, player: &Player) -> bool { let equipment = player.living_entity.entity_equipment.try_lock(); if let Ok(equipment) = equipment && let Some(head_stack) = equipment.equipment.get(&EquipmentSlot::HEAD) @@ -403,11 +403,10 @@ impl EndermanEntity { let player_eye_pos = Vector3::new(player_pos.x, player_eye_y, player_pos.z); let world = entity.world.load(); world - .raycast(enderman_eye_pos, player_eye_pos, async |block_pos, w| { + .raycast(enderman_eye_pos, player_eye_pos, |block_pos, w| { let state = w.get_block_state(block_pos); state.is_solid() }) - .await .is_none() } } @@ -433,70 +432,50 @@ impl Mob for EndermanEntity { &self.mob_entity } - fn set_mob_target(&self, target: Option>) -> GoalFuture<'_, ()> { - Box::pin(async move { - self.set_target(target).await; - }) + fn set_mob_target(&self, target: Option>) { + self.set_target(target); } // TODO: sunlight avoidance, carried block drop on death, angerable system, ambient sound override - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> GoalFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() { + return; + } - let world = entity.world.load(); - let raining_at_feet = world.is_raining_at(&entity.block_pos.load()).await; - let raining_at_head = world - .is_raining_at(&entity.bounding_box.load().max_block_pos()) - .await; - if entity.touching_water.load(Ordering::SeqCst) || raining_at_feet || raining_at_head { - self.mob_entity - .living_entity - .damage_with_context(self, 1.0, DamageType::DROWN, None, None, None) - .await; - } - - // NOTE: Enderman ambient portal particles are intentionally NOT sent server-side. - // The vanilla Minecraft client generates these particles locally in the entity - // renderer. Sending them from the server would cause duplicate particles and - // massive network overhead (2 packets/tick/enderman = 40 packets/sec/enderman). - }) + let world = entity.world.load(); + let raining_at_feet = world.is_raining_at(&entity.block_pos.load()); + let raining_at_head = world.is_raining_at(&entity.bounding_box.load().max_block_pos()); + if entity.touching_water.load(Ordering::SeqCst) || raining_at_feet || raining_at_head { + let entity_id = entity.entity_id; + let world_full = entity.world.load_full(); + tokio::spawn(async move { + if let Some(entity) = world_full.get_entity_by_id(entity_id) { + entity.damage(entity.as_ref(), 1.0, DamageType::DROWN); + } + }); + } } - fn pre_damage<'a>( - &'a self, - damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> GoalFuture<'a, bool> { + fn pre_damage(&self, damage_type: DamageType, _source: Option<&dyn EntityBase>) -> bool { let is_projectile = is_projectile_damage(damage_type); - Box::pin(async move { - if is_projectile { - for _ in 0..64 { - if self.teleport_randomly() { - return false; - } + if is_projectile { + for _ in 0..64 { + if self.teleport_randomly() { + return false; } } - true - }) + } + true } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - source: Option<&'a dyn EntityBase>, - ) -> GoalFuture<'a, ()> { - Box::pin(async move { - if source.is_some_and(|s| s.get_living_entity().is_some()) { - return; - } - let should_teleport = self.get_random().random_range(0..10) != 0; - if should_teleport { - self.teleport_randomly(); - } - }) + fn on_damage(&self, _damage_type: DamageType, source: Option<&dyn EntityBase>) { + if source.is_some_and(|s| s.get_living_entity().is_some()) { + return; + } + let should_teleport = self.get_random().random_range(0..10) != 0; + if should_teleport { + self.teleport_randomly(); + } } } diff --git a/crates/pumpkin/src/entity/mob/equipment.rs b/crates/pumpkin/src/entity/mob/equipment.rs index 2a6eade26..361cd024a 100644 --- a/crates/pumpkin/src/entity/mob/equipment.rs +++ b/crates/pumpkin/src/entity/mob/equipment.rs @@ -1008,7 +1008,7 @@ fn equip_mob_from_def( /// and broadcasts the changes to nearby players. /// /// Mobs not listed in the registry silently receive no equipment. -pub async fn equip_mob_on_spawn(mob: &dyn EntityBase, world: &Arc) { +pub fn equip_mob_on_spawn(mob: &dyn EntityBase, world: &Arc) { let entity_type = mob.get_entity().entity_type; let pos = mob.get_entity().pos.load(); let difficulty = RegionalDifficulty::at(world, pos); @@ -1023,8 +1023,14 @@ pub async fn equip_mob_on_spawn(mob: &dyn EntityBase, world: &Arc = Vec::new(); diff --git a/crates/pumpkin/src/entity/mob/evoker.rs b/crates/pumpkin/src/entity/mob/evoker.rs index d2dab4a18..af3bdf6e4 100644 --- a/crates/pumpkin/src/entity/mob/evoker.rs +++ b/crates/pumpkin/src/entity/mob/evoker.rs @@ -1,6 +1,6 @@ +use std::sync::Mutex; use std::sync::atomic::{AtomicI32, AtomicU8, Ordering}; use std::sync::{Arc, Weak}; -use tokio::sync::Mutex; use uuid::Uuid; use pumpkin_data::entity::EntityType; @@ -10,11 +10,10 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ai::goal::{ - Controls, Goal, GoalFuture, active_target::ActiveTargetGoal, - look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, - wander_around::WanderAroundGoal, + Controls, Goal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, + look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, }, mob::{ Mob, MobEntity, @@ -182,14 +181,12 @@ impl Mob for EvokerEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let ticks = self.spell_casting_tick_count.load(Ordering::Relaxed); - if ticks > 0 { - self.spell_casting_tick_count - .store(ticks - 1, Ordering::Relaxed); - } - }) + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let ticks = self.spell_casting_tick_count.load(Ordering::Relaxed); + if ticks > 0 { + self.spell_casting_tick_count + .store(ticks - 1, Ordering::Relaxed); + } } } @@ -221,43 +218,35 @@ impl EvokerCastingSpellGoal { } impl Goal for EvokerCastingSpellGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(evoker) = self.evoker.upgrade() else { - return false; - }; - evoker.is_casting_spell() - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(evoker) = self.evoker.upgrade() else { + return false; + }; + evoker.is_casting_spell() } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(evoker) = self.evoker.upgrade() else { - return false; - }; - evoker.is_casting_spell() - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(evoker) = self.evoker.upgrade() else { + return false; + }; + evoker.is_casting_spell() } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(evoker) = self.evoker.upgrade() { - evoker - .mob_entity - .living_entity - .entity - .velocity - .store(Vector3::new(0.0, 0.0, 0.0)); - } - }) + fn start(&mut self, _mob: &dyn Mob) { + if let Some(evoker) = self.evoker.upgrade() { + evoker + .mob_entity + .living_entity + .entity + .velocity + .store(Vector3::new(0.0, 0.0, 0.0)); + } } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(evoker) = self.evoker.upgrade() { - evoker.set_is_casting_spell(IllagerSpell::None); - } - }) + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(evoker) = self.evoker.upgrade() { + evoker.set_is_casting_spell(IllagerSpell::None); + } } fn controls(&self) -> Controls { @@ -283,138 +272,130 @@ impl EvokerAttackSpellGoal { } impl Goal for EvokerAttackSpellGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(evoker) = self.evoker.upgrade() else { + return false; + }; + if evoker.is_casting_spell() { + return false; + } + let entity = &evoker.mob_entity.living_entity.entity; + if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { + return false; + } + let target = evoker.mob_entity.get_target(); + target.is_some() + } + + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.warmup_delay > 0 + } + + fn start(&mut self, _mob: &dyn Mob) { + self.warmup_delay = 20; + if let Some(evoker) = self.evoker.upgrade() { + evoker.set_spell_casting_time(40); + let age = evoker + .mob_entity + .living_entity + .entity + .age + .load(Ordering::Relaxed); + self.next_attack_tick = age + 100; + evoker.set_is_casting_spell(IllagerSpell::Fangs); + evoker + .mob_entity + .living_entity + .entity + .play_sound(Sound::EntityEvokerPrepareAttack); + } + } + + fn tick(&mut self, _mob: &dyn Mob) { + self.warmup_delay -= 1; + if self.warmup_delay == 0 { let Some(evoker) = self.evoker.upgrade() else { - return false; + return; + }; + let target = evoker.mob_entity.get_target(); + let Some(target) = target else { + return; }; - if evoker.is_casting_spell() { - return false; - } - let entity = &evoker.mob_entity.living_entity.entity; - if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { - return false; - } - let target = evoker.mob_entity.target.lock().await.clone(); - target.is_some() - }) - } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.warmup_delay > 0 }) - } + let evoker_ent = &evoker.mob_entity.living_entity.entity; + evoker_ent.play_sound(Sound::EntityEvokerCastSpell); - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay = 20; - if let Some(evoker) = self.evoker.upgrade() { - evoker.set_spell_casting_time(40); - let age = evoker - .mob_entity - .living_entity - .entity - .age - .load(Ordering::Relaxed); - self.next_attack_tick = age + 100; - evoker.set_is_casting_spell(IllagerSpell::Fangs); - evoker - .mob_entity - .living_entity - .entity - .play_sound(Sound::EntityEvokerPrepareAttack); - } - }) - } + let evoker_pos = evoker_ent.pos.load(); + let target_pos = target.get_entity().pos.load(); - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay -= 1; - if self.warmup_delay == 0 { - let Some(evoker) = self.evoker.upgrade() else { - return; - }; - let target = evoker.mob_entity.target.lock().await.clone(); - let Some(target) = target else { - return; - }; + let dx = target_pos.x - evoker_pos.x; + let dz = target_pos.z - evoker_pos.z; + let angle_towards_target = (dz.atan2(dx)) as f32; - let evoker_ent = &evoker.mob_entity.living_entity.entity; - evoker_ent.play_sound(Sound::EntityEvokerCastSpell); + let min_y = evoker_pos.y.min(target_pos.y); + let dist_sq = evoker_pos.squared_distance_to_vec(&target_pos); + let world = evoker_ent.world.load(); + let evoker_id = evoker_ent.entity_id; - let evoker_pos = evoker_ent.pos.load(); - let target_pos = target.get_entity().pos.load(); + if dist_sq < 81.0 { + // Close range: concentric rings around Evoker + for i in 0..5 { + let angle = angle_towards_target + (i as f32) * std::f32::consts::PI * 0.4; + let spawn_x = evoker_pos.x + (angle.cos() as f64) * 1.5; + let spawn_z = evoker_pos.z + (angle.sin() as f64) * 1.5; + let pos = Vector3::new(spawn_x, min_y, spawn_z); - let dx = target_pos.x - evoker_pos.x; - let dz = target_pos.z - evoker_pos.z; - let angle_towards_target = (dz.atan2(dx)) as f32; + let entity = Entity::from_uuid( + Uuid::new_v4(), + world.clone(), + pos, + &EntityType::EVOKER_FANGS, + ); + let fangs = Arc::new(EvokerFangsEntity::new(entity, 0, angle, Some(evoker_id))); + world.spawn_entity(fangs); + } - let min_y = evoker_pos.y.min(target_pos.y); - let dist_sq = evoker_pos.squared_distance_to_vec(&target_pos); - let world = evoker_ent.world.load(); - let evoker_id = evoker_ent.entity_id; + for i in 0..8 { + let angle = angle_towards_target + + (i as f32) * std::f32::consts::PI * 2.0 / 8.0 + + 1.256_637_1; + let spawn_x = evoker_pos.x + (angle.cos() as f64) * 2.5; + let spawn_z = evoker_pos.z + (angle.sin() as f64) * 2.5; + let pos = Vector3::new(spawn_x, min_y, spawn_z); - if dist_sq < 81.0 { - // Close range: concentric rings around Evoker - for i in 0..5 { - let angle = angle_towards_target + (i as f32) * std::f32::consts::PI * 0.4; - let spawn_x = evoker_pos.x + (angle.cos() as f64) * 1.5; - let spawn_z = evoker_pos.z + (angle.sin() as f64) * 1.5; - let pos = Vector3::new(spawn_x, min_y, spawn_z); + let entity = Entity::from_uuid( + Uuid::new_v4(), + world.clone(), + pos, + &EntityType::EVOKER_FANGS, + ); + let fangs = Arc::new(EvokerFangsEntity::new(entity, 3, angle, Some(evoker_id))); + world.spawn_entity(fangs); + } + } else { + // Long range: line of fangs towards target + for i in 0..16 { + let reach = 1.25 * ((i + 1) as f64); + let spawn_x = evoker_pos.x + (angle_towards_target.cos() as f64) * reach; + let spawn_z = evoker_pos.z + (angle_towards_target.sin() as f64) * reach; + let pos = Vector3::new(spawn_x, min_y, spawn_z); - let entity = Entity::from_uuid( - Uuid::new_v4(), - world.clone(), - pos, - &EntityType::EVOKER_FANGS, - ); - let fangs = - Arc::new(EvokerFangsEntity::new(entity, 0, angle, Some(evoker_id))); - world.spawn_entity(fangs).await; - } - - for i in 0..8 { - let angle = angle_towards_target - + (i as f32) * std::f32::consts::PI * 2.0 / 8.0 - + 1.256_637_1; - let spawn_x = evoker_pos.x + (angle.cos() as f64) * 2.5; - let spawn_z = evoker_pos.z + (angle.sin() as f64) * 2.5; - let pos = Vector3::new(spawn_x, min_y, spawn_z); - - let entity = Entity::from_uuid( - Uuid::new_v4(), - world.clone(), - pos, - &EntityType::EVOKER_FANGS, - ); - let fangs = - Arc::new(EvokerFangsEntity::new(entity, 3, angle, Some(evoker_id))); - world.spawn_entity(fangs).await; - } - } else { - // Long range: line of fangs towards target - for i in 0..16 { - let reach = 1.25 * ((i + 1) as f64); - let spawn_x = evoker_pos.x + (angle_towards_target.cos() as f64) * reach; - let spawn_z = evoker_pos.z + (angle_towards_target.sin() as f64) * reach; - let pos = Vector3::new(spawn_x, min_y, spawn_z); - - let entity = Entity::from_uuid( - Uuid::new_v4(), - world.clone(), - pos, - &EntityType::EVOKER_FANGS, - ); - let fangs = Arc::new(EvokerFangsEntity::new( - entity, - i as u32, - angle_towards_target, - Some(evoker_id), - )); - world.spawn_entity(fangs).await; - } + let entity = Entity::from_uuid( + Uuid::new_v4(), + world.clone(), + pos, + &EntityType::EVOKER_FANGS, + ); + let fangs = Arc::new(EvokerFangsEntity::new( + entity, + i as u32, + angle_towards_target, + Some(evoker_id), + )); + world.spawn_entity(fangs); } } - }) + } } } @@ -436,90 +417,84 @@ impl EvokerSummonSpellGoal { } impl Goal for EvokerSummonSpellGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(evoker) = self.evoker.upgrade() else { + return false; + }; + if evoker.is_casting_spell() { + return false; + } + let entity = &evoker.mob_entity.living_entity.entity; + if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { + return false; + } + let target = evoker.mob_entity.get_target(); + if target.is_none() { + return false; + } + + // Count nearby Vexes + let bb = entity.bounding_box.load().expand(16.0, 16.0, 16.0); + let world = entity.world.load(); + let nearby = world.get_entities_at_box(&bb); + let vex_count = nearby + .iter() + .filter(|e| *e.get_entity().entity_type == EntityType::VEX) + .count(); + + let max_allowed = (rand::random::() % 8 + 1) as usize; + vex_count < max_allowed + } + + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.warmup_delay > 0 + } + + fn start(&mut self, _mob: &dyn Mob) { + self.warmup_delay = 20; + if let Some(evoker) = self.evoker.upgrade() { + evoker.set_spell_casting_time(100); + let age = evoker + .mob_entity + .living_entity + .entity + .age + .load(Ordering::Relaxed); + self.next_attack_tick = age + 340; + evoker.set_is_casting_spell(IllagerSpell::SummonVex); + evoker + .mob_entity + .living_entity + .entity + .play_sound(Sound::EntityEvokerPrepareSummon); + } + } + + fn tick(&mut self, _mob: &dyn Mob) { + self.warmup_delay -= 1; + if self.warmup_delay == 0 { let Some(evoker) = self.evoker.upgrade() else { - return false; + return; }; - if evoker.is_casting_spell() { - return false; + let evoker_ent = &evoker.mob_entity.living_entity.entity; + evoker_ent.play_sound(Sound::EntityEvokerCastSpell); + + let world = evoker_ent.world.load(); + let evoker_pos = evoker_ent.pos.load(); + + for _ in 0..3 { + let offset_x = (rand::random::() % 5 - 2) as f64; + let offset_z = (rand::random::() % 5 - 2) as f64; + let spawn_pos = Vector3::new( + evoker_pos.x + offset_x, + evoker_pos.y + 1.0, + evoker_pos.z + offset_z, + ); + + let vex = from_type(&EntityType::VEX, spawn_pos, &world, Uuid::new_v4()); + world.spawn_entity(vex); } - let entity = &evoker.mob_entity.living_entity.entity; - if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { - return false; - } - let target = evoker.mob_entity.target.lock().await.clone(); - if target.is_none() { - return false; - } - - // Count nearby Vexes - let bb = entity.bounding_box.load().expand(16.0, 16.0, 16.0); - let world = entity.world.load(); - let nearby = world.get_entities_at_box(&bb); - let vex_count = nearby - .iter() - .filter(|e| *e.get_entity().entity_type == EntityType::VEX) - .count(); - - let max_allowed = (rand::random::() % 8 + 1) as usize; - vex_count < max_allowed - }) - } - - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.warmup_delay > 0 }) - } - - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay = 20; - if let Some(evoker) = self.evoker.upgrade() { - evoker.set_spell_casting_time(100); - let age = evoker - .mob_entity - .living_entity - .entity - .age - .load(Ordering::Relaxed); - self.next_attack_tick = age + 340; - evoker.set_is_casting_spell(IllagerSpell::SummonVex); - evoker - .mob_entity - .living_entity - .entity - .play_sound(Sound::EntityEvokerPrepareSummon); - } - }) - } - - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay -= 1; - if self.warmup_delay == 0 { - let Some(evoker) = self.evoker.upgrade() else { - return; - }; - let evoker_ent = &evoker.mob_entity.living_entity.entity; - evoker_ent.play_sound(Sound::EntityEvokerCastSpell); - - let world = evoker_ent.world.load(); - let evoker_pos = evoker_ent.pos.load(); - - for _ in 0..3 { - let offset_x = (rand::random::() % 5 - 2) as f64; - let offset_z = (rand::random::() % 5 - 2) as f64; - let spawn_pos = Vector3::new( - evoker_pos.x + offset_x, - evoker_pos.y + 1.0, - evoker_pos.z + offset_z, - ); - - let vex = from_type(&EntityType::VEX, spawn_pos, &world, Uuid::new_v4()); - world.spawn_entity(vex).await; - } - } - }) + } } } @@ -541,109 +516,111 @@ impl EvokerWololoSpellGoal { } impl Goal for EvokerWololoSpellGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(evoker) = self.evoker.upgrade() else { - return false; - }; - if evoker.is_casting_spell() { - return false; - } - let entity = &evoker.mob_entity.living_entity.entity; - if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { - return false; - } - let target = evoker.mob_entity.target.lock().await.clone(); - if target.is_some() { - return false; - } + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(evoker) = self.evoker.upgrade() else { + return false; + }; + if evoker.is_casting_spell() { + return false; + } + let entity = &evoker.mob_entity.living_entity.entity; + if entity.age.load(Ordering::Relaxed) < self.next_attack_tick { + return false; + } + let target = evoker.mob_entity.get_target(); + if target.is_some() { + return false; + } - // Find blue sheep within 16 blocks - let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); - let world = entity.world.load(); + // Find blue sheep within 16 blocks + let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); + let world = entity.world.load(); + let candidates = world.get_entities_at_box(&bb); + + for cand in candidates { + if *cand.get_entity().entity_type == EntityType::SHEEP + && let Some(mob) = cand.get_mob() + && let Some(sheep) = mob.get_sheep() + { + // Blue color is 11 in Minecraft + if sheep.get_color() == 11 { + *evoker + .wololo_target_id + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + Some(cand.get_entity().entity_id); + return true; + } + } + } + + false + } + + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.warmup_delay > 0 + } + + fn start(&mut self, _mob: &dyn Mob) { + self.warmup_delay = 40; + if let Some(evoker) = self.evoker.upgrade() { + evoker.set_spell_casting_time(60); + let age = evoker + .mob_entity + .living_entity + .entity + .age + .load(Ordering::Relaxed); + self.next_attack_tick = age + 140; + evoker.set_is_casting_spell(IllagerSpell::Wololo); + evoker + .mob_entity + .living_entity + .entity + .play_sound(Sound::EntityEvokerPrepareWololo); + } + } + + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(evoker) = self.evoker.upgrade() { + *evoker + .wololo_target_id + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } + } + + fn tick(&mut self, _mob: &dyn Mob) { + self.warmup_delay -= 1; + if self.warmup_delay == 0 { + let Some(evoker) = self.evoker.upgrade() else { + return; + }; + let evoker_ent = &evoker.mob_entity.living_entity.entity; + evoker_ent.play_sound(Sound::EntityEvokerCastSpell); + + let target_id = *evoker + .wololo_target_id + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some(target_id) = target_id else { + return; + }; + + let world = evoker_ent.world.load(); + let bb = evoker_ent.bounding_box.load().expand(16.0, 4.0, 16.0); let candidates = world.get_entities_at_box(&bb); for cand in candidates { - if *cand.get_entity().entity_type == EntityType::SHEEP + if cand.get_entity().entity_id == target_id && let Some(mob) = cand.get_mob() && let Some(sheep) = mob.get_sheep() { - // Blue color is 11 in Minecraft - if sheep.get_color() == 11 { - *evoker.wololo_target_id.lock().await = Some(cand.get_entity().entity_id); - return true; - } + // Convert color to Red (14) + sheep.set_color(14); + break; } } - - false - }) - } - - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { self.warmup_delay > 0 }) - } - - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay = 40; - if let Some(evoker) = self.evoker.upgrade() { - evoker.set_spell_casting_time(60); - let age = evoker - .mob_entity - .living_entity - .entity - .age - .load(Ordering::Relaxed); - self.next_attack_tick = age + 140; - evoker.set_is_casting_spell(IllagerSpell::Wololo); - evoker - .mob_entity - .living_entity - .entity - .play_sound(Sound::EntityEvokerPrepareWololo); - } - }) - } - - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(evoker) = self.evoker.upgrade() { - *evoker.wololo_target_id.lock().await = None; - } - }) - } - - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.warmup_delay -= 1; - if self.warmup_delay == 0 { - let Some(evoker) = self.evoker.upgrade() else { - return; - }; - let evoker_ent = &evoker.mob_entity.living_entity.entity; - evoker_ent.play_sound(Sound::EntityEvokerCastSpell); - - let target_id = *evoker.wololo_target_id.lock().await; - let Some(target_id) = target_id else { - return; - }; - - let world = evoker_ent.world.load(); - let bb = evoker_ent.bounding_box.load().expand(16.0, 4.0, 16.0); - let candidates = world.get_entities_at_box(&bb); - - for cand in candidates { - if cand.get_entity().entity_id == target_id - && let Some(mob) = cand.get_mob() - && let Some(sheep) = mob.get_sheep() - { - // Convert color to Red (14) - sheep.set_color(14); - break; - } - } - } - }) + } } } diff --git a/crates/pumpkin/src/entity/mob/ghast.rs b/crates/pumpkin/src/entity/mob/ghast.rs index fca2d1b60..a549ecc46 100644 --- a/crates/pumpkin/src/entity/mob/ghast.rs +++ b/crates/pumpkin/src/entity/mob/ghast.rs @@ -1,5 +1,5 @@ use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, Mutex, Weak}; use pumpkin_data::damage::DamageType; use pumpkin_data::entity::EntityType; @@ -10,14 +10,13 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; -use tokio::sync::Mutex; use crate::entity::ai::goal::active_target::ActiveTargetGoal; use crate::entity::living::LivingEntity; use crate::entity::projectile::fireball::FireballEntity; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, - ai::goal::{Controls, Goal, GoalFuture}, + Entity, EntityBase, NbtFuture, + ai::goal::{Controls, Goal}, mob::{Mob, MobEntity}, }; use crate::world::World; @@ -82,9 +81,7 @@ impl GhastEntity { 10, true, false, - Some(|_target: Arc, _world: Arc| { - Box::pin(async move { true }) - }), + Some(|_target: &LivingEntity, _world: &World| true), )), ); }; @@ -144,16 +141,14 @@ impl Mob for GhastEntity { Some(0.95) } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - if self.is_charging() { - entity.send_meta_data( - &[Metadata::new(tracked_data::ghast::DATA_IS_CHARGING, true)], - None, - ); - } - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + if self.is_charging() { + entity.send_meta_data( + &[Metadata::new(tracked_data::ghast::DATA_IS_CHARGING, true)], + None, + ); + } } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { @@ -206,39 +201,37 @@ impl GhastLookGoal { } impl Goal for GhastLookGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { true }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + true } fn should_run_every_tick(&self) -> bool { true } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - let target_opt = mob_entity.target.lock().await.clone(); + fn tick(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + let target_opt = mob_entity.get_target(); - if let Some(target) = target_opt { - let mob_pos = mob_entity.living_entity.entity.pos.load(); - let target_pos = target.get_entity().pos.load(); + if let Some(target) = target_opt { + let mob_pos = mob_entity.living_entity.entity.pos.load(); + let target_pos = target.get_entity().pos.load(); - if mob_pos.squared_distance_to_vec(&target_pos) < 4096.0 { - let dx = target_pos.x - mob_pos.x; - let dz = target_pos.z - mob_pos.z; - let yaw = (-f64::atan2(dx, dz).to_degrees()) as f32; - mob_entity.living_entity.entity.yaw.store(yaw); - mob_entity.living_entity.entity.head_yaw.store(yaw); - } - } else { - let velocity = mob_entity.living_entity.entity.velocity.load(); - if velocity.x != 0.0 || velocity.z != 0.0 { - let yaw = (-f64::atan2(velocity.x, velocity.z).to_degrees()) as f32; - mob_entity.living_entity.entity.yaw.store(yaw); - mob_entity.living_entity.entity.head_yaw.store(yaw); - } + if mob_pos.squared_distance_to_vec(&target_pos) < 4096.0 { + let dx = target_pos.x - mob_pos.x; + let dz = target_pos.z - mob_pos.z; + let yaw = (-f64::atan2(dx, dz).to_degrees()) as f32; + mob_entity.living_entity.entity.yaw.store(yaw); + mob_entity.living_entity.entity.head_yaw.store(yaw); } - }) + } else { + let velocity = mob_entity.living_entity.entity.velocity.load(); + if velocity.x != 0.0 || velocity.z != 0.0 { + let yaw = (-f64::atan2(velocity.x, velocity.z).to_degrees()) as f32; + mob_entity.living_entity.entity.yaw.store(yaw); + mob_entity.living_entity.entity.head_yaw.store(yaw); + } + } } fn controls(&self) -> Controls { @@ -262,121 +255,111 @@ impl GhastShootFireballGoal { } impl Goal for GhastShootFireballGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return false; - }; - let target = ghast.mob_entity.target.lock().await.clone(); - target.is_some_and(|t| t.get_entity().is_alive()) - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(ghast) = self.ghast.upgrade() else { + return false; + }; + let target = ghast.mob_entity.get_target(); + target.is_some_and(|t| t.get_entity().is_alive()) } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return false; - }; - let target = ghast.mob_entity.target.lock().await.clone(); - target.is_some_and(|t| t.get_entity().is_alive()) - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(ghast) = self.ghast.upgrade() else { + return false; + }; + let target = ghast.mob_entity.get_target(); + target.is_some_and(|t| t.get_entity().is_alive()) } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.charge_time = 0; - }) + fn start(&mut self, _mob: &dyn Mob) { + self.charge_time = 0; } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(ghast) = self.ghast.upgrade() { - ghast.set_charging(false); - } - }) + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(ghast) = self.ghast.upgrade() { + ghast.set_charging(false); + } } fn should_run_every_tick(&self) -> bool { true } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return; - }; + fn tick(&mut self, _mob: &dyn Mob) { + let Some(ghast) = self.ghast.upgrade() else { + return; + }; - let target_opt = ghast.mob_entity.target.lock().await.clone(); - let Some(target) = target_opt else { - return; - }; + let target_opt = ghast.mob_entity.get_target(); + let Some(target) = target_opt else { + return; + }; - let entity = &ghast.mob_entity.living_entity.entity; - let ghast_pos = entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - let dist_sq = ghast_pos.squared_distance_to_vec(&target_pos); + let entity = &ghast.mob_entity.living_entity.entity; + let ghast_pos = entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = ghast_pos.squared_distance_to_vec(&target_pos); - if dist_sq < 4096.0 { - let world = entity.world.load(); - self.charge_time += 1; + if dist_sq < 4096.0 { + let world = entity.world.load(); + self.charge_time += 1; - if self.charge_time == 10 { - world.play_sound_fine( - Sound::EntityGhastWarn, - SoundCategory::Hostile, - &ghast_pos, - 5.0, - 1.0, - ); - } - - if self.charge_time == 20 { - world.play_sound_fine( - Sound::EntityGhastShoot, - SoundCategory::Hostile, - &ghast_pos, - 5.0, - 1.0, - ); - - let yaw_rad = f64::from(entity.yaw.load()).to_radians(); - let pitch_rad = f64::from(entity.pitch.load()).to_radians(); - let view_x = -pitch_rad.cos() * yaw_rad.sin(); - let view_z = pitch_rad.cos() * yaw_rad.cos(); - - let spawn_pos = Vector3::new( - ghast_pos.x + view_x * 4.0, - ghast_pos.y + 2.5, - ghast_pos.z + view_z * 4.0, - ); - - let target_y = target_pos.y + target.get_entity().get_eye_height() * 0.5; - let dir_x = target_pos.x - spawn_pos.x; - let dir_y = target_y - spawn_pos.y; - let dir_z = target_pos.z - spawn_pos.z; - let direction = Vector3::new(dir_x, dir_y, dir_z); - - let fireball_base = Entity::from_uuid( - uuid::Uuid::new_v4(), - world.clone(), - spawn_pos, - &EntityType::FIREBALL, - ); - - let fireball = FireballEntity::new_shot(fireball_base, entity, direction); - fireball - .explosion_power - .store(f32::from(ghast.get_explosion_power()), Ordering::Relaxed); - - world.spawn_entity(Arc::new(fireball)).await; - self.charge_time = -40; - } - } else if self.charge_time > 0 { - self.charge_time -= 1; + if self.charge_time == 10 { + world.play_sound_fine( + Sound::EntityGhastWarn, + SoundCategory::Hostile, + &ghast_pos, + 5.0, + 1.0, + ); } - ghast.set_charging(self.charge_time > 10); - }) + if self.charge_time == 20 { + world.play_sound_fine( + Sound::EntityGhastShoot, + SoundCategory::Hostile, + &ghast_pos, + 5.0, + 1.0, + ); + + let yaw_rad = f64::from(entity.yaw.load()).to_radians(); + let pitch_rad = f64::from(entity.pitch.load()).to_radians(); + let view_x = -pitch_rad.cos() * yaw_rad.sin(); + let view_z = pitch_rad.cos() * yaw_rad.cos(); + + let spawn_pos = Vector3::new( + ghast_pos.x + view_x * 4.0, + ghast_pos.y + 2.5, + ghast_pos.z + view_z * 4.0, + ); + + let target_y = target_pos.y + target.get_entity().get_eye_height() * 0.5; + let dir_x = target_pos.x - spawn_pos.x; + let dir_y = target_y - spawn_pos.y; + let dir_z = target_pos.z - spawn_pos.z; + let direction = Vector3::new(dir_x, dir_y, dir_z); + + let fireball_base = Entity::from_uuid( + uuid::Uuid::new_v4(), + world.clone(), + spawn_pos, + &EntityType::FIREBALL, + ); + + let fireball = FireballEntity::new_shot(fireball_base, entity, direction); + fireball + .explosion_power + .store(f32::from(ghast.get_explosion_power()), Ordering::Relaxed); + + world.spawn_entity_non_save(Arc::new(fireball)); + self.charge_time = -40; + } + } else if self.charge_time > 0 { + self.charge_time -= 1; + } + + ghast.set_charging(self.charge_time > 10); } fn controls(&self) -> Controls { @@ -400,48 +383,51 @@ impl RandomFloatAroundGoal { } impl Goal for RandomFloatAroundGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return false; - }; - let wanted = *ghast.wanted_fly_target.lock().await; - wanted.is_none_or(|target| { - let pos = ghast.mob_entity.living_entity.entity.pos.load(); - let dist_sq = pos.squared_distance_to_vec(&target); - dist_sq < 1.0 || dist_sq > 3600.0 - }) - }) - } - - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return; - }; + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(ghast) = self.ghast.upgrade() else { + return false; + }; + let wanted = *ghast + .wanted_fly_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + wanted.is_none_or(|target| { let pos = ghast.mob_entity.living_entity.entity.pos.load(); - let new_target = { - let mut rng = rand::rng(); - let target_x = pos.x + (rng.random::() * 2.0 - 1.0) * 16.0; - let target_y = pos.y + (rng.random::() * 2.0 - 1.0) * 16.0; - let target_z = pos.z + (rng.random::() * 2.0 - 1.0) * 16.0; - Vector3::new(target_x, target_y, target_z) - }; - *ghast.wanted_fly_target.lock().await = Some(new_target); + let dist_sq = pos.squared_distance_to_vec(&target); + dist_sq < 1.0 || dist_sq > 3600.0 }) } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return false; - }; - let wanted = *ghast.wanted_fly_target.lock().await; - wanted.is_some_and(|target| { - let pos = ghast.mob_entity.living_entity.entity.pos.load(); - let dist_sq = pos.squared_distance_to_vec(&target); - (1.0..=3600.0).contains(&dist_sq) - }) + fn start(&mut self, _mob: &dyn Mob) { + let Some(ghast) = self.ghast.upgrade() else { + return; + }; + let pos = ghast.mob_entity.living_entity.entity.pos.load(); + let new_target = { + let mut rng = rand::rng(); + let target_x = pos.x + (rng.random::() * 2.0 - 1.0) * 16.0; + let target_y = pos.y + (rng.random::() * 2.0 - 1.0) * 16.0; + let target_z = pos.z + (rng.random::() * 2.0 - 1.0) * 16.0; + Vector3::new(target_x, target_y, target_z) + }; + *ghast + .wanted_fly_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(new_target); + } + + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(ghast) = self.ghast.upgrade() else { + return false; + }; + let wanted = *ghast + .wanted_fly_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + wanted.is_some_and(|target| { + let pos = ghast.mob_entity.living_entity.entity.pos.load(); + let dist_sq = pos.squared_distance_to_vec(&target); + (1.0..=3600.0).contains(&dist_sq) }) } @@ -449,42 +435,43 @@ impl Goal for RandomFloatAroundGoal { true } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(ghast) = self.ghast.upgrade() else { - return; - }; + fn tick(&mut self, _mob: &dyn Mob) { + let Some(ghast) = self.ghast.upgrade() else { + return; + }; - let wanted = *ghast.wanted_fly_target.lock().await; - let Some(target) = wanted else { - return; - }; + let wanted = *ghast + .wanted_fly_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some(target) = wanted else { + return; + }; - let entity = &ghast.mob_entity.living_entity.entity; - let pos = entity.pos.load(); - self.float_duration -= 1; + let entity = &ghast.mob_entity.living_entity.entity; + let pos = entity.pos.load(); + self.float_duration -= 1; - if self.float_duration <= 0 { - self.float_duration = rand::random_range(2..=6); - let travel = Vector3::new(target.x - pos.x, target.y - pos.y, target.z - pos.z); - let dist = travel.length(); - if dist > 0.001 { - let move_scale = GhastEntity::FLYING_SPEED * 5.0 / 3.0; // 0.1 - let norm = travel.normalize(); - let delta = Vector3::new( - norm.x * move_scale, - norm.y * move_scale, - norm.z * move_scale, - ); - let current_vel = entity.velocity.load(); - entity.velocity.store(Vector3::new( - current_vel.x + delta.x, - current_vel.y + delta.y, - current_vel.z + delta.z, - )); - } + if self.float_duration <= 0 { + self.float_duration = rand::random_range(2..=6); + let travel = Vector3::new(target.x - pos.x, target.y - pos.y, target.z - pos.z); + let dist = travel.length(); + if dist > 0.001 { + let move_scale = GhastEntity::FLYING_SPEED * 5.0 / 3.0; // 0.1 + let norm = travel.normalize(); + let delta = Vector3::new( + norm.x * move_scale, + norm.y * move_scale, + norm.z * move_scale, + ); + let current_vel = entity.velocity.load(); + entity.velocity.store(Vector3::new( + current_vel.x + delta.x, + current_vel.y + delta.y, + current_vel.z + delta.z, + )); } - }) + } } fn controls(&self) -> Controls { diff --git a/crates/pumpkin/src/entity/mob/magma_cube.rs b/crates/pumpkin/src/entity/mob/magma_cube.rs index 7316cf426..5eab67b9a 100644 --- a/crates/pumpkin/src/entity/mob/magma_cube.rs +++ b/crates/pumpkin/src/entity/mob/magma_cube.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::entity::{ - Entity, + Entity, EntityBase, mob::{Mob, MobEntity, slime::SlimeEntity}, }; @@ -21,21 +21,15 @@ impl Mob for MagmaCubeEntity { self.slime.get_mob_entity() } - fn mob_tick<'a>( - &'a self, - caller: &'a Arc, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - self.slime.mob_tick(caller) + fn mob_tick<'a>(&'a self, caller: &'a Arc) { + self.slime.mob_tick(caller); } - fn post_tick(&self) -> crate::entity::EntityBaseFuture<'_, ()> { - self.slime.post_tick() + fn post_tick(&self) { + self.slime.post_tick(); } - fn mob_player_collision<'a>( - &'a self, - player: &'a Arc, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - self.slime.mob_player_collision(player) + fn mob_player_collision(&self, player: &Arc) { + self.slime.mob_player_collision(player); } } diff --git a/crates/pumpkin/src/entity/mob/mod.rs b/crates/pumpkin/src/entity/mob/mod.rs index a960a1414..9bd61eaaa 100644 --- a/crates/pumpkin/src/entity/mob/mod.rs +++ b/crates/pumpkin/src/entity/mob/mod.rs @@ -76,7 +76,7 @@ pub struct MobEntity { pub goals_selector: std::sync::Mutex, pub target_selector: std::sync::Mutex, pub navigator: std::sync::Mutex, - pub target: tokio::sync::Mutex>>, + pub target: std::sync::Mutex>>, pub look_control: std::sync::Mutex, pub move_control: std::sync::Mutex>, pub position_target: AtomicCell, @@ -172,7 +172,7 @@ impl MobEntity { goals_selector: std::sync::Mutex::new(GoalSelector::default()), target_selector: std::sync::Mutex::new(GoalSelector::default()), navigator: std::sync::Mutex::new(Navigator::default()), - target: tokio::sync::Mutex::new(None), + target: std::sync::Mutex::new(None), look_control: std::sync::Mutex::new(LookControl::default()), move_control: std::sync::Mutex::new(Box::new(MoveControl::default())), position_target: AtomicCell::new(BlockPos::ZERO), @@ -237,14 +237,14 @@ impl MobEntity { (self.mob_flags.load(Relaxed) & Self::AI_DISABLED_FLAG) != 0 } - pub async fn clear_ai_goals(&self, mob: &dyn Mob) { + pub fn clear_ai_goals(&self, mob: &dyn Mob) { let running_goals = self .goals_selector .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .clear(); for mut goal in running_goals { - goal.goal.stop(mob).await; + goal.goal.stop(mob); } let running_target_goals = self @@ -253,7 +253,7 @@ impl MobEntity { .unwrap_or_else(std::sync::PoisonError::into_inner) .clear(); for mut goal in running_target_goals { - goal.goal.stop(mob).await; + goal.goal.stop(mob); } } @@ -299,13 +299,19 @@ impl MobEntity { .add_goal(priority, Box::new(goal)); } - pub async fn set_target(&self, target: Option>) { - let mut t = self.target.lock().await; + pub fn set_target(&self, target: Option>) { + let mut t = self + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); *t = target; } - pub async fn get_target(&self) -> Option> { - self.target.lock().await.clone() + pub fn get_target(&self) -> Option> { + self.target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } fn set_mob_flag(&self, flag: u8, value: bool) { @@ -341,7 +347,7 @@ impl MobEntity { && self.breeding_cooldown.load(Relaxed) <= 0 } - pub async fn is_in_attack_range(&self, target: &dyn EntityBase) -> bool { + pub fn is_in_attack_range(&self, target: &dyn EntityBase) -> bool { const DEFAULT_ATTACK_RANGE: f64 = 0.828_427_12; // sqrt(2.04) - 0.6 // TODO: Implement DataComponent lookup for ATTACK_RANGE when components are ready @@ -350,19 +356,11 @@ impl MobEntity { let target_hitbox = target.get_entity().bounding_box.load(); - if !self - .get_attack_box(max_range) - .await - .intersects(&target_hitbox) - { + if !self.get_attack_box(max_range).intersects(&target_hitbox) { return false; } - min_range <= 0.0 - || !self - .get_attack_box(min_range) - .await - .intersects(&target_hitbox) + min_range <= 0.0 || !self.get_attack_box(min_range).intersects(&target_hitbox) } pub fn is_dark_enough_to_spawn(world: &World, pos: &BlockPos, is_thundering: bool) -> bool { @@ -403,7 +401,7 @@ impl MobEntity { true } - pub async fn try_attack(&self, caller: &dyn EntityBase, target: &dyn EntityBase) { + pub fn try_attack(&self, caller: &dyn EntityBase, target: &dyn EntityBase) { if self.living_entity.dead.load(Relaxed) { return; } @@ -412,16 +410,14 @@ impl MobEntity { self.living_entity .get_attribute_value(&Attributes::ATTACK_DAMAGE) as f32; - let damaged = target - .damage_with_context( - target, - attack_damage, - DamageType::MOB_ATTACK, - None, - Some(caller), - Some(caller), - ) - .await; + let damaged = target.damage_with_context( + target, + attack_damage, + DamageType::MOB_ATTACK, + None, + Some(caller), + Some(caller), + ); if damaged { self.living_entity @@ -433,10 +429,10 @@ impl MobEntity { } } - async fn get_attack_box(&self, attack_range: f64) -> BoundingBox { - let vehicle_lock = self.living_entity.entity.vehicle.lock().await; + fn get_attack_box(&self, attack_range: f64) -> BoundingBox { + let vehicle_opt = self.living_entity.entity.get_vehicle(); - let base_box = vehicle_lock.as_ref().map_or_else( + let base_box = vehicle_opt.as_ref().map_or_else( || self.living_entity.entity.bounding_box.load(), |vehicle| { let vehicle_box = vehicle.get_entity().bounding_box.load(); @@ -460,7 +456,7 @@ impl MobEntity { base_box.expand(attack_range, 0.0, attack_range) } - pub async fn tick_sun_burn(&self) { + pub fn tick_sun_burn(&self) { if !self .living_entity .entity @@ -469,13 +465,13 @@ impl MobEntity { { return; } - if !self.is_sun_burn_tick().await { + if !self.is_sun_burn_tick() { return; } self.apply_sun_burn(); } - async fn is_sun_burn_tick(&self) -> bool { + fn is_sun_burn_tick(&self) -> bool { let entity = &self.living_entity.entity; let world_arc = entity.world.load(); @@ -485,7 +481,7 @@ impl MobEntity { // value=false at tick 12542 (dusk), value=true at tick 23460 (dawn). // TODO: read directly from EnvironmentAttributes::MONSTERS_BURN once implemented. - let day_time = world.get_time_of_day().await % 24000; + let day_time = world.get_time_of_day() % 24000; if (NIGHT_START..=NIGHT_END).contains(&day_time) { return false; } @@ -504,7 +500,7 @@ impl MobEntity { } let is_in_non_burnable = entity.touching_water.load(Relaxed) - || world.weather.lock().await.raining + || world.is_raining() || entity.is_in_powder_snow() || entity.was_in_powder_snow.load(Relaxed); @@ -527,24 +523,24 @@ impl MobEntity { entity.set_on_fire_for(8.0); } - pub async fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + pub fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { let entity = &self.living_entity.entity; // If already leashed to player, right-clicking unleashes the mob - let currently_leashed = { - let guard = entity.leashed_to.lock().await; - guard.is_some() - }; + let currently_leashed = entity + .leashed_to + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some(); if currently_leashed { - entity.unleash().await; + entity.unleash(); let lead_item = pumpkin_data::item_stack::ItemStack::new(1, &pumpkin_data::item::Item::LEAD); entity .world .load() - .drop_stack(&entity.block_pos.load(), lead_item) - .await; + .drop_stack(&entity.block_pos.load(), lead_item); return true; } @@ -555,7 +551,7 @@ impl MobEntity { let diff = entity.pos.load() - player.get_entity().pos.load(); let dist_sq = diff.length_squared(); if dist_sq <= Entity::LEASH_SNAP_DISTANCE * Entity::LEASH_SNAP_DISTANCE { - entity.leash_to(player.clone() as Arc).await; + entity.leash_to(player.clone() as Arc); if player.gamemode.load() != pumpkin_util::GameMode::Creative { item_stack.decrement(1); } @@ -612,13 +608,11 @@ pub trait Mob: EntityBase + Send + Sync { None } - fn is_job_site_pending(&self) -> EntityBaseFuture<'_, bool> { - Box::pin(async { false }) + fn is_job_site_pending(&self) -> bool { + false } - fn release_pending_job_site(&self, _position: BlockPos) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn release_pending_job_site(&self, _position: BlockPos) {} fn get_trading_player(&self) -> Option> { None @@ -647,35 +641,19 @@ pub trait Mob: EntityBase + Send + Sync { fn set_saddled(&self, _saddled: bool) {} /// Per-mob tick hook called each tick before AI runs. Override for mob-specific logic. - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async {}) - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) {} - fn post_tick(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn post_tick(&self) {} /// Called before damage is applied. Return `false` to cancel the damage entirely. /// Used by endermen to dodge projectiles via teleportation. - fn pre_damage<'a>( - &'a self, - _damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async { true }) + fn pre_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) -> bool { + true } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async {}) - } + fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) {} - fn on_eating_grass(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn on_eating_grass(&self) {} fn modify_incoming_damage(&self, amount: f32, _damage_type: DamageType) -> f32 { amount @@ -744,7 +722,10 @@ pub trait Mob: EntityBase + Send + Sync { }; let living = &self.get_mob_entity().living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut first = true; for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { @@ -802,7 +783,10 @@ pub trait Mob: EntityBase + Send + Sync { ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { let living = &self.get_mob_entity().living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(stack) = equipment.equipment.get_mut(slot) && !stack.is_empty() && rand::random::() < chance * difficulty.special_multiplier @@ -825,24 +809,23 @@ pub trait Mob: EntityBase + Send + Sync { } /// Set or clear the mob's target. Override to add side effects when targeting changes. - fn set_mob_target(&self, target: Option>) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let target_id = target.as_ref().map(|t| t.get_entity().entity_id); - let mob = self.get_mob_entity(); + fn set_mob_target(&self, target: Option>) { + let mob = self.get_mob_entity(); + let target_id = target.as_ref().map(|t| t.get_entity().entity_id); + *mob.target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = target; + let world = mob.living_entity.entity.world.load_full(); + let entity_id = mob.living_entity.entity.entity_id; + tokio::spawn(async move { let mut event = crate::plugin::api::events::entity::entity_target::EntityTargetEvent::new( - mob.living_entity.entity.entity_id, - target_id, + entity_id, target_id, ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + if let Some(server) = world.server.upgrade() { server.plugin_manager.fire(&server, &mut event).await; } - if event.cancelled { - return; - } - let mut mob_target = mob.target.lock().await; - *mob_target = target; - }) + }); } fn mob_interact<'a>( @@ -850,7 +833,7 @@ pub trait Mob: EntityBase + Send + Sync { player: &'a Arc, item_stack: &'a mut ItemStack, ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { self.get_mob_entity().mob_interact(player, item_stack).await }) + Box::pin(async move { self.get_mob_entity().mob_interact(player, item_stack) }) } fn tame<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { @@ -985,9 +968,7 @@ pub trait Mob: EntityBase + Send + Sync { }) } - fn mob_player_collision<'a>(&'a self, _player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async {}) - } + fn mob_player_collision(&self, _player: &Arc) {} fn get_owner_uuid(&self) -> Option { self.as_tamable() @@ -1008,17 +989,15 @@ pub trait Mob: EntityBase + Send + Sync { self.get_entity().entity_type.experience_reward } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(std::sync::atomic::Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], - None, - ); - } - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(std::sync::atomic::Ordering::Relaxed) < 0; + if is_baby { + entity.send_meta_data( + &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], + None, + ); + } } fn mob_set_variant_name(&self, _name: &str) {} @@ -1059,26 +1038,23 @@ impl EntityBase for T { Mob::get_item_steerable(self) } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.mob_init_data_tracker().await; - let world = self.get_mob_entity().living_entity.entity.world.load(); - crate::entity::mob::equipment::equip_mob_on_spawn(self as &dyn EntityBase, &world) - .await; + fn init_data_tracker(&self) { + self.mob_init_data_tracker(); + let world = self.get_mob_entity().living_entity.entity.world.load(); + crate::entity::mob::equipment::equip_mob_on_spawn(self as &dyn EntityBase, &world); - let entity_name = self.get_entity().entity_type.resource_name; - if let Some(def) = crate::entity::mob::equipment::EQUIPMENT_REGISTRY.get(entity_name) - && def.can_pick_up_loot - { - let difficulty = crate::entity::mob::equipment::RegionalDifficulty::at( - &world, - self.get_entity().pos.load(), - ); - let pickup_chance = 0.55 * difficulty.special_multiplier; - self.get_mob_entity() - .set_can_pick_up_loot(rand::random::() < pickup_chance); - } - }) + let entity_name = self.get_entity().entity_type.resource_name; + if let Some(def) = crate::entity::mob::equipment::EQUIPMENT_REGISTRY.get(entity_name) + && def.can_pick_up_loot + { + let difficulty = crate::entity::mob::equipment::RegionalDifficulty::at( + &world, + self.get_entity().pos.load(), + ); + let pickup_chance = 0.55 * difficulty.special_multiplier; + self.get_mob_entity() + .set_can_pick_up_loot(rand::random::() < pickup_chance); + } } fn set_variant_name(&self, name: &str) { @@ -1086,152 +1062,143 @@ impl EntityBase for T { } #[allow(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let mob_entity = self.get_mob_entity(); - mob_entity.living_entity.entity.tick_leash().await; - mob_entity.tick_sun_burn().await; + fn tick(&self, caller: &Arc, server: &Server) { + let mob_entity = self.get_mob_entity(); + mob_entity.living_entity.entity.tick_leash(); + mob_entity.tick_sun_burn(); - if mob_entity.breeding_cooldown.load(Relaxed) > 0 { - mob_entity.breeding_cooldown.fetch_sub(1, Relaxed); - } + if mob_entity.breeding_cooldown.load(Relaxed) > 0 { + mob_entity.breeding_cooldown.fetch_sub(1, Relaxed); + } - if mob_entity.love_ticks.load(Relaxed) > 0 { - let ticks = mob_entity.love_ticks.fetch_sub(1, Relaxed); - if ticks % 10 == 0 { - let entity = &mob_entity.living_entity.entity; - let pos = entity.pos.load(); - let world = entity.world.load(); - world.spawn_particle( - pos + Vector3::new(0.0, f64::from(entity.height()) + 0.5, 0.0), - Vector3::new(0.5, 0.5, 0.5), - 1.0, - 1, - pumpkin_data::particle::Particle::Heart, - ); - } - } - - self.mob_tick(caller).await; - - let age = mob_entity.living_entity.entity.age.load(Relaxed); - let entity_id = mob_entity.living_entity.entity.entity_id; - - // 1. "Take" selectors out of the mutexes - let mut target_selector = { - let mut guard = mob_entity - .target_selector - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - std::mem::take(&mut *guard) - }; - let mut goals_selector = { - let mut guard = mob_entity - .goals_selector - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - std::mem::take(&mut *guard) - }; - - // 2. Perform AI logic (No locks held, so .await is safe!) - if (age + entity_id) % 2 != 0 && age > 1 { - target_selector.tick_goals(self, false).await; - goals_selector.tick_goals(self, false).await; - } else { - target_selector.tick(self).await; - goals_selector.tick(self).await; - } - - // 3. "Put back" selectors - { - *mob_entity - .target_selector - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = target_selector; - *mob_entity - .goals_selector - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = goals_selector; - }; - - // 4. Repeat for Navigator - let mut navigator = { - let mut guard = mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - std::mem::take(&mut *guard) - }; - - navigator.tick(&mob_entity.living_entity).await; - - { - *mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = navigator; - }; - - // Controllers are synchronous, so we can just use normal blocks - { - let mut look_control = mob_entity - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - look_control.tick(self); - }; - - { - let mut move_control = mob_entity - .move_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - move_control.tick(self); - }; - - mob_entity.living_entity.tick(caller, server).await; - self.post_tick().await; - - // --- Packet logic remains the same --- - let entity = &mob_entity.living_entity.entity; - let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; - let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; - let head_yaw = (entity.head_yaw.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; - - let last_yaw = mob_entity.last_sent_yaw.load(Relaxed); - let last_pitch = mob_entity.last_sent_pitch.load(Relaxed); - let last_head_yaw = mob_entity.last_sent_head_yaw.load(Relaxed); - - let chunk_pos = entity.chunk_pos.load(); - if yaw.abs_diff(last_yaw) >= 1 || pitch.abs_diff(last_pitch) >= 1 { + if mob_entity.love_ticks.load(Relaxed) > 0 { + let ticks = mob_entity.love_ticks.fetch_sub(1, Relaxed); + if ticks % 10 == 0 { + let entity = &mob_entity.living_entity.entity; + let pos = entity.pos.load(); let world = entity.world.load(); - world.broadcast_to_chunk( - chunk_pos, - &CUpdateEntityRot::new( - entity.entity_id.into(), - yaw, - pitch, - entity.on_ground.load(Relaxed), - ), + world.spawn_particle( + pos + Vector3::new(0.0, f64::from(entity.height()) + 0.5, 0.0), + Vector3::new(0.5, 0.5, 0.5), + 1.0, + 1, + pumpkin_data::particle::Particle::Heart, ); - mob_entity.last_sent_yaw.store(yaw, Relaxed); - mob_entity.last_sent_pitch.store(pitch, Relaxed); } + } - if head_yaw.abs_diff(last_head_yaw) >= 1 { - let world = entity.world.load(); + self.mob_tick(caller); - world.broadcast_to_chunk( - chunk_pos, - &CHeadRot::new(entity.entity_id.into(), head_yaw), - ); - mob_entity.last_sent_head_yaw.store(head_yaw, Relaxed); - } - }) + let age = mob_entity.living_entity.entity.age.load(Relaxed); + let entity_id = mob_entity.living_entity.entity.entity_id; + + // 1. "Take" selectors out of the mutexes + let mut target_selector = { + let mut guard = mob_entity + .target_selector + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::take(&mut *guard) + }; + let mut goals_selector = { + let mut guard = mob_entity + .goals_selector + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::take(&mut *guard) + }; + + // 2. Perform AI logic + if (age + entity_id) % 2 != 0 && age > 1 { + target_selector.tick_goals(self, false); + goals_selector.tick_goals(self, false); + } else { + target_selector.tick(self); + goals_selector.tick(self); + } + + // 3. "Put back" selectors + { + *mob_entity + .target_selector + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = target_selector; + *mob_entity + .goals_selector + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = goals_selector; + }; + + // 4. Repeat for Navigator + let mut navigator = { + let mut guard = mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::take(&mut *guard) + }; + + navigator.tick(&mob_entity.living_entity); + + { + *mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = navigator; + }; + + // Controllers are synchronous, so we can just use normal blocks + { + let mut look_control = mob_entity + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + look_control.tick(self); + }; + + { + let mut move_control = mob_entity + .move_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + move_control.tick(self); + }; + + mob_entity.living_entity.tick(caller, server); + self.post_tick(); + + // --- Packet logic remains the same --- + let entity = &mob_entity.living_entity.entity; + let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; + let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; + let head_yaw = (entity.head_yaw.load() * 256.0 / 360.0).rem_euclid(256.0) as u8; + + let last_yaw = mob_entity.last_sent_yaw.load(Relaxed); + let last_pitch = mob_entity.last_sent_pitch.load(Relaxed); + let last_head_yaw = mob_entity.last_sent_head_yaw.load(Relaxed); + + let chunk_pos = entity.chunk_pos.load(); + if yaw.abs_diff(last_yaw) >= 1 || pitch.abs_diff(last_pitch) >= 1 { + let world = entity.world.load(); + world.broadcast_to_chunk( + chunk_pos, + &CUpdateEntityRot::new( + entity.entity_id.into(), + yaw, + pitch, + entity.on_ground.load(Relaxed), + ), + ); + mob_entity.last_sent_yaw.store(yaw, Relaxed); + mob_entity.last_sent_pitch.store(pitch, Relaxed); + } + + if head_yaw.abs_diff(last_head_yaw) >= 1 { + let world = entity.world.load(); + + world.broadcast_to_chunk(chunk_pos, &CHeadRot::new(entity.entity_id.into(), head_yaw)); + mob_entity.last_sent_head_yaw.store(head_yaw, Relaxed); + } } fn is_collidable(&self, _entity: Option>) -> bool { @@ -1242,32 +1209,33 @@ impl EntityBase for T { true } - fn damage_with_context<'a>( - &'a self, - caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + caller: &dyn EntityBase, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - // pre_damage hook: allows mobs to dodge/cancel damage (e.g. enderman projectile dodge) - if !self.pre_damage(damage_type, source).await { - return false; - } - // Mob-specific damage modifier (e.g. shulker armor when closed). - let amount = self.modify_incoming_damage(amount, damage_type); - let damaged = self - .get_mob_entity() - .living_entity - .damage_with_context(caller, amount, damage_type, position, source, cause) - .await; - if damaged { - self.on_damage(damage_type, source).await; - } - damaged - }) + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + // pre_damage hook: allows mobs to dodge/cancel damage (e.g. enderman projectile dodge) + if !self.pre_damage(damage_type, source) { + return false; + } + // Mob-specific damage modifier (e.g. shulker armor when closed). + let amount = self.modify_incoming_damage(amount, damage_type); + let damaged = self.get_mob_entity().living_entity.damage_with_context( + caller, + amount, + damage_type, + position, + source, + cause, + ); + if damaged { + self.on_damage(damage_type, source); + } + damaged } fn interact<'a>( @@ -1278,8 +1246,8 @@ impl EntityBase for T { Box::pin(async move { self.mob_interact(player, item_stack).await }) } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.mob_player_collision(player).await }) + fn on_player_collision(&self, player: &Arc) { + self.mob_player_collision(player); } fn get_entity(&self) -> &Entity { @@ -1433,9 +1401,5 @@ pub trait PathAwareEntity: Mob + Send + Sync { } pub trait RangedAttackMob: Mob + Send + Sync { - fn perform_ranged_attack<'a>( - &'a self, - target: &'a Arc, - power: f32, - ) -> EntityBaseFuture<'a, ()>; + fn perform_ranged_attack(&self, target: &Arc, power: f32); } diff --git a/crates/pumpkin/src/entity/mob/patrol.rs b/crates/pumpkin/src/entity/mob/patrol.rs index 1ad17184b..d3c5a6550 100644 --- a/crates/pumpkin/src/entity/mob/patrol.rs +++ b/crates/pumpkin/src/entity/mob/patrol.rs @@ -6,7 +6,7 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::mob::raider::create_ominous_banner; @@ -95,14 +95,11 @@ pub trait PatrollingMonster: Mob { if self.is_patrol_leader() { let banner = create_ominous_banner(); let living = &self.get_mob_entity().living_entity; - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - let mut equipment = living.entity_equipment.lock().await; - equipment.put(&EquipmentSlot::HEAD, banner.clone()); - drop(equipment); - living.send_equipment_changes(&[(EquipmentSlot::HEAD, banner)]); - }); - }); + if let Ok(mut equipment) = living.entity_equipment.try_lock() { + equipment.put(&EquipmentSlot::HEAD, banner.clone()); + drop(equipment); + living.send_equipment_changes(&[(EquipmentSlot::HEAD, banner)]); + } } if is_patrol_spawn { @@ -158,70 +155,65 @@ impl LongDistancePatrolGoal { } impl Goal for LongDistancePatrolGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(patrol) = mob.as_patrolling_monster() else { - return false; - }; - let world = mob.get_entity().world.load(); - let game_time = world.level_time.lock().await.query_daytime(); - let is_on_cooldown = game_time < self.cooldown_until; + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(patrol) = mob.as_patrolling_monster() else { + return false; + }; + let world = mob.get_entity().world.load(); + let game_time = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .query_daytime(); + let is_on_cooldown = game_time < self.cooldown_until; - let target = mob.get_mob_entity().target.lock().await.clone(); - patrol.is_patrolling() - && target.is_none() - && patrol.has_patrol_target() - && !is_on_cooldown - }) + let target = mob.get_mob_entity().get_target().clone(); + patrol.is_patrolling() && target.is_none() && patrol.has_patrol_target() && !is_on_cooldown } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(patrol) = mob.as_patrolling_monster() else { - return false; - }; - let target = mob.get_mob_entity().target.lock().await.clone(); - patrol.is_patrolling() && target.is_none() && patrol.has_patrol_target() - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(patrol) = mob.as_patrolling_monster() else { + return false; + }; + let target = mob.get_mob_entity().get_target().clone(); + patrol.is_patrolling() && target.is_none() && patrol.has_patrol_target() } fn controls(&self) -> Controls { Controls::MOVE } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(patrol) = mob.as_patrolling_monster() else { - return; - }; - let is_leader = patrol.is_patrol_leader(); - let entity = mob.get_entity(); - let pos = entity.pos.load(); + fn tick(&mut self, mob: &dyn Mob) { + let Some(patrol) = mob.as_patrolling_monster() else { + return; + }; + let is_leader = patrol.is_patrol_leader(); + let entity = mob.get_entity(); + let pos = entity.pos.load(); - let Some(patrol_target) = patrol.get_patrol_target() else { - return; - }; + let Some(patrol_target) = patrol.get_patrol_target() else { + return; + }; - let dist_sq = pos.squared_distance_to_vec(&patrol_target.to_f64()); - if is_leader && dist_sq < 100.0 { - patrol.find_patrol_target(); + let dist_sq = pos.squared_distance_to_vec(&patrol_target.to_f64()); + if is_leader && dist_sq < 100.0 { + patrol.find_patrol_target(); + } else { + let speed = if is_leader { + self.leader_speed_modifier } else { - let speed = if is_leader { - self.leader_speed_modifier - } else { - self.speed_modifier - }; - let mut nav = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - nav.set_progress(NavigatorGoal { - current_progress: pos, - destination: patrol_target.to_f64(), - speed, - }); - } - }) + self.speed_modifier + }; + let mut nav = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + nav.set_progress(NavigatorGoal { + current_progress: pos, + destination: patrol_target.to_f64(), + speed, + }); + } } } diff --git a/crates/pumpkin/src/entity/mob/piglin.rs b/crates/pumpkin/src/entity/mob/piglin.rs index 999d480ba..08e4e9b3f 100644 --- a/crates/pumpkin/src/entity/mob/piglin.rs +++ b/crates/pumpkin/src/entity/mob/piglin.rs @@ -128,8 +128,8 @@ impl PiglinEntity { 10, true, false, - Some(|target: Arc, _world: Arc| { - Box::pin(async move { !PiglinAi::is_wearing_safe_armor(&target).await }) + Some(|target: &LivingEntity, _world: &World| { + !PiglinAi::is_wearing_safe_armor(target) }), )), ); @@ -158,9 +158,8 @@ impl PiglinEntity { 10, true, false, - Some(move |_target: Arc, _world: Arc| { - let piglin = piglin_clone.clone(); - Box::pin(async move { piglin.is_adult() && piglin.can_hunt() }) + Some(move |_target: &LivingEntity, _world: &World| { + piglin_clone.is_adult() && piglin_clone.can_hunt() }), )), ); @@ -280,7 +279,12 @@ impl PiglinEntity { .store(PiglinAi::ADMIRE_DURATION, Ordering::Relaxed); *self.admiring_item.lock().await = Some(item.clone()); - let mut equip = self.mob_entity.living_entity.entity_equipment.lock().await; + let mut equip = self + .mob_entity + .living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equip.put(&EquipmentSlot::OFF_HAND, item); let entity = &self.mob_entity.living_entity.entity; @@ -299,7 +303,12 @@ impl PiglinEntity { }; let _ = { - let mut equip = self.mob_entity.living_entity.entity_equipment.lock().await; + let mut equip = self + .mob_entity + .living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equip.put(&EquipmentSlot::OFF_HAND, ItemStack::EMPTY.clone()) }; @@ -324,18 +333,18 @@ impl PiglinEntity { } if !event.cancelled { - PiglinAi::throw_items(self, event.outcome, None).await; + PiglinAi::throw_items(self, event.outcome, None); } } else if !is_barter { let remainder = self.add_to_inventory(item).await; if let Some(rem) = remainder { - PiglinAi::throw_items(self, vec![rem], None).await; + PiglinAi::throw_items(self, vec![rem], None); } } } else { let remainder = self.add_to_inventory(item).await; if let Some(rem) = remainder { - PiglinAi::throw_items(self, vec![rem], None).await; + PiglinAi::throw_items(self, vec![rem], None); } } } @@ -349,10 +358,15 @@ impl PiglinEntity { }; if let Some(item) = item { let _ = { - let mut equip = self.mob_entity.living_entity.entity_equipment.lock().await; + let mut equip = self + .mob_entity + .living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equip.put(&EquipmentSlot::OFF_HAND, ItemStack::EMPTY.clone()) }; - PiglinAi::throw_items(self, vec![item], None).await; + PiglinAi::throw_items(self, vec![item], None); } } } @@ -380,11 +394,11 @@ impl PiglinEntity { } } - pub async fn drop_inventory(&self) { - let items = { - let mut inv = self.inventory.lock().await; - std::mem::take(&mut *inv) - }; + pub fn drop_inventory(&self) { + let items = self + .inventory + .try_lock() + .map_or_else(|_| Vec::new(), |mut inv| std::mem::take(&mut *inv)); let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -394,7 +408,7 @@ impl PiglinEntity { Entity::new(world.clone(), pos, &EntityType::ITEM), item, ); - world.spawn_entity(Arc::new(item_entity)).await; + world.spawn_entity(Arc::new(item_entity)); } } } @@ -406,7 +420,7 @@ impl PiglinEntity { state.id != Block::NETHER_WART_BLOCK.default_state.id } - async fn convert_to_zombified(&self) { + fn convert_to_zombified(&self) { let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -419,7 +433,7 @@ impl PiglinEntity { ); } - self.drop_inventory().await; + self.drop_inventory(); let zombified = crate::entity::r#type::from_type( &EntityType::ZOMBIFIED_PIGLIN, @@ -442,17 +456,25 @@ impl PiglinEntity { } { - let src_equip = self.mob_entity.living_entity.entity_equipment.lock().await; + let src_equip = self + .mob_entity + .living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(living) = zombified.get_living_entity() { - let mut dst_equip = living.entity_equipment.lock().await; + let mut dst_equip = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (slot, item) in &src_equip.equipment { dst_equip.put(slot, item.clone()); } } } - world.spawn_entity(zombified).await; - entity.remove().await; + world.spawn_entity(zombified); + entity.remove(); } } @@ -469,7 +491,10 @@ impl Mob for PiglinEntity { Box::pin(async move { if !self.is_baby.load(Ordering::Relaxed) { let living = &self.mob_entity.living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); // Spawn weapon: 50% crossbow, 5% golden spear (10% of remaining 50%), 45% golden sword let weapon = if rand::random::() < 0.5 { @@ -507,32 +532,30 @@ impl Mob for PiglinEntity { }) } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let mut meta = Vec::new(); - if self.is_immune_to_zombification() { - meta.push(Metadata::new( - tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, - true, - )); - } - if self.is_baby() { - meta.push(Metadata::new(tracked_data::piglin::DATA_BABY_ID, true)); - } - if self.is_charging_crossbow() { - meta.push(Metadata::new( - tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, - true, - )); - } - if self.is_dancing() { - meta.push(Metadata::new(tracked_data::piglin::DATA_IS_DANCING, true)); - } - if !meta.is_empty() { - entity.send_meta_data(&meta, None); - } - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let mut meta = Vec::new(); + if self.is_immune_to_zombification() { + meta.push(Metadata::new( + tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, + true, + )); + } + if self.is_baby() { + meta.push(Metadata::new(tracked_data::piglin::DATA_BABY_ID, true)); + } + if self.is_charging_crossbow() { + meta.push(Metadata::new( + tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, + true, + )); + } + if self.is_dancing() { + meta.push(Metadata::new(tracked_data::piglin::DATA_IS_DANCING, true)); + } + if !meta.is_empty() { + entity.send_meta_data(&meta, None); + } } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { @@ -624,64 +647,73 @@ impl Mob for PiglinEntity { self.start_admiring(given).await; return true; } - self.mob_entity.mob_interact(player, item_stack).await + self.mob_entity.mob_interact(player, item_stack) }) } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + fn mob_tick<'a>(&'a self, caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() { + return; + } - let world = entity.world.load(); - if self.is_converting(&world) { - let time = self.time_in_overworld.fetch_add(1, Ordering::Relaxed) + 1; - if time > Self::CONVERSION_TIME { - self.convert_to_zombified().await; - } - } else { - self.time_in_overworld.store(0, Ordering::Relaxed); + let world = entity.world.load(); + if self.is_converting(&world) { + let time = self.time_in_overworld.fetch_add(1, Ordering::Relaxed) + 1; + if time > Self::CONVERSION_TIME { + self.convert_to_zombified(); } + } else { + self.time_in_overworld.store(0, Ordering::Relaxed); + } - if self.admiring_disabled_timer.load(Ordering::Relaxed) > 0 { - self.admiring_disabled_timer.fetch_sub(1, Ordering::Relaxed); - } - if self.eat_cooldown_timer.load(Ordering::Relaxed) > 0 { - self.eat_cooldown_timer.fetch_sub(1, Ordering::Relaxed); - } - if self.hunt_cooldown_timer.load(Ordering::Relaxed) > 0 { - self.hunt_cooldown_timer.fetch_sub(1, Ordering::Relaxed); - } - if self.celebration_timer.load(Ordering::Relaxed) > 0 { - let remaining = self.celebration_timer.fetch_sub(1, Ordering::Relaxed) - 1; - if remaining <= 0 { - self.set_dancing(false); - } + if self.admiring_disabled_timer.load(Ordering::Relaxed) > 0 { + self.admiring_disabled_timer.fetch_sub(1, Ordering::Relaxed); + } + if self.eat_cooldown_timer.load(Ordering::Relaxed) > 0 { + self.eat_cooldown_timer.fetch_sub(1, Ordering::Relaxed); + } + if self.hunt_cooldown_timer.load(Ordering::Relaxed) > 0 { + self.hunt_cooldown_timer.fetch_sub(1, Ordering::Relaxed); + } + if self.celebration_timer.load(Ordering::Relaxed) > 0 { + let remaining = self.celebration_timer.fetch_sub(1, Ordering::Relaxed) - 1; + if remaining <= 0 { + self.set_dancing(false); } + } - if self.admire_timer.load(Ordering::Relaxed) > 0 { - let remaining = self.admire_timer.fetch_sub(1, Ordering::Relaxed) - 1; - if remaining <= 0 { - self.stop_holding_off_hand_item(true).await; - } + if self.admire_timer.load(Ordering::Relaxed) > 0 { + let remaining = self.admire_timer.fetch_sub(1, Ordering::Relaxed) - 1; + if remaining <= 0 { + let caller_clone = caller.clone(); + tokio::spawn(async move { + if let Some(piglin) = caller_clone.cast_any().downcast_ref::() { + piglin.stop_holding_off_hand_item(true).await; + } + }); } - }) + } } - fn on_damage<'a>( - &'a self, + fn on_damage( + &self, _damage_type: pumpkin_data::damage::DamageType, - source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.mob_entity.living_entity.dead.load(Ordering::Relaxed) { - self.drop_inventory().await; - } else { - self.was_hurt_by(source).await; + source: Option<&dyn EntityBase>, + ) { + if self.mob_entity.living_entity.dead.load(Ordering::Relaxed) { + self.drop_inventory(); + } else { + self.set_dancing(false); + self.celebration_timer.store(0, Ordering::Relaxed); + + if let Some(attacker_entity) = source + && attacker_entity.get_entity().entity_type.id == EntityType::PLAYER.id + { + self.admiring_disabled_timer + .store(PiglinAi::ADMIRING_DISABLED_DURATION, Ordering::Relaxed); } - }) + } } fn as_crossbow_attack_mob(&self) -> Option<&dyn CrossbowAttackMob> { diff --git a/crates/pumpkin/src/entity/mob/piglin_ai.rs b/crates/pumpkin/src/entity/mob/piglin_ai.rs index bd5590ff3..d58f9aed9 100644 --- a/crates/pumpkin/src/entity/mob/piglin_ai.rs +++ b/crates/pumpkin/src/entity/mob/piglin_ai.rs @@ -59,8 +59,10 @@ impl PiglinAi { rand::random::() < Self::PROBABILITY_OF_CELEBRATION_DANCE } - pub async fn is_wearing_safe_armor(target: &LivingEntity) -> bool { - let equipment = target.entity_equipment.lock().await; + pub fn is_wearing_safe_armor(target: &LivingEntity) -> bool { + let Ok(equipment) = target.entity_equipment.try_lock() else { + return false; + }; [ EquipmentSlot::HEAD, EquipmentSlot::CHEST, @@ -154,7 +156,7 @@ impl PiglinAi { } } - pub async fn throw_items( + pub fn throw_items( piglin: &PiglinEntity, items: Vec, target_pos: Option>, @@ -179,7 +181,7 @@ impl PiglinAi { vel.z * 0.3, )); } - world.spawn_entity(Arc::new(item_entity)).await; + world.spawn_entity(Arc::new(item_entity)); } } } diff --git a/crates/pumpkin/src/entity/mob/piglin_brute.rs b/crates/pumpkin/src/entity/mob/piglin_brute.rs index 8dec01bc5..990bde14e 100644 --- a/crates/pumpkin/src/entity/mob/piglin_brute.rs +++ b/crates/pumpkin/src/entity/mob/piglin_brute.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, @@ -122,7 +122,7 @@ impl PiglinBruteEntity { state.id != Block::NETHER_WART_BLOCK.default_state.id } - async fn convert_to_zombified(&self) { + fn convert_to_zombified(&self) { let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -156,17 +156,25 @@ impl PiglinBruteEntity { } { - let src_equip = self.mob_entity.living_entity.entity_equipment.lock().await; + let src_equip = self + .mob_entity + .living_entity + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(living) = zombified.get_living_entity() { - let mut dst_equip = living.entity_equipment.lock().await; + let mut dst_equip = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (slot, item) in &src_equip.equipment { dst_equip.put(slot, item.clone()); } } } - world.spawn_entity(zombified).await; - entity.remove().await; + world.spawn_entity(zombified); + entity.remove(); } } @@ -175,19 +183,17 @@ impl Mob for PiglinBruteEntity { &self.mob_entity } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - if self.is_immune_to_zombification() { - entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, - true, - )], - None, - ); - } - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + if self.is_immune_to_zombification() { + entity.send_meta_data( + &[Metadata::new( + tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, + true, + )], + None, + ); + } } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { @@ -214,23 +220,21 @@ impl Mob for PiglinBruteEntity { }) } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + if !entity.is_alive() { + return; + } - let world = entity.world.load(); - if self.is_converting(&world) { - let time = self.time_in_overworld.fetch_add(1, Ordering::Relaxed) + 1; - if time > Self::CONVERSION_TIME { - self.convert_to_zombified().await; - } - } else { - self.time_in_overworld.store(0, Ordering::Relaxed); + let world = entity.world.load(); + if self.is_converting(&world) { + let time = self.time_in_overworld.fetch_add(1, Ordering::Relaxed) + 1; + if time > Self::CONVERSION_TIME { + self.convert_to_zombified(); } - }) + } else { + self.time_in_overworld.store(0, Ordering::Relaxed); + } } fn get_base_experience_reward(&self) -> u32 { diff --git a/crates/pumpkin/src/entity/mob/pillager.rs b/crates/pumpkin/src/entity/mob/pillager.rs index 9070eca43..e5d2a11ed 100644 --- a/crates/pumpkin/src/entity/mob/pillager.rs +++ b/crates/pumpkin/src/entity/mob/pillager.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::java::client::play::Metadata; use tokio::sync::Mutex; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_crossbow_attack::RangedCrossbowAttackGoal, @@ -125,11 +125,11 @@ impl PillagerEntity { } } - pub async fn drop_inventory(&self) { - let items = { - let mut inv = self.inventory.lock().await; - std::mem::take(&mut *inv) - }; + pub fn drop_inventory(&self) { + let items = self + .inventory + .try_lock() + .map_or_else(|_| Vec::new(), |mut inv| std::mem::take(&mut *inv)); let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); @@ -139,7 +139,7 @@ impl PillagerEntity { Entity::new(world.clone(), pos, &EntityType::ITEM), item, ); - world.spawn_entity(Arc::new(item_entity)).await; + world.spawn_entity(Arc::new(item_entity)); } } } @@ -162,19 +162,17 @@ impl Mob for PillagerEntity { Some(self) } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - if self.is_charging_crossbow() { - entity.send_meta_data( - &[Metadata::new( - tracked_data::pillager::IS_CHARGING_CROSSBOW, - true, - )], - None, - ); - } - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + if self.is_charging_crossbow() { + entity.send_meta_data( + &[Metadata::new( + tracked_data::pillager::IS_CHARGING_CROSSBOW, + true, + )], + None, + ); + } } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { @@ -217,16 +215,14 @@ impl Mob for PillagerEntity { }) } - fn on_damage<'a>( - &'a self, + fn on_damage( + &self, _damage_type: pumpkin_data::damage::DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.mob_entity.living_entity.dead.load(Ordering::Relaxed) { - self.drop_inventory().await; - } - }) + _source: Option<&dyn EntityBase>, + ) { + if self.mob_entity.living_entity.dead.load(Ordering::Relaxed) { + self.drop_inventory(); + } } } diff --git a/crates/pumpkin/src/entity/mob/raider.rs b/crates/pumpkin/src/entity/mob/raider.rs index 910ef4ae4..09279e4f4 100644 --- a/crates/pumpkin/src/entity/mob/raider.rs +++ b/crates/pumpkin/src/entity/mob/raider.rs @@ -12,7 +12,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::mob::patrol::{PatrolData, PatrollingMonster}; @@ -154,74 +154,66 @@ impl HoldGroundAttackGoal { } impl Goal for HoldGroundAttackGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; - if raider.has_active_raid() || !raider.is_patrolling() { - return false; - } - let target = mob.get_mob_entity().target.lock().await.clone(); - target.is_some() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; + if raider.has_active_raid() || !raider.is_patrolling() { + return false; + } + let target = mob.get_mob_entity().get_target().clone(); + target.is_some() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - target.is_some() - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target().clone(); + target.is_some() } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - mob.get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); + fn start(&mut self, mob: &dyn Mob) { + mob.get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .stop(); - let target = mob.get_mob_entity().target.lock().await.clone(); - if let Some(target) = target { - let entity = mob.get_entity(); - let world = entity.world.load(); - let bb = entity.bounding_box.load().expand(8.0, 8.0, 8.0); - let nearby = world.get_entities_at_box(&bb); + let target = mob.get_mob_entity().get_target().clone(); + if let Some(target) = target { + let entity = mob.get_entity(); + let world = entity.world.load(); + let bb = entity.bounding_box.load().expand(8.0, 8.0, 8.0); + let nearby = world.get_entities_at_box(&bb); - for cand in nearby { - if cand.get_entity().entity_id != entity.entity_id - && let Some(cand_mob) = cand.get_mob() - && cand_mob.as_raider().is_some() - { - *cand_mob.get_mob_entity().target.lock().await = Some(target.clone()); - } + for cand in nearby { + if cand.get_entity().entity_id != entity.entity_id + && let Some(cand_mob) = cand.get_mob() + && cand_mob.as_raider().is_some() + { + cand_mob.get_mob_entity().set_target(Some(target.clone())); } } - }) + } } fn controls(&self) -> Controls { Controls::MOVE | Controls::LOOK } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let target = mob.get_mob_entity().target.lock().await.clone(); - if let Some(target) = target { - let mob_pos = mob.get_entity().pos.load(); - let target_pos = target.get_entity().pos.load(); - let dist_sq = mob_pos.squared_distance_to_vec(&target_pos); + fn tick(&mut self, mob: &dyn Mob) { + let target = mob.get_mob_entity().get_target().clone(); + if let Some(target) = target { + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(&target_pos); - if dist_sq > self.hostile_radius_sqr { - mob.get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at_entity_with_range(&target, 30.0, 30.0); - } + if dist_sq > self.hostile_radius_sqr { + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at_entity_with_range(&target, 30.0, 30.0); } - }) + } } } @@ -229,74 +221,71 @@ impl Goal for HoldGroundAttackGoal { pub struct ObtainRaidLeaderBannerGoal; impl Goal for ObtainRaidLeaderBannerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; - if !raider.can_be_leader() || raider.is_patrol_leader() { - return false; - } - // Check if dropped banner nearby - let entity = mob.get_entity(); - let world = entity.world.load(); - let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); - let nearby = world.get_entities_at_box(&bb); + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; + if !raider.can_be_leader() || raider.is_patrol_leader() { + return false; + } + // Check if dropped banner nearby + let entity = mob.get_entity(); + let world = entity.world.load(); + let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); + let nearby = world.get_entities_at_box(&bb); - nearby - .iter() - .any(|e| *e.get_entity().entity_type == EntityType::ITEM) - }) + nearby + .iter() + .any(|e| *e.get_entity().entity_type == EntityType::ITEM) } fn controls(&self) -> Controls { Controls::MOVE } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return; - }; - if !raider.can_be_leader() || raider.is_patrol_leader() { - return; - } + fn tick(&mut self, mob: &dyn Mob) { + let Some(raider) = mob.as_raider() else { + return; + }; + if !raider.can_be_leader() || raider.is_patrol_leader() { + return; + } - let entity = mob.get_entity(); - let pos = entity.pos.load(); - let world = entity.world.load(); - let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); - let nearby = world.get_entities_at_box(&bb); + let entity = mob.get_entity(); + let pos = entity.pos.load(); + let world = entity.world.load(); + let bb = entity.bounding_box.load().expand(16.0, 4.0, 16.0); + let nearby = world.get_entities_at_box(&bb); - for cand in nearby { - if *cand.get_entity().entity_type == EntityType::ITEM { - let cand_pos = cand.get_entity().pos.load(); - let dist = pos.squared_distance_to_vec(&cand_pos); - if dist < 2.0 { - raider.set_patrol_leader(true); - let banner = create_ominous_banner(); - let living = &mob.get_mob_entity().living_entity; - let mut equipment = living.entity_equipment.lock().await; + for cand in nearby { + if *cand.get_entity().entity_type == EntityType::ITEM { + let cand_pos = cand.get_entity().pos.load(); + let dist = pos.squared_distance_to_vec(&cand_pos); + if dist < 2.0 { + raider.set_patrol_leader(true); + let banner = create_ominous_banner(); + let living = &mob.get_mob_entity().living_entity; + if let Ok(mut equipment) = living.entity_equipment.try_lock() { equipment.put(&EquipmentSlot::HEAD, banner.clone()); drop(equipment); living.send_equipment_changes(&[(EquipmentSlot::HEAD, banner)]); - cand.get_entity().remove().await; - break; } - let mut nav = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - nav.set_progress(NavigatorGoal { - current_progress: pos, - destination: cand_pos, - speed: 1.15, - }); + cand.get_entity().remove(); break; } + let mut nav = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + nav.set_progress(NavigatorGoal { + current_progress: pos, + destination: cand_pos, + speed: 1.15, + }); + break; } - }) + } } } @@ -304,60 +293,50 @@ impl Goal for ObtainRaidLeaderBannerGoal { pub struct RaiderCelebrationGoal; impl Goal for RaiderCelebrationGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; - let target = mob.get_mob_entity().target.lock().await.clone(); - target.is_none() && raider.is_celebrating() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; + let target = mob.get_mob_entity().get_target().clone(); + target.is_none() && raider.is_celebrating() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; - let target = mob.get_mob_entity().target.lock().await.clone(); - target.is_none() && raider.is_celebrating() - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; + let target = mob.get_mob_entity().get_target().clone(); + target.is_none() && raider.is_celebrating() } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(raider) = mob.as_raider() { - raider.set_celebrating(true); - } - }) + fn start(&mut self, mob: &dyn Mob) { + if let Some(raider) = mob.as_raider() { + raider.set_celebrating(true); + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(raider) = mob.as_raider() { - raider.set_celebrating(false); - } - }) + fn stop(&mut self, mob: &dyn Mob) { + if let Some(raider) = mob.as_raider() { + raider.set_celebrating(false); + } } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return; - }; - let entity = mob.get_entity(); - let pos = entity.pos.load(); - let world = entity.world.load(); + fn tick(&mut self, mob: &dyn Mob) { + let Some(raider) = mob.as_raider() else { + return; + }; + let entity = mob.get_entity(); + let pos = entity.pos.load(); + let world = entity.world.load(); - let r: f32 = rand::random(); - if r < 0.02 && !entity.silent.load(Ordering::Relaxed) { - world.play_sound( - raider.get_celebrate_sound(), - pumpkin_data::sound::SoundCategory::Hostile, - &pos, - ); - } - }) + let r: f32 = rand::random(); + if r < 0.02 && !entity.silent.load(Ordering::Relaxed) { + world.play_sound( + raider.get_celebrate_sound(), + pumpkin_data::sound::SoundCategory::Hostile, + &pos, + ); + } } } @@ -374,44 +353,40 @@ impl RaiderMoveThroughVillageGoal { } impl Goal for RaiderMoveThroughVillageGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(raider) = mob.as_raider() else { - return false; - }; - if !raider.has_active_raid() { - return false; - } - let target = mob.get_mob_entity().target.lock().await.clone(); - target.is_none() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(raider) = mob.as_raider() else { + return false; + }; + if !raider.has_active_raid() { + return false; + } + let target = mob.get_mob_entity().get_target().clone(); + target.is_none() } fn controls(&self) -> Controls { Controls::MOVE } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let entity = mob.get_entity(); - let pos = entity.pos.load(); - let mut nav = mob - .get_mob_entity() - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + fn tick(&mut self, mob: &dyn Mob) { + let entity = mob.get_entity(); + let pos = entity.pos.load(); + let mut nav = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - if nav.is_idle() { - let dx: f64 = (rand::random::() - 0.5) * 32.0; - let dz: f64 = (rand::random::() - 0.5) * 32.0; - let dest = Vector3::new(pos.x + dx, pos.y, pos.z + dz); - nav.set_progress(NavigatorGoal { - current_progress: pos, - destination: dest, - speed: self.speed_modifier, - }); - } - }) + if nav.is_idle() { + let dx: f64 = (rand::random::() - 0.5) * 32.0; + let dz: f64 = (rand::random::() - 0.5) * 32.0; + let dest = Vector3::new(pos.x + dx, pos.y, pos.z + dz); + nav.set_progress(NavigatorGoal { + current_progress: pos, + destination: dest, + speed: self.speed_modifier, + }); + } } } diff --git a/crates/pumpkin/src/entity/mob/shulker.rs b/crates/pumpkin/src/entity/mob/shulker.rs index bb7e23593..46861e1c4 100644 --- a/crates/pumpkin/src/entity/mob/shulker.rs +++ b/crates/pumpkin/src/entity/mob/shulker.rs @@ -18,10 +18,10 @@ use crate::entity::ai::goal::active_target::ActiveTargetGoal; use crate::entity::ai::goal::look_around::RandomLookAroundGoal; use crate::entity::ai::goal::look_at_entity::LookAtEntityGoal; use crate::entity::ai::goal::revenge::RevengeGoal; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::mob::{Mob, MobEntity}; use crate::entity::projectile::shulker_bullet::ShulkerBulletEntity; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture}; +use crate::entity::{Entity, EntityBase, NbtFuture}; const DEFAULT_ATTACH_FACE: BlockDirection = BlockDirection::Down; const NO_COLOR: u8 = 16; @@ -224,18 +224,18 @@ impl ShulkerEntity { } /// Try to find a new attachment point, cascading to a random teleport. - async fn find_new_attachment(&self) { + fn find_new_attachment(&self) { let pos = self.mob_entity.living_entity.entity.block_pos.load(); if let Some(dir) = self.find_attachable_face(&pos) { self.set_attach_face(dir); } else { - self.teleport_somewhere().await; + self.teleport_somewhere(); } } /// Attempt to teleport to a random nearby location where the shulker can attach. /// Returns `true` on success. - pub async fn teleport_somewhere(&self) -> bool { + pub fn teleport_somewhere(&self) -> bool { let entity = &self.mob_entity.living_entity.entity; let base_pos = entity.block_pos.load(); let world = entity.world.load(); @@ -299,7 +299,7 @@ impl ShulkerEntity { // Close the shulker and drop the current target after teleport. self.set_raw_peek(0); - self.mob_entity.target.lock().await.take(); + self.mob_entity.set_target(None); return true; } @@ -307,14 +307,14 @@ impl ShulkerEntity { false } - pub async fn on_shulker_damage(&self, _damage_type: DamageType) { + pub fn on_shulker_damage(&self, _damage_type: DamageType) { let living = &self.mob_entity.living_entity; let health = living.health.load(); let max = living.get_max_health(); // Teleport at half-health (random 1-in-4 chance) if health < max * 0.5 && rand::rng().random_range(0..4) == 0 { - self.teleport_somewhere().await; + self.teleport_somewhere(); } // pre_damage for arrow blocking below. @@ -352,50 +352,36 @@ impl Mob for ShulkerEntity { 0.0 } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; - if !entity.is_alive() { - return; - } + if !entity.is_alive() { + return; + } - entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); + entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); - // Advance peek interpolation - self.update_peek_amount(); + // Advance peek interpolation + self.update_peek_amount(); - // Ensure the current attachment face still has a solid block behind it. - let pos = entity.block_pos.load(); - let face = self.get_attach_face(); - if !self.can_stay_at(&pos, face) { - self.find_new_attachment().await; - } - }) + // Ensure the current attachment face still has a solid block behind it. + let pos = entity.block_pos.load(); + let face = self.get_attach_face(); + if !self.can_stay_at(&pos, face) { + self.find_new_attachment(); + } } - fn on_damage<'a>( - &'a self, - damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.on_shulker_damage(damage_type).await; - }) + fn on_damage(&self, damage_type: DamageType, _source: Option<&dyn EntityBase>) { + self.on_shulker_damage(damage_type); } /// When closed, block arrows entirely. - fn pre_damage<'a>( - &'a self, - damage_type: DamageType, - _source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_closed() && damage_type == DamageType::ARROW { - return false; - } - true - }) + fn pre_damage(&self, damage_type: DamageType, _source: Option<&dyn EntityBase>) -> bool { + if self.is_closed() && damage_type == DamageType::ARROW { + return false; + } + true } /// Apply armor modifier (20 armor) reduction when closed. @@ -429,98 +415,84 @@ impl Goal for ShulkerAttackGoal { Controls::MOVE | Controls::LOOK } - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await; - target - .as_ref() - .is_some_and(|t| t.get_living_entity().is_some_and(|l| l.entity.is_alive())) - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target(); + target + .as_ref() + .is_some_and(|t| t.get_living_entity().is_some_and(|l| l.entity.is_alive())) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let target = mob.get_mob_entity().target.lock().await; - target - .as_ref() - .is_some_and(|t| t.get_living_entity().is_some_and(|l| l.entity.is_alive())) - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target(); + target + .as_ref() + .is_some_and(|t| t.get_living_entity().is_some_and(|l| l.entity.is_alive())) } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.attack_cooldown.store(20, Ordering::Relaxed); - self.shulker.set_raw_peek(100); - }) + fn start(&mut self, _mob: &dyn Mob) { + self.attack_cooldown.store(20, Ordering::Relaxed); + self.shulker.set_raw_peek(100); } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.shulker.set_raw_peek(0); - }) + fn stop(&mut self, _mob: &dyn Mob) { + self.shulker.set_raw_peek(0); } fn should_run_every_tick(&self) -> bool { true } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - let target_arc = { - let guard = mob_entity.target.lock().await; - guard.clone() - }; + fn tick(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + let target_arc = mob_entity.get_target(); - let Some(target) = target_arc else { - return; - }; + let Some(target) = target_arc else { + return; + }; - if !target.get_entity().is_alive() { - return; - } + if !target.get_entity().is_alive() { + return; + } - let entity = &mob_entity.living_entity.entity; - let shulker_pos = entity.pos.load(); + let entity = &mob_entity.living_entity.entity; + let shulker_pos = entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = shulker_pos.squared_distance_to_vec(&target_pos); + + // De-target if too far (>20 blocks) + if dist_sq > 400.0 { + mob_entity.set_target(None); + return; + } + + let cooldown = self.attack_cooldown.fetch_sub(1, Ordering::Relaxed) - 1; + if cooldown <= 0 { + // Reset cooldown + let new_cd = 20 + mob.get_random().random_range(0..5) * 10; + self.attack_cooldown.store(new_cd, Ordering::Relaxed); + + // Spawn bullet + let world = entity.world.load(); let target_pos = target.get_entity().pos.load(); - let dist_sq = shulker_pos.squared_distance_to_vec(&target_pos); + let bullet = ShulkerBulletEntity::new( + entity, + target.get_entity().entity_id, + target_pos, + self.shulker.get_attach_face().axis_of(), + ); + world.spawn_entity_non_save(Arc::new(bullet)); - // De-target if too far (>20 blocks) - if dist_sq > 400.0 { - mob_entity.target.lock().await.take(); - return; - } - - let cooldown = self.attack_cooldown.fetch_sub(1, Ordering::Relaxed) - 1; - if cooldown <= 0 { - // Reset cooldown - let new_cd = 20 + mob.get_random().random_range(0..5) * 10; - self.attack_cooldown.store(new_cd, Ordering::Relaxed); - - // Spawn bullet - let world = entity.world.load(); - let target_pos = target.get_entity().pos.load(); - let bullet = ShulkerBulletEntity::new( - entity, - target.get_entity().entity_id, - target_pos, - self.shulker.get_attach_face().axis_of(), - ); - let bullet_arc = Arc::new(bullet); - world.spawn_entity(bullet_arc).await; - - // Shoot sound (random pitch) - let pitch = 1.0 - + (mob.get_random().random::() - mob.get_random().random::()) * 0.2; - world.play_sound_fine( - Sound::EntityShulkerShoot, - SoundCategory::Hostile, - &shulker_pos, - 2.0, - pitch, - ); - } - }) + // Shoot sound (random pitch) + let pitch = + 1.0 + (mob.get_random().random::() - mob.get_random().random::()) * 0.2; + world.play_sound_fine( + Sound::EntityShulkerShoot, + SoundCategory::Hostile, + &shulker_pos, + 2.0, + pitch, + ); + } } } @@ -539,49 +511,39 @@ impl ShulkerPeekGoal { } impl Goal for ShulkerPeekGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let has_target = mob.get_mob_entity().target.lock().await.is_some(); - if has_target { - return false; - } - if mob.get_random().random_range(0..40) != 0 { - return false; - } - let pos = mob.get_mob_entity().living_entity.entity.block_pos.load(); - let face = self.shulker.get_attach_face(); - self.shulker.can_stay_at(&pos, face) - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let has_target = mob.get_mob_entity().get_target().is_some(); + if has_target { + return false; + } + if mob.get_random().random_range(0..40) != 0 { + return false; + } + let pos = mob.get_mob_entity().living_entity.entity.block_pos.load(); + let face = self.shulker.get_attach_face(); + self.shulker.can_stay_at(&pos, face) } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let has_target = mob.get_mob_entity().target.lock().await.is_some(); - !has_target && self.peek_time.load(Ordering::Relaxed) > 0 - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let has_target = mob.get_mob_entity().get_target().is_some(); + !has_target && self.peek_time.load(Ordering::Relaxed) > 0 } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let duration = 20 * (1 + mob.get_random().random_range(0..3)); - self.peek_time.store(duration, Ordering::Relaxed); - self.shulker.set_raw_peek(30); - }) + fn start(&mut self, mob: &dyn Mob) { + let duration = 20 * (1 + mob.get_random().random_range(0..3)); + self.peek_time.store(duration, Ordering::Relaxed); + self.shulker.set_raw_peek(30); } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let has_target = self.shulker.mob_entity.target.lock().await.is_some(); - if !has_target { - self.shulker.set_raw_peek(0); - } - }) + fn stop(&mut self, _mob: &dyn Mob) { + let has_target = self.shulker.mob_entity.get_target().is_some(); + if !has_target { + self.shulker.set_raw_peek(0); + } } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.peek_time.fetch_sub(1, Ordering::Relaxed); - }) + fn tick(&mut self, _mob: &dyn Mob) { + self.peek_time.fetch_sub(1, Ordering::Relaxed); } } diff --git a/crates/pumpkin/src/entity/mob/skeleton/mod.rs b/crates/pumpkin/src/entity/mob/skeleton/mod.rs index 0e120ffc4..d0f42c8ad 100644 --- a/crates/pumpkin/src/entity/mob/skeleton/mod.rs +++ b/crates/pumpkin/src/entity/mob/skeleton/mod.rs @@ -100,7 +100,10 @@ impl Mob for SkeletonEntityBase { }; let living = &self.mob_entity.living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut first = true; for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { @@ -119,7 +122,10 @@ impl Mob for SkeletonEntityBase { // AbstractSkeleton sets BOW on MAIN_HAND let living = &self.mob_entity.living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, &Item::BOW)); }) } diff --git a/crates/pumpkin/src/entity/mob/slime.rs b/crates/pumpkin/src/entity/mob/slime.rs index d75cacc27..b892c00ad 100644 --- a/crates/pumpkin/src/entity/mob/slime.rs +++ b/crates/pumpkin/src/entity/mob/slime.rs @@ -14,7 +14,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::entity::{ Entity, EntityBase, NbtFuture, ai::control::{Control, MoveControlTrait}, - ai::goal::{Goal, GoalFuture, active_target::ActiveTargetGoal}, + ai::goal::{Goal, active_target::ActiveTargetGoal}, mob::{Mob, MobEntity}, }; use crate::world::World; @@ -286,111 +286,98 @@ impl Mob for SlimeEntity { &self.entity } - fn mob_tick<'a>( - &'a self, - _caller: &'a Arc, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.o_squish.store(self.squish.load()); - self.squish - .store(self.squish.load() + (self.target_squish.load() - self.squish.load()) * 0.5); + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.o_squish.store(self.squish.load()); + self.squish + .store(self.squish.load() + (self.target_squish.load() - self.squish.load()) * 0.5); - let on_ground = self + let on_ground = self + .entity + .living_entity + .entity + .on_ground + .load(Ordering::Relaxed); + let was_on_ground = self.was_on_ground.load(Ordering::Relaxed); + + if on_ground && !was_on_ground { + // TODO: particles + + let world = self.entity.living_entity.entity.world.load(); + world.play_sound_fine( + self.get_squish_sound(), + SoundCategory::Hostile, + &self.entity.living_entity.entity.pos.load(), + self.get_sound_volume(), + ((rand::random_range(0.0..1.0) - rand::random_range(0.0..1.0)) * 0.2 + 1.0) / 0.8, + ); + + self.target_squish.store(-0.5); + } else if !on_ground && was_on_ground { + self.target_squish.store(1.0); + } + + self.was_on_ground.store(on_ground, Ordering::Relaxed); + self.target_squish.store(self.target_squish.load() * 0.6); + + self.is_aggressive.store(false, Ordering::Relaxed); + self.speed_modifier.store(0.0); + } + + fn mob_player_collision(&self, player: &Arc) { + if !self.is_tiny() { + // dealDamage + self.entity.try_attack(self, &**player); + } + } + + fn post_tick(&self) { + if self.entity.living_entity.dead.load(Ordering::Relaxed) + && self.get_size() > 1 + && self + .has_split + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + let size = self.get_size(); + let world = self.entity.living_entity.entity.world.load(); + let pos = self.entity.living_entity.entity.pos.load(); + let half_size = size / 2; + let count = 2 + rand::random_range(0..3); + + let width = self .entity .living_entity .entity - .on_ground - .load(Ordering::Relaxed); - let was_on_ground = self.was_on_ground.load(Ordering::Relaxed); + .entity_dimension + .load() + .width; + let xz_offset = width / 4.0; - if on_ground && !was_on_ground { - // TODO: particles + for i in 0..count { + let xd = ((i % 2) as f32 - 0.5) * xz_offset; + let zd = ((i / 2) as f32 - 0.5) * xz_offset; - let world = self.entity.living_entity.entity.world.load(); - world.play_sound_fine( - self.get_squish_sound(), - SoundCategory::Hostile, - &self.entity.living_entity.entity.pos.load(), - self.get_sound_volume(), - ((rand::random_range(0.0..1.0) - rand::random_range(0.0..1.0)) * 0.2 + 1.0) - / 0.8, + let new_pos = pumpkin_util::math::vector3::Vector3::new( + pos.x + xd as f64, + pos.y + 0.5, + pos.z + zd as f64, ); - - self.target_squish.store(-0.5); - } else if !on_ground && was_on_ground { - self.target_squish.store(1.0); - } - - self.was_on_ground.store(on_ground, Ordering::Relaxed); - self.target_squish.store(self.target_squish.load() * 0.6); - - self.is_aggressive.store(false, Ordering::Relaxed); - self.speed_modifier.store(0.0); - }) - } - - fn mob_player_collision<'a>( - &'a self, - player: &'a Arc, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - Box::pin(async move { - if !self.is_tiny() { - // dealDamage - self.entity.try_attack(self, &**player).await; - } - }) - } - - fn post_tick(&self) -> crate::entity::EntityBaseFuture<'_, ()> { - Box::pin(async move { - if self.entity.living_entity.dead.load(Ordering::Relaxed) - && self.get_size() > 1 - && self - .has_split - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - let size = self.get_size(); - let world = self.entity.living_entity.entity.world.load(); - let pos = self.entity.living_entity.entity.pos.load(); - let half_size = size / 2; - let count = 2 + rand::random_range(0..3); - - let width = self + let new_entity = Entity::new( + world.clone(), + new_pos, + self.entity.living_entity.entity.entity_type, + ); + let slime_like = Self::new(new_entity); + slime_like.set_size(half_size, true); + slime_like .entity .living_entity .entity - .entity_dimension - .load() - .width; - let xz_offset = width / 4.0; - - for i in 0..count { - let xd = ((i % 2) as f32 - 0.5) * xz_offset; - let zd = ((i / 2) as f32 - 0.5) * xz_offset; - - let new_pos = pumpkin_util::math::vector3::Vector3::new( - pos.x + xd as f64, - pos.y + 0.5, - pos.z + zd as f64, - ); - let new_entity = Entity::new( - world.clone(), - new_pos, - self.entity.living_entity.entity.entity_type, - ); - let slime_like = Self::new(new_entity); - slime_like.set_size(half_size, true); - slime_like - .entity - .living_entity - .entity - .yaw - .store(rand::random_range(0.0..360.0)); - world.spawn_entity(slime_like).await; - } + .yaw + .store(rand::random_range(0.0..360.0)); + world.spawn_entity_non_save(slime_like as Arc); } - }) + } } } @@ -478,25 +465,21 @@ impl SlimeFloatGoal { } impl Goal for SlimeFloatGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let entity = &self.slime.entity.living_entity.entity; - entity.touching_water.load(Ordering::Relaxed) - || entity.touching_lava.load(Ordering::Relaxed) - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let entity = &self.slime.entity.living_entity.entity; + entity.touching_water.load(Ordering::Relaxed) + || entity.touching_lava.load(Ordering::Relaxed) } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if rand::random_range(0.0..1.0) < 0.8 { - self.slime - .entity - .living_entity - .jumping - .store(true, Ordering::SeqCst); - } - self.slime.speed_modifier.store(1.2); - }) + fn tick(&mut self, _mob: &dyn Mob) { + if rand::random_range(0.0..1.0) < 0.8 { + self.slime + .entity + .living_entity + .jumping + .store(true, Ordering::SeqCst); + } + self.slime.speed_modifier.store(1.2); } fn should_run_every_tick(&self) -> bool { @@ -523,40 +506,29 @@ impl SlimeAttackGoal { } impl Goal for SlimeAttackGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = self.slime.entity.target.lock().await; - target.is_some() - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + self.slime.entity.get_target().is_some() } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.grow_tired_timer = 300; - }) + fn start(&mut self, _mob: &dyn Mob) { + self.grow_tired_timer = 300; } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = self.slime.entity.target.lock().await; - target.is_some() && self.grow_tired_timer > 0 - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + self.slime.entity.get_target().is_some() && self.grow_tired_timer > 0 } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.grow_tired_timer -= 1; - let target_guard = self.slime.entity.target.lock().await; - if let Some(target) = target_guard.as_ref() { - let pos = target.get_entity().pos.load(); - let my_pos = self.slime.entity.living_entity.entity.pos.load(); - let dx = pos.x - my_pos.x; - let dz = pos.z - my_pos.z; - let yaw = dx.atan2(dz).to_degrees() as f32; - self.slime.target_yaw.store(yaw); - } - self.slime.is_aggressive.store(true, Ordering::Relaxed); - }) + fn tick(&mut self, _mob: &dyn Mob) { + self.grow_tired_timer -= 1; + if let Some(target) = self.slime.entity.get_target() { + let pos = target.get_entity().pos.load(); + let my_pos = self.slime.entity.living_entity.entity.pos.load(); + let dx = pos.x - my_pos.x; + let dz = pos.z - my_pos.z; + let yaw = dx.atan2(dz).to_degrees() as f32; + self.slime.target_yaw.store(yaw); + } + self.slime.is_aggressive.store(true, Ordering::Relaxed); } fn should_run_every_tick(&self) -> bool { @@ -585,44 +557,39 @@ impl SlimeRandomDirectionGoal { } impl Goal for SlimeRandomDirectionGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let target = self.slime.entity.target.lock().await; - target.is_none() - && (self + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + self.slime.entity.get_target().is_none() + && (self + .slime + .entity + .living_entity + .entity + .on_ground + .load(Ordering::Relaxed) + || self .slime .entity .living_entity .entity - .on_ground + .touching_water .load(Ordering::Relaxed) - || self - .slime - .entity - .living_entity - .entity - .touching_water - .load(Ordering::Relaxed) - || self - .slime - .entity - .living_entity - .entity - .touching_lava - .load(Ordering::Relaxed)) - }) + || self + .slime + .entity + .living_entity + .entity + .touching_lava + .load(Ordering::Relaxed)) } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.next_randomize_time -= 1; - if self.next_randomize_time <= 0 { - self.next_randomize_time = rand::random_range(40..100); - self.chosen_degrees = rand::random_range(0.0..360.0); - } - self.slime.target_yaw.store(self.chosen_degrees); - self.slime.is_aggressive.store(false, Ordering::Relaxed); - }) + fn tick(&mut self, _mob: &dyn Mob) { + self.next_randomize_time -= 1; + if self.next_randomize_time <= 0 { + self.next_randomize_time = rand::random_range(40..100); + self.chosen_degrees = rand::random_range(0.0..360.0); + } + self.slime.target_yaw.store(self.chosen_degrees); + self.slime.is_aggressive.store(false, Ordering::Relaxed); } fn controls(&self) -> crate::entity::ai::goal::Controls { @@ -635,23 +602,19 @@ pub struct SlimeKeepOnJumpingGoal { } impl SlimeKeepOnJumpingGoal { + #[must_use] pub const fn new(slime: Arc) -> Self { Self { slime } } } impl Goal for SlimeKeepOnJumpingGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let vehicle = self.slime.entity.living_entity.entity.vehicle.lock().await; - vehicle.is_none() - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + !self.slime.entity.living_entity.entity.has_vehicle() } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - self.slime.speed_modifier.store(1.0); - }) + fn tick(&mut self, _mob: &dyn Mob) { + self.slime.speed_modifier.store(1.0); } fn controls(&self) -> crate::entity::ai::goal::Controls { diff --git a/crates/pumpkin/src/entity/mob/witch.rs b/crates/pumpkin/src/entity/mob/witch.rs index b35bb9f7c..8d3c39465 100644 --- a/crates/pumpkin/src/entity/mob/witch.rs +++ b/crates/pumpkin/src/entity/mob/witch.rs @@ -13,7 +13,7 @@ use pumpkin_data::tracked_data; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_attack::RangedAttackGoal, revenge::RevengeGoal, @@ -132,13 +132,13 @@ impl WitchEntity { self.drinking_potion.load(Ordering::Relaxed) } - pub async fn throw_potion(&self, target: &Arc) { + pub fn throw_potion(&self, target: &Arc) { if self.is_drinking_potion() { return; } let entity = &self.mob_entity.living_entity.entity; - let world = entity.world.load(); + let world = entity.world.load_full(); let target_entity = target.get_entity(); let target_pos = target_entity.pos.load(); @@ -154,15 +154,13 @@ impl WitchEntity { if let Some(target_living) = target.get_living_entity() { let r: f32 = rand::random(); - if dist >= 8.0 && !target_living.has_effect(&StatusEffect::SLOWNESS).await { + if dist >= 8.0 && !target_living.has_effect(&StatusEffect::SLOWNESS) { potion = &Potion::SLOWNESS; } else if target_living.health.load() >= 8.0 - && !target_living.has_effect(&StatusEffect::POISON).await + && !target_living.has_effect(&StatusEffect::POISON) { potion = &Potion::POISON; - } else if dist <= 3.0 - && !target_living.has_effect(&StatusEffect::WEAKNESS).await - && r < 0.25 + } else if dist <= 3.0 && !target_living.has_effect(&StatusEffect::WEAKNESS) && r < 0.25 { potion = &Potion::WEAKNESS; } @@ -172,7 +170,7 @@ impl WitchEntity { let splash_entity = Entity::new(world.clone(), witch_pos, &EntityType::SPLASH_POTION); let splash = SplashPotionEntity::new_shot(splash_entity, entity); - splash.set_item_stack(potion_stack).await; + splash.set_item_stack(potion_stack); let speed = if dist <= 2.0 { 0.45 } else { 0.75 }; let yo = dist * 0.2; @@ -184,7 +182,7 @@ impl WitchEntity { } let splash_arc: Arc = Arc::new(splash); - world.spawn_entity(splash_arc).await; + world.spawn_entity(splash_arc); } } @@ -219,19 +217,13 @@ impl Mob for WitchEntity { }) } - fn pre_damage<'a>( - &'a self, - _damage_type: DamageType, - source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if let Some(src) = source - && src.get_entity().entity_id == self.mob_entity.living_entity.entity.entity_id - { - return false; - } - true - }) + fn pre_damage(&self, _damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool { + if let Some(src) = source + && src.get_entity().entity_id == self.mob_entity.living_entity.entity.entity_id + { + return false; + } + true } fn modify_incoming_damage(&self, mut amount: f32, damage_type: DamageType) -> f32 { @@ -245,17 +237,21 @@ impl Mob for WitchEntity { amount } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; - let living = &self.mob_entity.living_entity; - let world = entity.world.load(); + fn mob_tick<'a>(&'a self, caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; + let living = &self.mob_entity.living_entity; + let world = entity.world.load(); - if self.is_drinking_potion() { - let remaining = self.using_time.fetch_sub(1, Ordering::Relaxed) - 1; - if remaining <= 0 { - self.set_drinking_potion(false); - let mut equipment = living.entity_equipment.lock().await; + if self.is_drinking_potion() { + let remaining = self.using_time.fetch_sub(1, Ordering::Relaxed) - 1; + if remaining <= 0 { + self.set_drinking_potion(false); + if let Some(witch) = caller.cast_any().downcast_ref::() { + let living = &witch.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let stack = equipment.get(&EquipmentSlot::MAIN_HAND); equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::EMPTY.clone()); drop(equipment); @@ -270,65 +266,64 @@ impl Mob for WitchEntity { effects, 1.0, crate::item::potion::PotionApplicationSource::Normal, - ) - .await; + ); } - } else { - let mut potion: Option<&'static Potion> = None; - let r: f32 = rand::random(); + } + } else { + let mut potion: Option<&'static Potion> = None; + let r: f32 = rand::random(); - if r < 0.15 - && entity.touching_water.load(Ordering::Relaxed) - && !living.has_effect(&StatusEffect::WATER_BREATHING).await - { - potion = Some(&Potion::WATER_BREATHING); - } else if r < 0.15 - && entity.fire_ticks.load(Ordering::Relaxed) > 0 - && !living.has_effect(&StatusEffect::FIRE_RESISTANCE).await - { - potion = Some(&Potion::FIRE_RESISTANCE); - } else if r < 0.05 && living.health.load() < living.get_max_health() { - potion = Some(&Potion::HEALING); - } else if r < 0.5 - && let Some(target) = self.mob_entity.target.lock().await.as_ref() - && !living.has_effect(&StatusEffect::SPEED).await - { - let target_pos = target.get_entity().pos.load(); - let self_pos = entity.pos.load(); - if self_pos.squared_distance_to_vec(&target_pos) > 121.0 { - potion = Some(&Potion::SWIFTNESS); - } + if r < 0.15 + && entity.touching_water.load(Ordering::Relaxed) + && !living.has_effect(&StatusEffect::WATER_BREATHING) + { + potion = Some(&Potion::WATER_BREATHING); + } else if r < 0.15 + && entity.fire_ticks.load(Ordering::Relaxed) > 0 + && !living.has_effect(&StatusEffect::FIRE_RESISTANCE) + { + potion = Some(&Potion::FIRE_RESISTANCE); + } else if r < 0.05 && living.health.load() < living.get_max_health() { + potion = Some(&Potion::HEALING); + } else if r < 0.5 + && let Some(target) = self.mob_entity.get_target() + && !living.has_effect(&StatusEffect::SPEED) + { + let target_pos = target.get_entity().pos.load(); + let self_pos = entity.pos.load(); + if self_pos.squared_distance_to_vec(&target_pos) > 121.0 { + potion = Some(&Potion::SWIFTNESS); } + } - if let Some(potion) = potion { - let stack = create_potion_stack(&Item::POTION, potion); - let mut equipment = living.entity_equipment.lock().await; + if let Some(potion) = potion { + let stack = create_potion_stack(&Item::POTION, potion); + if let Some(witch) = caller.cast_any().downcast_ref::() { + let living = &witch.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment.put(&EquipmentSlot::MAIN_HAND, stack.clone()); drop(equipment); living.send_equipment_changes(&[(EquipmentSlot::MAIN_HAND, stack)]); + } - self.using_time.store(32, Ordering::Relaxed); - self.set_drinking_potion(true); + self.using_time.store(32, Ordering::Relaxed); + self.set_drinking_potion(true); - if !entity.silent.load(Ordering::Relaxed) { - let pos = entity.pos.load(); - world.play_sound(Sound::EntityWitchDrink, SoundCategory::Hostile, &pos); - } + if !entity.silent.load(Ordering::Relaxed) { + let pos = entity.pos.load(); + world.play_sound(Sound::EntityWitchDrink, SoundCategory::Hostile, &pos); } } - }) + } } } impl RangedAttackMob for WitchEntity { - fn perform_ranged_attack<'a>( - &'a self, - target: &'a Arc, - _power: f32, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.throw_potion(target).await; - }) + fn perform_ranged_attack(&self, target: &Arc, _power: f32) { + self.throw_potion(target); } } diff --git a/crates/pumpkin/src/entity/mob/zombie/mod.rs b/crates/pumpkin/src/entity/mob/zombie/mod.rs index a3c83af9e..05a28afdd 100644 --- a/crates/pumpkin/src/entity/mob/zombie/mod.rs +++ b/crates/pumpkin/src/entity/mob/zombie/mod.rs @@ -104,7 +104,7 @@ impl ZombieEntityBase { self.can_break_doors.load(Ordering::Relaxed) } - pub async fn set_can_break_doors(&self, can_break_doors: bool, mob: &dyn Mob) { + pub fn set_can_break_doors(&self, can_break_doors: bool, mob: &dyn Mob) { if self .can_break_doors .swap(can_break_doors, Ordering::Relaxed) @@ -120,11 +120,11 @@ impl ZombieEntityBase { goal_selector.add_goal(1, Box::new(BreakDoorGoal::default())); Vec::new() } else { - goal_selector.remove_goal_sync::() + goal_selector.remove_goals::() } }; for goal in &mut stopped { - goal.stop(mob).await; + goal.stop(mob); } } } @@ -159,7 +159,10 @@ impl Mob for ZombieEntityBase { }; let living = &self.mob_entity.living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut first = true; for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { @@ -189,7 +192,10 @@ impl Mob for ZombieEntityBase { _ => &Item::IRON_SHOVEL, }; let living = &self.mob_entity.living_entity; - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, weapon_item)); } }) @@ -206,7 +212,7 @@ impl Mob for ZombieEntityBase { fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { if let Some(can_break_doors) = nbt.get_bool("CanBreakDoors") { - self.set_can_break_doors(can_break_doors, self).await; + self.set_can_break_doors(can_break_doors, self); } }) } diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs index cdadcf5dd..8b3baa90c 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -171,18 +171,12 @@ pub trait EntityBase: Send + Sync + std::any::Any { /// but in some scenarios (e.g., interactions or events), it might be a different entity. /// /// The `server` parameter provides access to the game server instance. - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if let Some(living) = self.get_living_entity() { - living.tick(caller, server).await; - } else { - self.get_entity().tick(caller, server).await; - } - }) + fn tick(&self, caller: &Arc, server: &Server) { + if let Some(living) = self.get_living_entity() { + living.tick(caller, server); + } else { + self.get_entity().tick(caller, server); + } } fn get_job_site_pos(&self) -> Option { @@ -213,22 +207,20 @@ pub trait EntityBase: Send + Sync + std::any::Any { Vector3::from_yaw_pitch(entity.yaw.load(), entity.pitch.load()) } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); + fn init_data_tracker(&self) { + let entity = self.get_entity(); - // If the internal age is negative, it's a baby - let is_baby = entity.age.load(Ordering::Relaxed) < 0; + // If the internal age is negative, it's a baby + let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - let mut bedrock_meta = SyncedActorDataList::new(); - bedrock_meta.set_flag(entity_data_key::FLAGS, entity_data_flag::BABY as u8, true); - entity.send_meta_data( - &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], - Some(&bedrock_meta), - ); - } - }) + if is_baby { + let mut bedrock_meta = SyncedActorDataList::new(); + bedrock_meta.set_flag(entity_data_key::FLAGS, entity_data_flag::BABY as u8, true); + entity.send_meta_data( + &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], + Some(&bedrock_meta), + ); + } } fn set_variant_name(&self, _name: &str) {} @@ -265,22 +257,13 @@ pub trait EntityBase: Send + Sync + std::any::Any { None } - fn tick_in_void<'a>(&'a self, _dyn_self: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.get_entity().remove().await }) + fn tick_in_void(&self, _dyn_self: &dyn EntityBase) { + self.get_entity().remove(); } /// Returns if damage was successful or not - fn damage<'a>( - &'a self, - caller: &'a dyn EntityBase, - amount: f32, - damage_type: DamageType, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - caller - .damage_with_context(caller, amount, damage_type, None, None, None) - .await - }) + fn damage(&self, caller: &dyn EntityBase, amount: f32, damage_type: DamageType) -> bool { + caller.damage_with_context(caller, amount, damage_type, None, None, None) } fn on_lightning_strike<'a>( @@ -291,7 +274,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { Box::pin(async move { if self.get_living_entity().is_some() { self.set_on_fire_for(8.0); - let cause = lightning.get_cause().await; + let cause = lightning.get_cause(); self.damage_with_context( caller, 5.0, @@ -299,8 +282,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { None, Some(lightning), cause.as_deref().map(|p| p as &dyn EntityBase), - ) - .await; + ); } }) } @@ -407,23 +389,26 @@ pub trait EntityBase: Send + Sync + std::any::Any { }) } - fn damage_with_context<'a>( - &'a self, - caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + caller: &dyn EntityBase, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if caller.get_living_entity().is_some() { - return caller - .damage_with_context(caller, amount, damage_type, position, source, cause) - .await; - } - false - }) + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + if let Some(living) = caller.get_living_entity() { + return living.damage_with_context( + caller, + amount, + damage_type, + position, + source, + cause, + ); + } + false } /// Called when a player right-clicks this entity with an item. @@ -451,11 +436,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { ticks as f32 / 20.0, ); if let Some(server) = entity.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut event).await; - }); - }); + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -466,17 +447,15 @@ pub trait EntityBase: Send + Sync + std::any::Any { // TODO: defrost } - /// Called when a player collides with a entity - fn on_player_collision<'a>(&'a self, _player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async {}) - } + /// Called when a player collides with an entity + fn on_player_collision(&self, _player: &Arc) {} fn is_passenger(&self) -> EntityBaseFuture<'_, bool> { - Box::pin(async move { self.get_entity().has_vehicle().await }) + Box::pin(async move { self.get_entity().has_vehicle() }) } fn is_vehicle(&self) -> EntityBaseFuture<'_, bool> { - Box::pin(async move { self.get_entity().has_passengers().await }) + Box::pin(async move { self.get_entity().has_passengers() }) } fn has_passenger<'a>(&'a self, other: &'a Arc) -> EntityBaseFuture<'a, bool> { @@ -490,14 +469,8 @@ pub trait EntityBase: Send + Sync + std::any::Any { }) } - fn move_entity<'a>( - &'a self, - caller: &'a Arc, - motion: Vector3, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.get_entity().move_entity(caller, motion).await; - }) + fn move_entity(&self, caller: &Arc, motion: Vector3) { + self.get_entity().move_entity(caller, motion); } fn is_pushable(&self) -> bool { @@ -509,8 +482,8 @@ pub trait EntityBase: Send + Sync + std::any::Any { let self_entity = self.get_entity(); let other_entity = entity.get_entity(); - if self_entity.no_clip.load(Ordering::Relaxed) - || other_entity.no_clip.load(Ordering::Relaxed) + if self_entity.no_physics.load(Ordering::Relaxed) + || other_entity.no_physics.load(Ordering::Relaxed) { return; } @@ -550,7 +523,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { dx *= 0.05; dz *= 0.05; - if !self_entity.has_passengers().await && self.is_pushable() { + if !self_entity.has_passengers() && self.is_pushable() { let mut vel = self_entity.velocity.load(); vel.x -= dx; vel.z -= dz; @@ -558,7 +531,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { self_entity.send_velocity(); } - if !other_entity.has_passengers().await && entity.is_pushable() { + if !other_entity.has_passengers() && entity.is_pushable() { let mut vel = other_entity.velocity.load(); vel.x += dx; vel.z += dz; @@ -648,7 +621,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { if (is_iron_golem || is_other_minecart || is_vehicle - || !other.get_entity().has_vehicle().await) + || !other.get_entity().has_vehicle()) && other.is_pushable() { dyn_self.push(&other).await; @@ -698,13 +671,9 @@ pub trait EntityBase: Send + Sync + std::any::Any { }) } - fn on_hit(&self, _hit: crate::entity::projectile::ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn on_hit(&self, _hit: crate::entity::projectile::ProjectileHit) {} - fn set_paddle_state(&self, _left: bool, _right: bool) -> EntityBaseFuture<'_, ()> { - Box::pin(async {}) - } + fn set_paddle_state(&self, _left: bool, _right: bool) {} fn is_in_love(&self) -> bool { false @@ -777,12 +746,10 @@ pub trait EntityBase: Send + Sync + std::any::Any { fn kill<'a>(&'a self, caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { Box::pin(async move { if self.get_living_entity().is_some() { - caller - .damage(caller, f32::MAX, DamageType::GENERIC_KILL) - .await; + caller.damage(caller, f32::MAX, DamageType::GENERIC_KILL); } else { // TODO this should be removed once all entities are implemented - self.get_entity().remove().await; + self.get_entity().remove(); } }) } @@ -894,7 +861,7 @@ pub struct Entity { /// Whether this entity is invulnerable to all damage pub invulnerable: AtomicBool, /// List of damage types this entity is immune to - pub damage_immunities: Mutex>, + pub damage_immunities: std::sync::Mutex>, // Whether the entity is immune to fire (to disable visual fire and fire damage) pub fire_immune: AtomicBool, pub fire_ticks: AtomicI32, @@ -912,7 +879,7 @@ pub struct Entity { /// The vehicle that entity is in pub vehicle: Mutex>>, /// The entity this entity is attached/leashed to (if any) - pub leashed_to: Mutex>>, + pub leashed_to: std::sync::Mutex>>, /// Cooldown before entity can mount again after dismounting pub riding_cooldown: AtomicI32, /// The age of the entity in ticks. Negative values indicate a baby. @@ -941,8 +908,8 @@ pub struct Entity { pub bedrock_flags: std::sync::atomic::AtomicI64, /// Stores more Bedrock-specific entity boolean flags (bit 0-63) pub bedrock_flags_two: std::sync::atomic::AtomicI64, - /// If true, the entity cannot collide with anything (e.g. spectator) - pub no_clip: AtomicBool, + /// If true, the entity bypasses physics, collisions, and block effects (e.g. spectator, markers, display entities) + pub no_physics: AtomicBool, /// Multiplies movement for one tick before being reset pub movement_multiplier: AtomicCell>, /// Determines whether the entity's velocity needs to be sent @@ -1006,6 +973,10 @@ impl Entity { eye_height: entity_type.eye_height, }; + let current_biome = world + .level + .get_rough_biome(&BlockPos::new(floor_x, floor_y, floor_z)); + Self { entity_id, entity_uuid, @@ -1046,7 +1017,7 @@ impl Entity { )), entity_dimension: AtomicCell::new(bounding_box_size), invulnerable: AtomicBool::new(false), - damage_immunities: Mutex::new(Vec::new()), + damage_immunities: std::sync::Mutex::new(Vec::new()), data: AtomicI32::new(0), flags: std::sync::atomic::AtomicI8::new(0), bedrock_flags: std::sync::atomic::AtomicI64::new(0), @@ -1060,11 +1031,11 @@ impl Entity { removal_reason: AtomicCell::new(None), passengers: Mutex::new(Vec::new()), vehicle: Mutex::new(None), - leashed_to: Mutex::new(None), + leashed_to: std::sync::Mutex::new(None), riding_cooldown: AtomicI32::new(0), age: AtomicI32::new(0), - current_biome: ArcSwap::new(Arc::new(&Biome::PLAINS)), + current_biome: ArcSwap::new(Arc::new(current_biome)), last_biome_update_pos: AtomicCell::new(BlockPos::new(floor_x, floor_y, floor_z)), portal_cooldown: AtomicU32::new(0), portal_manager: Mutex::new(None), @@ -1073,7 +1044,7 @@ impl Entity { silent: AtomicBool::new(false), has_no_gravity: AtomicBool::new(false), scoreboard_tags: Mutex::new(HashSet::new()), - no_clip: AtomicBool::new(false), + no_physics: AtomicBool::new(false), movement_multiplier: AtomicCell::new(Vector3::default()), velocity_dirty: AtomicBool::new(true), removed: AtomicBool::new(false), @@ -1097,6 +1068,10 @@ impl Entity { /// Updates the world reference for this entity. /// Called when the entity changes dimensions (e.g., through a nether portal). pub fn set_world(&self, world: Arc) { + let block_pos = self.block_pos.load(); + let biome = world.level.get_rough_biome(&block_pos); + self.current_biome.store(Arc::new(biome)); + self.last_biome_update_pos.store(block_pos); self.world.store(world); } @@ -1260,7 +1235,7 @@ impl Entity { pub fn send_velocity(&self) { let velocity = self.velocity.load(); let chunk_pos = self.chunk_pos.load(); - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &CEntityVelocity::new(self.entity_id.into(), velocity), &CSetActorMotion { @@ -1313,7 +1288,13 @@ impl Entity { || floor_z != block_pos_vec.z { let new_block_pos = Vector3::new(floor_x, floor_y, floor_z); - self.block_pos.store(BlockPos(new_block_pos)); + let new_bp = BlockPos(new_block_pos); + self.block_pos.store(new_bp); + + let world = self.world.load(); + let biome = world.level.get_rough_biome(&new_bp); + self.current_biome.store(Arc::new(biome)); + self.last_biome_update_pos.store(new_bp); let chunk_pos = self.chunk_pos.load(); if get_section_cord(floor_x) != chunk_pos.x @@ -1413,7 +1394,7 @@ impl Entity { } #[expect(clippy::float_cmp)] - async fn adjust_movement_for_collisions( + fn adjust_movement_for_collisions( &self, movement: Vector3, caller: &dyn EntityBase, @@ -1431,8 +1412,7 @@ impl Entity { let (collisions, block_positions) = self .world .load() - .get_block_collisions(bounding_box.stretch(movement), caller) - .await; + .get_block_collisions(bounding_box.stretch(movement), caller); if collisions.is_empty() { return movement; @@ -1646,7 +1626,11 @@ impl Entity { */ } - async fn tick_block_collisions(&self, caller: &Arc, server: &Server) -> bool { + pub fn tick_block_collisions(&self, caller: &Arc, _server: &Server) -> bool { + if !self.is_affected_by_blocks() { + return false; + } + let bounding_box = self.bounding_box.load(); let aabb = bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7); @@ -1689,22 +1673,26 @@ impl Entity { caller.as_ref(), &pos, ) - .await } else { world .block_registry .get_inside_collision_shape(block, &world, state, &pos) - .await }; if bounding_box.intersects(&collision_shape.at_pos(pos)) { if block == &Block::POWDER_SNOW { self.is_in_powder_snow.store(true, Relaxed); } - world - .block_registry - .on_entity_collision(block, &world, caller.as_ref(), &pos, state, server) - .await; + if let Some(server_arc) = world.server.upgrade() { + world.block_registry.on_entity_collision( + block, + &world, + caller.as_ref(), + &pos, + state, + &server_arc, + ); + } } } @@ -1752,7 +1740,7 @@ impl Entity { self.on_ground.load(Relaxed), ); if self.entity_type == &EntityType::PLAYER { - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMovePlayer::new( @@ -1779,7 +1767,7 @@ impl Entity { if self.on_ground.load(Relaxed) { flags |= MOVE_ACTOR_DELTA_FLAG_ON_GROUND; } - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMoveActorDelta::new( @@ -1801,7 +1789,7 @@ impl Entity { self.on_ground.load(Relaxed), ); if self.entity_type == &EntityType::PLAYER { - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMovePlayer::new( @@ -1826,7 +1814,7 @@ impl Entity { flags |= MOVE_ACTOR_DELTA_FLAG_ON_GROUND; } - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMoveActorDelta::new( @@ -1849,7 +1837,7 @@ impl Entity { self.on_ground.load(Relaxed), ); if self.entity_type == &EntityType::PLAYER { - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMovePlayer::new( @@ -1873,7 +1861,7 @@ impl Entity { if self.on_ground.load(Relaxed) { flags |= MOVE_ACTOR_DELTA_FLAG_ON_GROUND; } - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMoveActorDelta::new( @@ -1947,7 +1935,7 @@ impl Entity { ); if self.entity_type == &EntityType::PLAYER { - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMovePlayer::new( @@ -1972,7 +1960,7 @@ impl Entity { flags |= MOVE_ACTOR_DELTA_FLAG_ON_GROUND; } - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( chunk_pos, &je_packet, &CMoveActorDelta::new( @@ -1991,7 +1979,7 @@ impl Entity { // updateWaterState() in yarn - async fn update_fluid_state(&self, caller: &Arc) { + fn update_fluid_state(&self, caller: &Arc) { let is_pushed = caller.is_pushed_by_fluids(); let mut fluids = BTreeMap::new(); @@ -2069,8 +2057,7 @@ impl Entity { for (_, fluid) in fluids { world .block_registry - .on_entity_collision_fluid(fluid, caller.as_ref()) - .await; + .on_entity_collision_fluid(fluid, caller.as_ref()); } let lava_speed = if world.dimension == Dimension::THE_NETHER { @@ -2272,17 +2259,15 @@ impl Entity { // Move by a delta, adjust for collisions, and send // Does not send movement. That must be done separately - pub async fn move_entity<'a>( - &'a self, - caller: &'a Arc, - mut motion: Vector3, - ) { + pub fn move_entity(&self, caller: &Arc, mut motion: Vector3) { if caller.get_player().is_some() { return; } - if self.no_clip.load(Ordering::Relaxed) { + if self.no_physics.load(Ordering::Relaxed) { self.move_pos(motion); + self.horizontal_collision.store(false, Ordering::Relaxed); + self.on_ground.store(false, Ordering::Relaxed); return; } @@ -2299,9 +2284,7 @@ impl Entity { self.velocity.store(Vector3::default()); } - let final_move = self - .adjust_movement_for_collisions(motion, caller.as_ref()) - .await; + let final_move = self.adjust_movement_for_collisions(motion, caller.as_ref()); self.move_pos(final_move); @@ -2310,14 +2293,8 @@ impl Entity { self.velocity.store(final_move * velocity_multiplier); if let Some(living) = caller.get_living_entity() { - living - .fall( - caller.clone(), - final_move.y, - self.on_ground.load(Ordering::SeqCst), - false, - ) - .await; + let on_ground = self.on_ground.load(Ordering::SeqCst); + living.fall(caller.as_ref(), final_move.y, on_ground, false); } if motion.y != final_move.y { @@ -2325,8 +2302,7 @@ impl Entity { let block = self.get_block_with_y_offset(0.2).1; world .block_registry - .update_entity_movement_after_fall_on(block, caller.as_ref()) - .await; + .update_entity_movement_after_fall_on(block, caller.as_ref()); } } @@ -2385,14 +2361,18 @@ impl Entity { self.velocity.store(velo); } - async fn tick_portal(&self, caller: &Arc) { + fn tick_portal(&self, caller: &Arc) { if self.portal_cooldown.load(Ordering::Relaxed) > 0 { self.portal_cooldown.fetch_sub(1, Ordering::Relaxed); } - let mut manager_guard = self.portal_manager.lock().await; + let Ok(mut manager_guard) = self.portal_manager.try_lock() else { + return; + }; let mut should_remove = false; if let Some(pmanager_mutex) = manager_guard.as_ref() { - let mut portal_processor = pmanager_mutex.lock().await; + let Ok(mut portal_processor) = pmanager_mutex.try_lock() else { + return; + }; if portal_processor.process_portal_teleportation( &self.world.load(), caller.as_ref(), @@ -2401,36 +2381,50 @@ impl Entity { self.portal_cooldown .store(self.default_portal_cooldown(), Ordering::Relaxed); - let transition = portal_processor - .portal_type - .get_portal_destination( - &self.world.load(), - portal_processor.destination_world.clone(), - caller, - portal_processor.entry_position, - portal_processor.source_portal.clone(), - ) - .await; + let caller_clone = caller.clone(); + let world_clone = self.world.load_full(); + let portal_type = portal_processor.portal_type; + let dest_world_opt = portal_processor.destination_world.clone(); + let entry_pos = portal_processor.entry_position; + let src_portal = portal_processor.source_portal.clone(); + let entity_id = self.entity_id; + let yaw = self.yaw.load(); - drop(portal_processor); - - if let Some(transition) = transition { - let dest_world = transition.new_world.clone(); - let yaw = transition.yaw; - let pitch = transition.pitch; - let teleport_pos = transition.position; - - // Teleport the main entity - caller - .clone() - .teleport(teleport_pos, yaw, pitch, dest_world.clone()) + tokio::spawn(async move { + let transition = portal_type + .get_portal_destination( + &world_clone, + dest_world_opt, + &caller_clone, + entry_pos, + src_portal, + ) .await; - // Teleport all passengers recursively along with the vehicle - let yaw_delta = yaw.map(|y| y - self.yaw.load()); - Self::teleport_passengers_recursive(self, teleport_pos, yaw_delta, &dest_world) - .await; - } + if let Some(transition) = transition { + let dest_world = transition.new_world.clone(); + let yaw_val = transition.yaw; + let pitch = transition.pitch; + let teleport_pos = transition.position; + + // Teleport the main entity + caller_clone + .teleport(teleport_pos, yaw_val, pitch, dest_world.clone()) + .await; + + // Teleport all passengers recursively along with the vehicle + if let Some(entity) = world_clone.get_entity_by_id(entity_id) { + let yaw_delta = yaw_val.map(|y| y - yaw); + Self::teleport_passengers_recursive( + entity.get_entity(), + teleport_pos, + yaw_delta, + &dest_world, + ) + .await; + } + } + }); } else if portal_processor.portal_time == 0 { should_remove = true; } @@ -2479,26 +2473,23 @@ impl Entity { }) } - pub async fn try_use_portal( - &self, - _portal_delay: u32, - portal_world: Arc, - pos: BlockPos, - ) { + pub fn try_use_portal(&self, _portal_delay: u32, portal_world: Arc, pos: BlockPos) { let mut portal_event = crate::plugin::api::events::entity::entity_portal::EntityPortalEvent::new( self.entity_id, pos, ); if let Some(server) = self.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut portal_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut portal_event); } if portal_event.cancelled { return; } // Passengers don't teleport independently - they wait for their vehicle - if self.has_vehicle().await { + if self.has_vehicle() { return; } @@ -2518,7 +2509,7 @@ impl Entity { return; } - let mut manager = self.portal_manager.lock().await; + let mut manager = self.portal_manager.blocking_lock(); let world = self.world.load(); if manager.is_none() { let portal_type = if portal_world.dimension == Dimension::THE_END @@ -2559,7 +2550,7 @@ impl Entity { *manager = Some(Mutex::new(new_manager)); } else if let Some(manager) = manager.as_ref() { - let mut manager = manager.lock().await; + let mut manager = manager.blocking_lock(); manager.entry_position = pos; manager.inside_portal_this_tick = true; } @@ -2592,7 +2583,7 @@ impl Entity { /// Mirrors vanilla `LivingEntity#canFreeze`: spectators and entities wearing /// freeze-immune wearables (e.g. leather armor) cannot freeze. - async fn can_freeze(&self, caller: &dyn EntityBase) -> bool { + fn can_freeze(&self, caller: &dyn EntityBase) -> bool { if caller.is_spectator() || self.is_freeze_immune() { return false; } @@ -2601,17 +2592,18 @@ impl Entity { return true; }; - let equipment = living.entity_equipment.lock().await; - for (slot, stack) in &equipment.equipment { - if (*slot == EquipmentSlot::HEAD - || *slot == EquipmentSlot::CHEST - || *slot == EquipmentSlot::LEGS - || *slot == EquipmentSlot::FEET) - && stack - .get_item() - .has_tag(&tag::Item::MINECRAFT_FREEZE_IMMUNE_WEARABLES) - { - return false; + if let Ok(equipment) = living.entity_equipment.try_lock() { + for (slot, stack) in &equipment.equipment { + if (*slot == EquipmentSlot::HEAD + || *slot == EquipmentSlot::CHEST + || *slot == EquipmentSlot::LEGS + || *slot == EquipmentSlot::FEET) + && stack + .get_item() + .has_tag(&tag::Item::MINECRAFT_FREEZE_IMMUNE_WEARABLES) + { + return false; + } } } @@ -2622,8 +2614,8 @@ impl Entity { /// In powder snow and freezeable: `frozen_ticks` increases by 1 (up to `MAX_FROZEN_TICKS`) /// Otherwise: `frozen_ticks` decreases by 2 (down to 0) /// When fully frozen, deals 1 damage every 40 ticks - pub async fn tick_frozen(&self, caller: &dyn EntityBase) { - let can_freeze = self.can_freeze(caller).await; + pub fn tick_frozen(&self, caller: &dyn EntityBase) { + let can_freeze = self.can_freeze(caller); let in_powder_snow = self.is_in_powder_snow(); let old_frozen_ticks = self.frozen_ticks.load(Ordering::Relaxed); @@ -2657,7 +2649,10 @@ impl Entity { && new_frozen_ticks >= Self::MAX_FROZEN_TICKS && self.age.load(Ordering::Relaxed) % Self::FREEZE_DAMAGE_INTERVAL == 0 { - caller.damage(caller, 1.0, DamageType::FREEZE).await; + let world = self.world.load_full(); + if let Some(entity) = world.get_entity_by_id(self.entity_id) { + entity.damage(entity.as_ref(), 1.0, DamageType::FREEZE); + } } } @@ -2673,8 +2668,8 @@ impl Entity { } /// Removes the `Entity` from their current `World` - pub async fn remove(&self) { - self.world.load().remove_entity(self).await; + pub fn remove(&self) { + self.world.load().remove_entity(self); } pub fn create_spawn_packet(&self) -> CSpawnEntity { @@ -2751,7 +2746,7 @@ impl Entity { self.sneaking.load(Ordering::Relaxed) } - pub async fn set_swimming(&self, swimming: bool) { + pub fn set_swimming(&self, swimming: bool) { if self.swimming.load(Ordering::Relaxed) != swimming { let mut event = crate::plugin::api::events::entity::entity_toggle_swim::EntityToggleSwimEvent::new( @@ -2759,7 +2754,7 @@ impl Entity { swimming, ); if let Some(server) = self.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; @@ -2770,9 +2765,7 @@ impl Entity { } /// Sets whether the entity is invisible and sends updated metadata. - #[expect(clippy::unused_async)] - #[allow(clippy::unused_async_trait_impl)] - pub async fn set_invisible(&self, invisible: bool) { + pub fn set_invisible(&self, invisible: bool) { if self.invisible.load(Ordering::Relaxed) != invisible { self.invisible.store(invisible, Relaxed); self.set_flag(Flag::Invisible, invisible); @@ -2780,9 +2773,7 @@ impl Entity { } /// Sets whether the entity is glowing and sends updated metadata. - #[expect(clippy::unused_async)] - #[allow(clippy::unused_async_trait_impl)] - pub async fn set_glowing(&self, glowing: bool) { + pub fn set_glowing(&self, glowing: bool) { if self.glowing.load(Ordering::Relaxed) != glowing { self.glowing.store(glowing, Ordering::Relaxed); self.set_flag(Flag::Glowing, glowing); @@ -2790,9 +2781,7 @@ impl Entity { } /// Sets whether the entity is on fire for visual and damage purposes. This is separate from `fire_ticks` which tracks the damage aspect of being on fire. - #[expect(clippy::unused_async)] - #[allow(clippy::unused_async_trait_impl)] - pub async fn set_on_fire(&self, on_fire: bool) { + pub fn set_on_fire(&self, on_fire: bool) { if self.has_visual_fire.load(Ordering::Relaxed) != on_fire { self.has_visual_fire.store(on_fire, Ordering::Relaxed); self.set_flag(Flag::OnFire, on_fire); @@ -3068,11 +3057,9 @@ impl Entity { (pose as u8).to_string(), ); if let Some(server) = self.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut pose_event).await; - }); - }); + server + .plugin_manager + .fire_blocking(&server, &mut pose_event); if pose_event.cancelled { return; } @@ -3105,7 +3092,7 @@ impl Entity { } /// Checks if the entity is invulnerable to the given damage type, considering both general invulnerability and specific immunities. - pub async fn is_invulnerable_to(&self, damage_type: &DamageType) -> bool { + pub fn is_invulnerable_to(&self, damage_type: &DamageType) -> bool { // Nothing is immune to void or kill if matches!( *damage_type, @@ -3120,12 +3107,18 @@ impl Entity { } // Specific type immunities - self.damage_immunities.lock().await.contains(damage_type) + self.damage_immunities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .contains(damage_type) } /// Sets if the entity is invulnerable to a specific damage type - pub async fn set_damage_immunity(&self, damage_type: DamageType, immune: bool) { - let mut immunities = self.damage_immunities.lock().await; + pub fn set_damage_immunity(&self, damage_type: DamageType, immune: bool) { + let mut immunities = self + .damage_immunities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if immune { if !immunities.contains(&damage_type) { immunities.push(damage_type); @@ -3141,7 +3134,7 @@ impl Entity { self.invulnerable.store(invulnerable, Relaxed); } - pub async fn check_block_collision(entity: &dyn EntityBase, server: &Server) { + pub fn check_block_collision(entity: &dyn EntityBase, server: &Server) { let aabb = entity.get_entity().bounding_box.load(); let blockpos = BlockPos::new( (aabb.min.x + 0.001).floor() as i32, @@ -3165,13 +3158,11 @@ impl Entity { if state.outline_shapes.is_empty() { world .block_registry - .on_entity_collision(block, &world, entity, &pos, state, server) - .await; + .on_entity_collision(block, &world, entity, &pos, state, server); let fluid = world.get_fluid(&pos); world .block_registry - .on_entity_collision_fluid(fluid, entity) - .await; + .on_entity_collision_fluid(fluid, entity); continue; } for outline in block_outlines { @@ -3179,13 +3170,11 @@ impl Entity { if outline_aabb.intersects(&aabb) { world .block_registry - .on_entity_collision(block, &world, entity, &pos, state, server) - .await; + .on_entity_collision(block, &world, entity, &pos, state, server); let fluid = world.get_fluid(&pos); world .block_registry - .on_entity_collision_fluid(fluid, entity) - .await; + .on_entity_collision_fluid(fluid, entity); break; } } @@ -3256,23 +3245,65 @@ impl Entity { !self.is_removed() } + #[must_use] + pub fn is_affected_by_blocks(&self) -> bool { + !self.is_removed() && !self.no_physics.load(Ordering::Relaxed) + } + + #[must_use] + pub fn is_in_wall(&self) -> bool { + if self.no_physics.load(Ordering::Relaxed) { + return false; + } + + let eye_pos = self.get_eye_pos(); + let half_width = (f64::from(self.entity_dimension.load().width) * 0.8) / 2.0; + let eye_bb = BoundingBox::new( + Vector3::new(eye_pos.x - half_width, eye_pos.y, eye_pos.z - half_width), + Vector3::new( + eye_pos.x + half_width, + eye_pos.y + 1.0e-6, + eye_pos.z + half_width, + ), + ); + let min = eye_bb.min_block_pos(); + let max = eye_bb.max_block_pos(); + let world = self.world.load(); + + for pos in BlockPos::iterate(min, max) { + let (block, state) = world.get_block_and_state(&pos); + if state.is_air() { + continue; + } + + if blocks_movement(state, block.id) && state.is_full_cube() { + return true; + } + } + + false + } + pub const LEASH_SNAP_DISTANCE: f64 = 12.0; pub const LEASH_ELASTIC_DISTANCE: f64 = 6.0; - pub async fn leash_to(&self, holder: Arc) { - let holder_entity = holder.get_entity(); - *self.leashed_to.lock().await = Some(holder.clone()); + pub fn leash_to(&self, holder: Arc) { + let holder_entity_id = holder.get_entity().entity_id; + *self + .leashed_to + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(holder); let je_packet = pumpkin_protocol::java::client::play::CSetEntityLink::new( self.entity_id, - holder_entity.entity_id, + holder_entity_id, true, ); let be_packet = pumpkin_protocol::bedrock::client::CSetActorLink { link: pumpkin_protocol::bedrock::client::common::ActorLink { ridden_unique_id: pumpkin_protocol::codec::var_long::VarLong(self.entity_id as i64), rider_unique_id: pumpkin_protocol::codec::var_long::VarLong( - holder_entity.entity_id as i64, + holder_entity_id as i64, ), link_type: 1, // Leash link immediate: true, @@ -3281,15 +3312,19 @@ impl Entity { }, }; - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( self.chunk_pos.load(), &je_packet, &be_packet, ); } - pub async fn unleash(&self) { - let old_holder = self.leashed_to.lock().await.take(); + pub fn unleash(&self) { + let old_holder = self + .leashed_to + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); if old_holder.is_none() { return; } @@ -3307,16 +3342,18 @@ impl Entity { }, }; - self.world.load().broadcast_to_chunk_editioned_sync( + self.world.load().broadcast_to_chunk_editioned( self.chunk_pos.load(), &je_packet, &be_packet, ); } - pub async fn tick_leash(&self) { + pub fn tick_leash(&self) { let holder = { - let guard = self.leashed_to.lock().await; + let Ok(guard) = self.leashed_to.try_lock() else { + return; + }; guard.clone() }; @@ -3325,7 +3362,7 @@ impl Entity { // Drop leash if entity or holder is removed or dead if !self.is_alive() || !holder_entity.is_alive() { - self.unleash().await; + self.unleash(); return; } @@ -3336,13 +3373,12 @@ impl Entity { if distance > Self::LEASH_SNAP_DISTANCE { // Too far: snap/break leash and drop lead item - self.unleash().await; + self.unleash(); let lead_item = pumpkin_data::item_stack::ItemStack::new(1, &pumpkin_data::item::Item::LEAD); self.world .load() - .drop_stack(&self.block_pos.load(), lead_item) - .await; + .drop_stack(&self.block_pos.load(), lead_item); } else if distance > Self::LEASH_ELASTIC_DISTANCE { // Elastic pull force towards leash holder let dir = (holder_pos - self_pos).normalize(); @@ -3354,18 +3390,30 @@ impl Entity { } } - pub async fn has_passengers(&self) -> bool { - !self.passengers.lock().await.is_empty() + pub fn has_passengers(&self) -> bool { + self.passengers.try_lock().is_ok_and(|p| !p.is_empty()) } - pub async fn has_vehicle(&self) -> bool { - let vehicle = self.vehicle.lock().await; - vehicle.is_some() + pub fn has_passenger(&self, id: i32) -> bool { + self.passengers.try_lock().is_ok_and(|p| { + p.iter() + .any(|passenger| passenger.get_entity().entity_id == id) + }) } - pub async fn is_leashed(&self) -> bool { - let leashed_to = self.leashed_to.lock().await; - leashed_to.is_some() + pub fn has_vehicle(&self) -> bool { + self.vehicle.try_lock().is_ok_and(|v| v.is_some()) + } + + pub fn get_vehicle(&self) -> Option> { + self.vehicle.try_lock().ok().and_then(|v| v.clone()) + } + + pub fn is_leashed(&self) -> bool { + self.leashed_to + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() } pub async fn add_passenger( @@ -3755,24 +3803,24 @@ impl Entity { } } - pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) { + pub fn check_out_of_world(&self, dyn_self: &dyn EntityBase) { if self.pos.load().y < f64::from(self.world.load().dimension.min_y) - 64.0 { - dyn_self.tick_in_void(dyn_self).await; + dyn_self.tick_in_void(dyn_self); } } - pub async fn reset_state(&self) { + pub fn reset_state(&self) { self.pose.store(EntityPose::Standing); self.fall_flying.store(false, Relaxed); self.extinguish(); - self.set_on_fire(false).await; + self.set_on_fire(false); } - pub async fn slow_movement(&self, state: &BlockState, multiplier: Vector3) { + pub fn slow_movement(&self, state: &BlockState, multiplier: Vector3) { match self.entity_type.id { v if v == EntityType::PLAYER.id => { if let Some(player_entity) = self.get_player() - && player_entity.is_flying().await + && player_entity.is_flying() { return; } @@ -3985,60 +4033,45 @@ impl Entity { } impl EntityBase for Entity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Recomputed during movement/block-collision handling in the same tick. - let was_in_powder_snow = self.is_in_powder_snow.load(Ordering::Relaxed); - self.was_in_powder_snow - .store(was_in_powder_snow, Ordering::Relaxed); - self.is_in_powder_snow.store(false, Ordering::Relaxed); + fn tick(&self, caller: &Arc, _server: &Server) { + // Recomputed during movement/block-collision handling in the same tick. + let was_in_powder_snow = self.is_in_powder_snow.load(Ordering::Relaxed); + self.was_in_powder_snow + .store(was_in_powder_snow, Ordering::Relaxed); + self.is_in_powder_snow.store(false, Ordering::Relaxed); - let block_pos = self.block_pos.load(); - if self.last_biome_update_pos.load() != block_pos { - let world = self.world.load(); - let biome = world.level.get_rough_biome(&block_pos); - self.current_biome.store(Arc::new(biome)); - self.last_biome_update_pos.store(block_pos); - } + self.update_last_pos(); + self.tick_portal(caller); + self.update_fluid_state(caller); + self.check_out_of_world(&**caller); + let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); - self.update_last_pos(); - 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) - let is_immune = - self.entity_type.fire_immune || self.fire_immune.load(Ordering::Relaxed); - if fire_ticks > 0 { - if is_immune { - self.fire_ticks.store(fire_ticks - 4, Ordering::Relaxed); - if self.fire_ticks.load(Ordering::Relaxed) < 0 { - self.extinguish(); - } - } else { - if fire_ticks % 20 == 0 { - (**caller).damage(&**caller, 1.0, DamageType::ON_FIRE).await; - } - - self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); + // Check for fire immunity (or if the specific entity is) + let is_immune = self.entity_type.fire_immune || self.fire_immune.load(Ordering::Relaxed); + if fire_ticks > 0 { + if is_immune { + self.fire_ticks.store(fire_ticks - 4, Ordering::Relaxed); + if self.fire_ticks.load(Ordering::Relaxed) < 0 { + self.extinguish(); + } + } else { + if fire_ticks % 20 == 0 { + caller.damage(&**caller, 1.0, DamageType::ON_FIRE); } - } - // Check if visual fire should be sent - let should_render_fire = self.fire_ticks.load(Ordering::Relaxed) > 0 && !is_immune; - self.set_on_fire(should_render_fire).await; - - let riding_cooldown = self.riding_cooldown.load(Ordering::Relaxed); - if riding_cooldown > 0 { - self.riding_cooldown - .store(riding_cooldown - 1, Ordering::Relaxed); + self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); } - }) + } + + // Check if visual fire should be sent + let should_render_fire = self.fire_ticks.load(Ordering::Relaxed) > 0 && !is_immune; + self.set_on_fire(should_render_fire); + + let riding_cooldown = self.riding_cooldown.load(Ordering::Relaxed); + if riding_cooldown > 0 { + self.riding_cooldown + .store(riding_cooldown - 1, Ordering::Relaxed); + } } fn teleport( diff --git a/crates/pumpkin/src/entity/passive/animal.rs b/crates/pumpkin/src/entity/passive/animal.rs index 181d1dcd6..2795fe643 100644 --- a/crates/pumpkin/src/entity/passive/animal.rs +++ b/crates/pumpkin/src/entity/passive/animal.rs @@ -101,7 +101,7 @@ pub trait Animal: Mob { } } - mob_entity.mob_interact(player, item_stack).await + mob_entity.mob_interact(player, item_stack) }) } } diff --git a/crates/pumpkin/src/entity/passive/armadillo.rs b/crates/pumpkin/src/entity/passive/armadillo.rs index acc9da605..3d58aee92 100644 --- a/crates/pumpkin/src/entity/passive/armadillo.rs +++ b/crates/pumpkin/src/entity/passive/armadillo.rs @@ -198,10 +198,10 @@ impl ArmadilloEntity { > ArmadilloState::Rolling.animation_duration() } - pub async fn can_stay_rolled_up(&self) -> bool { + pub fn can_stay_rolled_up(&self) -> bool { !self.is_panicking() && !self.mob_entity.living_entity.is_in_water() - && !self.get_entity().has_vehicle().await + && !self.get_entity().has_vehicle() } pub fn roll_up(&self) { @@ -243,14 +243,14 @@ impl ArmadilloEntity { Entity::new(world.clone(), pos, &EntityType::ITEM), ItemStack::new(1, &Item::ARMADILLO_SCUTE), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); world.play_sound(Sound::EntityArmadilloBrush, SoundCategory::Neutral, &pos); player.damage_held_item(16).await; true }) } - pub async fn is_scared_by(&self, living_entity: &dyn EntityBase) -> bool { + pub fn is_scared_by(&self, living_entity: &dyn EntityBase) -> bool { let entity = self.get_entity(); let pos = entity.pos.load(); let target_pos = living_entity.get_entity().pos.load(); @@ -272,7 +272,7 @@ impl ArmadilloEntity { if target_type == &EntityType::PLAYER { let target_ent = living_entity.get_entity(); - if target_ent.is_sprinting() || target_ent.has_vehicle().await { + if target_ent.is_sprinting() || target_ent.has_vehicle() { return true; } } @@ -341,107 +341,98 @@ impl Mob for ArmadilloEntity { } } - fn on_damage<'a>( - &'a self, - _damage_type: DamageType, - source: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.get_entity().is_alive() - && let Some(src) = source - && src.get_entity().entity_type != &EntityType::ITEM - { + fn on_damage(&self, _damage_type: DamageType, source: Option<&dyn EntityBase>) { + if self.get_entity().is_alive() + && let Some(src) = source + && src.get_entity().entity_type != &EntityType::ITEM + { + self.danger_detected_recently_ticks + .store(SCARE_CHECK_INTERVAL, Ordering::Relaxed); + if self.can_stay_rolled_up() { self.danger_detected_recently_ticks - .store(SCARE_CHECK_INTERVAL, Ordering::Relaxed); - if self.can_stay_rolled_up().await { - self.roll_up(); - } + .store(80, Ordering::Relaxed); } - }) + } } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ageable_ai_step(); + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.ageable_ai_step(); - self.in_state_ticks.fetch_add(1, Ordering::Relaxed); - let danger_ticks = self.danger_detected_recently_ticks.load(Ordering::Relaxed); - if danger_ticks > 0 { - self.danger_detected_recently_ticks - .store(danger_ticks - 1, Ordering::Relaxed); - } + self.in_state_ticks.fetch_add(1, Ordering::Relaxed); + let danger_ticks = self.danger_detected_recently_ticks.load(Ordering::Relaxed); + if danger_ticks > 0 { + self.danger_detected_recently_ticks + .store(danger_ticks - 1, Ordering::Relaxed); + } - let entity = self.get_entity(); - let world = entity.world.load(); + let entity = self.get_entity(); + let world = entity.world.load(); - if entity.is_alive() && !self.is_baby() { - let scute_time = self.scute_time.fetch_sub(1, Ordering::Relaxed) - 1; - if scute_time <= 0 { - let pos = entity.pos.load(); - let item_entity = Arc::new(ItemEntity::new( - Entity::new(world.clone(), pos, &EntityType::ITEM), - ItemStack::new(1, &Item::ARMADILLO_SCUTE), - )); - world.spawn_entity(item_entity).await; - world.play_sound( - Sound::EntityArmadilloScuteDrop, - SoundCategory::Neutral, - &pos, - ); - self.scute_time - .store(pick_next_scute_drop_time(), Ordering::Relaxed); - } - } - - let state = self.get_state(); - let ticks_in_state = self.in_state_ticks.load(Ordering::Relaxed); - - match state { - ArmadilloState::Rolling => { - if ticks_in_state > ArmadilloState::Rolling.animation_duration() { - self.switch_to_state(ArmadilloState::Scared); - } - } - ArmadilloState::Scared => { - if !self.can_stay_rolled_up().await { - self.roll_out(); - } else if ticks_in_state > ArmadilloState::Scared.animation_duration() - && self.danger_detected_recently_ticks.load(Ordering::Relaxed) == 0 - { - self.switch_to_state(ArmadilloState::Unrolling); - } - } - ArmadilloState::Unrolling => { - if ticks_in_state > ArmadilloState::Unrolling.animation_duration() { - self.roll_out(); - } - } - ArmadilloState::Idle => {} - } - }) - } - - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::armadillo::BABY_ID, - true, - )], - None, + if entity.is_alive() && !self.is_baby() { + let scute_time = self.scute_time.fetch_sub(1, Ordering::Relaxed) - 1; + if scute_time <= 0 { + let pos = entity.pos.load(); + let item_entity = Arc::new(ItemEntity::new( + Entity::new(world.clone(), pos, &EntityType::ITEM), + ItemStack::new(1, &Item::ARMADILLO_SCUTE), + )); + world.spawn_entity_non_save(item_entity as Arc); + world.play_sound( + Sound::EntityArmadilloScuteDrop, + SoundCategory::Neutral, + &pos, ); + self.scute_time + .store(pick_next_scute_drop_time(), Ordering::Relaxed); } + } + + let state = self.get_state(); + let ticks_in_state = self.in_state_ticks.load(Ordering::Relaxed); + + match state { + ArmadilloState::Rolling => { + if ticks_in_state > ArmadilloState::Rolling.animation_duration() { + self.switch_to_state(ArmadilloState::Scared); + } + } + ArmadilloState::Scared => { + if !self.can_stay_rolled_up() { + self.roll_out(); + } else if ticks_in_state > ArmadilloState::Scared.animation_duration() + && self.danger_detected_recently_ticks.load(Ordering::Relaxed) == 0 + { + self.switch_to_state(ArmadilloState::Unrolling); + } + } + ArmadilloState::Unrolling => { + if ticks_in_state > ArmadilloState::Unrolling.animation_duration() { + self.roll_out(); + } + } + ArmadilloState::Idle => {} + } + } + + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, - VarInt(self.get_state().id()), + pumpkin_data::tracked_data::armadillo::BABY_ID, + true, )], None, ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, + VarInt(self.get_state().id()), + )], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/cat.rs b/crates/pumpkin/src/entity/passive/cat.rs index f5b1034cd..17247d94c 100644 --- a/crates/pumpkin/src/entity/passive/cat.rs +++ b/crates/pumpkin/src/entity/passive/cat.rs @@ -371,69 +371,67 @@ impl Mob for CatEntity { self.variant.store(variant, Ordering::Relaxed); } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, - self.get_tame_flags(), + pumpkin_data::tracked_data::cat::BABY_ID, + true, )], None, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::OWNER_UUID, - self.get_owner(), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_VARIANT, - VarInt(self.variant.load(Ordering::Relaxed) as i32), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::IS_LYING, - self.is_lying.load(Ordering::Relaxed), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, - self.relax_state_one.load(Ordering::Relaxed), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, - VarInt(self.collar_color.load(Ordering::Relaxed) as i32), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::SOUND_VARIANT, - VarInt(self.sound_variant.load(Ordering::Relaxed) as i32), - )], - None, - ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, + self.get_tame_flags(), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::OWNER_UUID, + self.get_owner(), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::CAT_VARIANT, + VarInt(self.variant.load(Ordering::Relaxed) as i32), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::IS_LYING, + self.is_lying.load(Ordering::Relaxed), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, + self.relax_state_one.load(Ordering::Relaxed), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, + VarInt(self.collar_color.load(Ordering::Relaxed) as i32), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::cat::SOUND_VARIANT, + VarInt(self.sound_variant.load(Ordering::Relaxed) as i32), + )], + None, + ); } fn mob_interact<'a>( @@ -467,7 +465,7 @@ impl Mob for CatEntity { return true; } - let parent_interaction = self.mob_entity.mob_interact(player, item_stack).await; + let parent_interaction = self.mob_entity.mob_interact(player, item_stack); if !parent_interaction { self.set_sitting(!self.is_sitting()); return true; @@ -498,7 +496,7 @@ impl Mob for CatEntity { return true; } - self.mob_entity.mob_interact(player, item_stack).await + self.mob_entity.mob_interact(player, item_stack) }) } } diff --git a/crates/pumpkin/src/entity/passive/chicken.rs b/crates/pumpkin/src/entity/passive/chicken.rs index 8d0844bad..cd3f91981 100644 --- a/crates/pumpkin/src/entity/passive/chicken.rs +++ b/crates/pumpkin/src/entity/passive/chicken.rs @@ -150,61 +150,60 @@ impl Mob for ChickenEntity { self.variant.store(variant, Ordering::Relaxed); } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::chicken::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::chicken::VARIANT, - VarInt(self.variant.load(Ordering::Relaxed) as i32), + pumpkin_data::tracked_data::chicken::BABY_ID, + true, )], None, ); - }) + } + entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::chicken::VARIANT, + VarInt(self.variant.load(Ordering::Relaxed) as i32), + )], + None, + ); } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async { - if self.mob_entity.living_entity.dead.load(Relaxed) { - return; - } - let entity = &self.mob_entity.living_entity.entity; - let current_velocity = entity.velocity.load(); - let on_ground = entity.on_ground.load(Ordering::Relaxed); + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + if self.mob_entity.living_entity.dead.load(Relaxed) { + return; + } + let entity = &self.mob_entity.living_entity.entity; + let current_velocity = entity.velocity.load(); + let on_ground = entity.on_ground.load(Ordering::Relaxed); - // TODO: move velocity logic to physics tick when implemented - if (!on_ground) && current_velocity.y < 0.0 { - entity.set_velocity(current_velocity.multiply(1.0, 0.6, 1.0)); + // TODO: move velocity logic to physics tick when implemented + if (!on_ground) && current_velocity.y < 0.0 { + entity.set_velocity(current_velocity.multiply(1.0, 0.6, 1.0)); + } + if self.egg_lay_time.fetch_sub(1, Ordering::Relaxed) <= 1 { + let next_time = rand::rng().random_range(6000..12000); + let world = entity.world.load_full(); + let pos = entity.block_pos.load(); + let entity_id = entity.entity_id; + let mut drop_event = + crate::plugin::api::events::entity::entity_drop_item::EntityDropItemEvent::new( + entity_id, + "minecraft:egg".to_string(), + 1, + ); + if let Some(server) = world.server.upgrade() { + server + .plugin_manager + .fire_blocking(&server, &mut drop_event); } - if self.egg_lay_time.fetch_sub(1, Ordering::Relaxed) <= 1 { - let next_time = rand::rng().random_range(6000..12000); - let world = entity.world.load_full(); - let pos = entity.block_pos.load(); - let mut drop_event = - crate::plugin::api::events::entity::entity_drop_item::EntityDropItemEvent::new( - entity.entity_id, - "minecraft:egg".to_string(), - 1, - ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut drop_event).await; - } - if !drop_event.cancelled { - world.drop_stack(&pos, ItemStack::new(1, &Item::EGG)).await; - } - self.egg_lay_time.store(next_time, Ordering::Relaxed); + if !drop_event.cancelled { + world.drop_stack(&pos, ItemStack::new(1, &Item::EGG)); } - }) + self.egg_lay_time.store(next_time, Ordering::Relaxed); + } } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/copper_golem.rs b/crates/pumpkin/src/entity/passive/copper_golem.rs index a8d785fd1..0f06230ea 100644 --- a/crates/pumpkin/src/entity/passive/copper_golem.rs +++ b/crates/pumpkin/src/entity/passive/copper_golem.rs @@ -242,23 +242,21 @@ impl Mob for CopperGolemEntity { }) } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, - VarInt(self.get_weather_state().id()), - ), - Metadata::new( - pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, - VarInt(self.get_state().id()), - ), - ], - None, - ); - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + entity.send_meta_data( + &[ + Metadata::new( + pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, + VarInt(self.get_weather_state().id()), + ), + Metadata::new( + pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, + VarInt(self.get_state().id()), + ), + ], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/fox.rs b/crates/pumpkin/src/entity/passive/fox.rs index 094471cc5..50a6e45cc 100644 --- a/crates/pumpkin/src/entity/passive/fox.rs +++ b/crates/pumpkin/src/entity/passive/fox.rs @@ -282,40 +282,36 @@ impl Mob for FoxEntity { self.set_variant(FoxVariant::from_name(name)); } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ageable_ai_step(); - }) + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.ageable_ai_step(); } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::fox::TYPE_ID, - VarInt(self.get_variant().id()), + pumpkin_data::tracked_data::fox::BABY_ID, + true, )], None, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::FLAGS_ID, - self.flags.load(Ordering::Relaxed), - )], - None, - ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::fox::TYPE_ID, + VarInt(self.get_variant().id()), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::fox::FLAGS_ID, + self.flags.load(Ordering::Relaxed), + )], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/frog.rs b/crates/pumpkin/src/entity/passive/frog.rs index 3ddcaa9af..c4dd9cd34 100644 --- a/crates/pumpkin/src/entity/passive/frog.rs +++ b/crates/pumpkin/src/entity/passive/frog.rs @@ -176,33 +176,29 @@ impl Mob for FrogEntity { self.set_variant(FrogVariant::from_name(name)); } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ageable_ai_step(); - }) + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.ageable_ai_step(); } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::frog::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::frog::VARIANT, - VarInt(self.get_variant().id()), + pumpkin_data::tracked_data::frog::BABY_ID, + true, )], None, ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::frog::VARIANT, + VarInt(self.get_variant().id()), + )], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/happy_ghast.rs b/crates/pumpkin/src/entity/passive/happy_ghast.rs index 969ac37bc..02920f1cc 100644 --- a/crates/pumpkin/src/entity/passive/happy_ghast.rs +++ b/crates/pumpkin/src/entity/passive/happy_ghast.rs @@ -157,70 +157,66 @@ impl Mob for HappyGhastEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ageable_ai_step(); + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.ageable_ai_step(); - let leash_time = self.leash_holder_time.load(Ordering::Relaxed); - if leash_time > 0 { - self.leash_holder_time.fetch_sub(1, Ordering::Relaxed); - } - self.set_leash_holder(leash_time > 0); + let leash_time = self.leash_holder_time.load(Ordering::Relaxed); + if leash_time > 0 { + self.leash_holder_time.fetch_sub(1, Ordering::Relaxed); + } + self.set_leash_holder(leash_time > 0); - let still_timeout = self.server_still_timeout.load(Ordering::Relaxed); - if still_timeout > 0 { - let entity = self.get_entity(); - if entity.age.load(Ordering::Relaxed) > 60 { - self.server_still_timeout.fetch_sub(1, Ordering::Relaxed); - } - self.sync_stay_still_flag(); - } - - // Continuous healing + let still_timeout = self.server_still_timeout.load(Ordering::Relaxed); + if still_timeout > 0 { let entity = self.get_entity(); - if entity.is_alive() { - let living = &self.mob_entity.living_entity; - let current_health = living.health.load(); - let max_health = living.get_max_health(); - if current_health < max_health { - let world = entity.world.load(); - let ticks = world.level_time.lock().await.world_age; - let heal_interval = 600; - if ticks % heal_interval == 0 { - living.set_health(current_health + 1.0); - } + if entity.age.load(Ordering::Relaxed) > 60 { + self.server_still_timeout.fetch_sub(1, Ordering::Relaxed); + } + self.sync_stay_still_flag(); + } + + // Continuous healing + let entity = self.get_entity(); + if entity.is_alive() { + let living = &self.mob_entity.living_entity; + let current_health = living.health.load(); + let max_health = living.get_max_health(); + if current_health < max_health { + let world = entity.world.load(); + let ticks = world.get_world_age(); + let heal_interval = 600; + if ticks % heal_interval == 0 { + living.set_health(current_health + 1.0); } } - }) + } } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::happy_ghast::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, - self.is_leash_holder.load(Ordering::Relaxed), - ), - Metadata::new( - pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, - self.stays_still.load(Ordering::Relaxed), - ), - ], + &[Metadata::new( + pumpkin_data::tracked_data::happy_ghast::BABY_ID, + true, + )], None, ); - }) + } + entity.send_meta_data( + &[ + Metadata::new( + pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, + self.is_leash_holder.load(Ordering::Relaxed), + ), + Metadata::new( + pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, + self.stays_still.load(Ordering::Relaxed), + ), + ], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/iron_golem.rs b/crates/pumpkin/src/entity/passive/iron_golem.rs index 91abb96a2..699040a01 100644 --- a/crates/pumpkin/src/entity/passive/iron_golem.rs +++ b/crates/pumpkin/src/entity/passive/iron_golem.rs @@ -140,32 +140,28 @@ impl Mob for IronGolemEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let attack_tick = self.attack_animation_tick.load(Ordering::Relaxed); - if attack_tick > 0 { - self.attack_animation_tick.fetch_sub(1, Ordering::Relaxed); - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let attack_tick = self.attack_animation_tick.load(Ordering::Relaxed); + if attack_tick > 0 { + self.attack_animation_tick.fetch_sub(1, Ordering::Relaxed); + } - let flower_tick = self.offer_flower_tick.load(Ordering::Relaxed); - if flower_tick > 0 { - self.offer_flower_tick.fetch_sub(1, Ordering::Relaxed); - } - }) + let flower_tick = self.offer_flower_tick.load(Ordering::Relaxed); + if flower_tick > 0 { + self.offer_flower_tick.fetch_sub(1, Ordering::Relaxed); + } } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let flag: u8 = u8::from(self.is_player_created()); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::iron_golem::FLAGS_ID, - flag, - )], - None, - ); - }) + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let flag: u8 = u8::from(self.is_player_created()); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::iron_golem::FLAGS_ID, + flag, + )], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/llama.rs b/crates/pumpkin/src/entity/passive/llama.rs index 857583f3d..bf4ec2764 100644 --- a/crates/pumpkin/src/entity/passive/llama.rs +++ b/crates/pumpkin/src/entity/passive/llama.rs @@ -5,7 +5,7 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_attack::RangedAttackGoal, revenge::RevengeGoal, @@ -70,9 +70,9 @@ impl LlamaEntity { mob_arc } - pub async fn spit(&self, target: &Arc) { + pub fn spit(&self, target: &Arc) { let entity = self.get_entity(); - let world = entity.world.load(); + let world = entity.world.load_full(); let spit_entity = Entity::new(world.clone(), entity.pos.load(), &EntityType::LLAMA_SPIT); let spit = LlamaSpitEntity::new_shot(spit_entity, entity); @@ -95,7 +95,7 @@ impl LlamaEntity { } let spit_arc: Arc = Arc::new(spit); - world.spawn_entity(spit_arc).await; + world.spawn_entity(spit_arc); } } @@ -106,13 +106,7 @@ impl Mob for LlamaEntity { } impl RangedAttackMob for LlamaEntity { - fn perform_ranged_attack<'a>( - &'a self, - target: &'a Arc, - _power: f32, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.spit(target).await; - }) + fn perform_ranged_attack(&self, target: &Arc, _power: f32) { + self.spit(target); } } diff --git a/crates/pumpkin/src/entity/passive/nautilus.rs b/crates/pumpkin/src/entity/passive/nautilus.rs index 2ba254dea..890d916a9 100644 --- a/crates/pumpkin/src/entity/passive/nautilus.rs +++ b/crates/pumpkin/src/entity/passive/nautilus.rs @@ -262,79 +262,77 @@ impl Mob for NautilusEntity { &self.mob_entity } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::nautilus::DASH, - self.is_dashing(), - )], - None, - ); - }) + fn mob_init_data_tracker(&self) { + self.mob_entity.living_entity.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::nautilus::DASH, + self.is_dashing(), + )], + None, + ); } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.mob_entity.living_entity.entity; + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + let entity = &self.mob_entity.living_entity.entity; - let passengers = entity.passengers.lock().await; - if let Some(passenger) = passengers.first() - && let Some(player) = passenger.cast_any().downcast_ref::() - { + let Ok(passengers) = entity.passengers.try_lock() else { + return; + }; + if let Some(passenger) = passengers.first() + && let Some(player) = passenger.cast_any().downcast_ref::() + { + let world = entity.world.load(); + let game_time = world.get_world_age(); + if game_time % 40 == 0 { + let player_arc = world.get_player_by_uuid(player.gameprofile.id); + if let Some(p) = player_arc { + p.add_effect(Effect { + effect_type: &StatusEffect::BREATH_OF_THE_NAUTILUS, + duration: 60, + amplifier: 0, + ambient: true, + show_particles: true, + show_icon: true, + blend: true, + }); + } + } + } + + if self.is_dashing() && self.dash_cooldown.load(Ordering::Relaxed) < 35 { + self.set_dashing(false); + } + + let cooldown = self.dash_cooldown.load(Ordering::Relaxed); + if cooldown > 0 { + let next = cooldown - 1; + self.dash_cooldown.store(next, Ordering::Relaxed); + if next == 0 { let world = entity.world.load(); - let game_time = world.level_time.lock().await.world_age; - if game_time % 40 == 0 { - player - .living_entity - .add_effect(Effect { - effect_type: &StatusEffect::BREATH_OF_THE_NAUTILUS, - duration: 60, - amplifier: 0, - ambient: true, - show_particles: true, - show_icon: true, - blend: true, - }) - .await; - } + world.play_sound( + self.get_dash_ready_sound(), + SoundCategory::Neutral, + &entity.pos.load(), + ); } + } - if self.is_dashing() && self.dash_cooldown.load(Ordering::Relaxed) < 35 { - self.set_dashing(false); + if entity.touching_water.load(Ordering::Relaxed) { + let velo = entity.velocity.load(); + let speed = velo.length(); + let prob = (speed * 2.0).clamp(0.15, 1.0); + if rand::random::() < prob { + let world = entity.world.load(); + let pos = entity.pos.load(); + world.spawn_particle( + pos + Vector3::new(0.0, 0.25, 0.0), + Vector3::new(0.4, 0.4, 0.4), + 0.5, + 2, + Particle::Bubble, + ); } - - let cooldown = self.dash_cooldown.load(Ordering::Relaxed); - if cooldown > 0 { - let next = cooldown - 1; - self.dash_cooldown.store(next, Ordering::Relaxed); - if next == 0 { - let world = entity.world.load(); - world.play_sound( - self.get_dash_ready_sound(), - SoundCategory::Neutral, - &entity.pos.load(), - ); - } - } - - if entity.touching_water.load(Ordering::Relaxed) { - let velo = entity.velocity.load(); - let speed = velo.length(); - let prob = (speed * 2.0).clamp(0.15, 1.0); - if rand::random::() < prob { - let world = entity.world.load(); - let pos = entity.pos.load(); - world.spawn_particle( - pos + Vector3::new(0.0, 0.25, 0.0), - Vector3::new(0.4, 0.4, 0.4), - 0.5, - 2, - Particle::Bubble, - ); - } - } - }) + } } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/ocelot.rs b/crates/pumpkin/src/entity/passive/ocelot.rs index 84122e55a..f301fa00a 100644 --- a/crates/pumpkin/src/entity/passive/ocelot.rs +++ b/crates/pumpkin/src/entity/passive/ocelot.rs @@ -151,27 +151,25 @@ impl Mob for OcelotEntity { &self.mob_entity } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::ocelot::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::ocelot::TRUSTING, - self.is_trusting.load(Ordering::Relaxed), + pumpkin_data::tracked_data::ocelot::BABY_ID, + true, )], None, ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::ocelot::TRUSTING, + self.is_trusting.load(Ordering::Relaxed), + )], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/parrot.rs b/crates/pumpkin/src/entity/passive/parrot.rs index 28a0e5fb9..5649256af 100644 --- a/crates/pumpkin/src/entity/passive/parrot.rs +++ b/crates/pumpkin/src/entity/passive/parrot.rs @@ -58,7 +58,7 @@ impl ParrotEntity { /// Feeds the parrot a cookie: it is poisoned and then killed, as in vanilla /// `Parrot.mobInteract`. - async fn eat_cookie(&self, player: &Arc, item_stack: &mut ItemStack) { + fn eat_cookie(&self, player: &Arc, item_stack: &mut ItemStack) { item_stack.decrement_unless_creative(player.gamemode.load(), 1); self.mob_entity @@ -71,8 +71,7 @@ impl ParrotEntity { show_particles: true, show_icon: true, blend: true, - }) - .await; + }); // Vanilla guards this call with `player.isCreative() || !this.isInvulnerable()`, // but `hurt` re-checks invulnerability itself and `player_attack` doesn't bypass @@ -84,8 +83,7 @@ impl ParrotEntity { None, Some(player.as_ref()), Some(player.as_ref()), - ) - .await; + ); } } @@ -107,10 +105,10 @@ impl Mob for ParrotEntity { .get_item() .has_tag(&tag::Item::MINECRAFT_PARROT_POISONOUS_FOOD) { - return self.mob_entity.mob_interact(player, item_stack).await; + return self.mob_entity.mob_interact(player, item_stack); } - self.eat_cookie(player, item_stack).await; + self.eat_cookie(player, item_stack); true }) } diff --git a/crates/pumpkin/src/entity/passive/sheep.rs b/crates/pumpkin/src/entity/passive/sheep.rs index bdfbde89a..c41b694ec 100644 --- a/crates/pumpkin/src/entity/passive/sheep.rs +++ b/crates/pumpkin/src/entity/passive/sheep.rs @@ -156,10 +156,8 @@ impl Mob for SheepEntity { &self.mob_entity } - fn on_eating_grass(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async { - self.set_sheared(false); - }) + fn on_eating_grass(&self) { + self.set_sheared(false); } fn get_sheep(&self) -> Option<&SheepEntity> { diff --git a/crates/pumpkin/src/entity/passive/sniffer.rs b/crates/pumpkin/src/entity/passive/sniffer.rs index 3fa6bc24d..5f9357823 100644 --- a/crates/pumpkin/src/entity/passive/sniffer.rs +++ b/crates/pumpkin/src/entity/passive/sniffer.rs @@ -263,10 +263,10 @@ impl SnifferEntity { explored.insert(0, pos); } - pub async fn drop_seed(&self) { + pub fn drop_seed(&self) { let entity = self.get_entity(); let world = entity.world.load(); - let current_tick = world.level_time.lock().await.world_age as i32; + let current_tick = world.get_world_age() as i32; if self.drop_seed_at_tick.load(Ordering::Relaxed) == current_tick { let head_pos = self.get_head_position(); @@ -279,7 +279,7 @@ impl SnifferEntity { let item_entity = Entity::new(world.clone(), head_pos, &EntityType::ITEM); let item_arc = Arc::new(ItemEntity::new(item_entity, item_stack)); - world.spawn_entity(item_arc).await; + world.spawn_entity_non_save(item_arc as Arc); world.play_sound( Sound::EntitySnifferDropSeed, @@ -289,7 +289,7 @@ impl SnifferEntity { } } - pub async fn spawn_child_from_breeding(&self, partner: &dyn EntityBase) { + pub fn spawn_child_from_breeding(&self, partner: &dyn EntityBase) { let entity = self.get_entity(); let world = entity.world.load(); let pos = entity.pos.load(); @@ -297,7 +297,7 @@ impl SnifferEntity { let item_stack = ItemStack::new(1, &Item::SNIFFER_EGG); let egg_entity = Entity::new(world.clone(), pos, &EntityType::ITEM); let egg_arc = Arc::new(ItemEntity::new(egg_entity, item_stack)); - world.spawn_entity(egg_arc).await; + world.spawn_entity_non_save(egg_arc as Arc); world.play_sound(Sound::BlockSnifferEggPlop, SoundCategory::Neutral, &pos); @@ -361,58 +361,54 @@ impl Mob for SnifferEntity { &self.mob_entity } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.ageable_ai_step(); - let state = self.get_state(); - match state { - SnifferState::Searching => { - let entity = self.get_entity(); - let world = entity.world.load(); - let ticks = world.level_time.lock().await.world_age; - if ticks % 20 == 0 { - world.play_sound( - Sound::EntitySnifferSearching, - SoundCategory::Neutral, - &entity.pos.load(), - ); - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + self.ageable_ai_step(); + let state = self.get_state(); + match state { + SnifferState::Searching => { + let entity = self.get_entity(); + let world = entity.world.load(); + let ticks = world.get_world_age(); + if ticks % 20 == 0 { + world.play_sound( + Sound::EntitySnifferSearching, + SoundCategory::Neutral, + &entity.pos.load(), + ); } - SnifferState::Digging => { - self.drop_seed().await; - } - _ => {} } - }) + SnifferState::Digging => { + self.drop_seed(); + } + _ => {} + } } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::sniffer::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::sniffer::STATE, - VarInt(self.get_state().id()), - ), - Metadata::new( - pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, - VarInt(self.drop_seed_at_tick.load(Ordering::Relaxed)), - ), - ], + &[Metadata::new( + pumpkin_data::tracked_data::sniffer::BABY_ID, + true, + )], None, ); - }) + } + entity.send_meta_data( + &[ + Metadata::new( + pumpkin_data::tracked_data::sniffer::STATE, + VarInt(self.get_state().id()), + ), + Metadata::new( + pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, + VarInt(self.drop_seed_at_tick.load(Ordering::Relaxed)), + ), + ], + None, + ); } fn mob_interact<'a>( diff --git a/crates/pumpkin/src/entity/passive/snow_golem.rs b/crates/pumpkin/src/entity/passive/snow_golem.rs index 7fec37b27..9bfbe2cce 100644 --- a/crates/pumpkin/src/entity/passive/snow_golem.rs +++ b/crates/pumpkin/src/entity/passive/snow_golem.rs @@ -5,7 +5,7 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_attack::RangedAttackGoal, @@ -65,9 +65,9 @@ impl SnowGolemEntity { mob_arc } - pub async fn throw_snowball(&self, target: &Arc) { + pub fn throw_snowball(&self, target: &Arc) { let entity = self.get_entity(); - let world = entity.world.load(); + let world = entity.world.load_full(); let snowball_entity = Entity::new(world.clone(), entity.pos.load(), &EntityType::SNOWBALL); let snowball = SnowballEntity::new_shot(snowball_entity, entity); @@ -90,7 +90,7 @@ impl SnowGolemEntity { } let snowball_arc: Arc = Arc::new(snowball); - world.spawn_entity(snowball_arc).await; + world.spawn_entity(snowball_arc); } } @@ -101,13 +101,7 @@ impl Mob for SnowGolemEntity { } impl RangedAttackMob for SnowGolemEntity { - fn perform_ranged_attack<'a>( - &'a self, - target: &'a Arc, - _power: f32, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.throw_snowball(target).await; - }) + fn perform_ranged_attack(&self, target: &Arc, _power: f32) { + self.throw_snowball(target); } } diff --git a/crates/pumpkin/src/entity/passive/trader_llama.rs b/crates/pumpkin/src/entity/passive/trader_llama.rs index 33d2c1d5f..fcbfd449f 100644 --- a/crates/pumpkin/src/entity/passive/trader_llama.rs +++ b/crates/pumpkin/src/entity/passive/trader_llama.rs @@ -5,7 +5,7 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_attack::RangedAttackGoal, revenge::RevengeGoal, @@ -67,9 +67,9 @@ impl TraderLlamaEntity { mob_arc } - pub async fn spit(&self, target: &Arc) { + pub fn spit(&self, target: &Arc) { let entity = self.get_entity(); - let world = entity.world.load(); + let world = entity.world.load_full(); let spit_entity = Entity::new(world.clone(), entity.pos.load(), &EntityType::LLAMA_SPIT); let spit = LlamaSpitEntity::new_shot(spit_entity, entity); @@ -92,7 +92,7 @@ impl TraderLlamaEntity { } let spit_arc: Arc = Arc::new(spit); - world.spawn_entity(spit_arc).await; + world.spawn_entity(spit_arc); } } @@ -103,13 +103,7 @@ impl Mob for TraderLlamaEntity { } impl RangedAttackMob for TraderLlamaEntity { - fn perform_ranged_attack<'a>( - &'a self, - target: &'a Arc, - _power: f32, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.spit(target).await; - }) + fn perform_ranged_attack(&self, target: &Arc, _power: f32) { + self.spit(target); } } diff --git a/crates/pumpkin/src/entity/passive/villager/mod.rs b/crates/pumpkin/src/entity/passive/villager/mod.rs index ee0d6eb31..2de0c88a1 100644 --- a/crates/pumpkin/src/entity/passive/villager/mod.rs +++ b/crates/pumpkin/src/entity/passive/villager/mod.rs @@ -21,6 +21,7 @@ use pumpkin_inventory::screen_handler::{ BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_nbt::compound::NbtCompound; +use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::bedrock::{ client::set_actor_data::{MetadataValue, SyncedActorDataList, entity_data_key}, server::actor_event::ActorEventID, @@ -57,12 +58,10 @@ pub use data::{ get_food_points, }; -pub(crate) async fn trigger_trade_advancement(player: &Player) { - player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::TradedWithVillager, - ) - .await; +pub(crate) fn trigger_trade_advancement(player: &Player) { + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::TradedWithVillager, + ); } fn enchanted_book_offer_items( @@ -257,7 +256,7 @@ pub(crate) fn apply_potion(stack: &mut ItemStack, potion_name: &str) { pub struct VillagerEntity { pub mob_entity: MobEntity, - pub villager_data: Mutex, + pub villager_data: std::sync::Mutex, pub food_level: AtomicI32, pub xp: AtomicI32, pub last_restock_time: AtomicI64, @@ -265,6 +264,7 @@ pub struct VillagerEntity { pub last_worked_at_poi: AtomicI64, pub restocks_today: AtomicI32, pub last_gossip_decay_time: AtomicI64, + pub last_gossip_share_time: AtomicI64, pub gossips: Mutex>>, pub inventory: Arc>>>>, pub merchant_inventory: Arc, @@ -273,7 +273,7 @@ pub struct VillagerEntity { pub unhappy_counter: AtomicI32, pub trade_sound_cooldown: AtomicI32, pub increase_profession_level_on_update: AtomicBool, - pub last_traded_player: Mutex>, + pub last_traded_player: std::sync::Mutex>, pub trading_player: std::sync::Mutex>, pub is_trading: AtomicBool, pub job_site: std::sync::Mutex>, @@ -329,7 +329,7 @@ impl VillagerEntity { let villager = Self { mob_entity, - villager_data: Mutex::new(villager_data), + villager_data: std::sync::Mutex::new(villager_data), food_level: AtomicI32::new(0), xp: AtomicI32::new(0), last_restock_time: AtomicI64::new(0), @@ -337,6 +337,7 @@ impl VillagerEntity { last_worked_at_poi: AtomicI64::new(0), restocks_today: AtomicI32::new(0), last_gossip_decay_time: AtomicI64::new(0), + last_gossip_share_time: AtomicI64::new(0), gossips: Mutex::new(HashMap::new()), inventory, merchant_inventory: Arc::new(SimpleInventory::new(3)), @@ -345,7 +346,7 @@ impl VillagerEntity { unhappy_counter: AtomicI32::new(0), trade_sound_cooldown: AtomicI32::new(0), increase_profession_level_on_update: AtomicBool::new(false), - last_traded_player: Mutex::new(None), + last_traded_player: std::sync::Mutex::new(None), trading_player: std::sync::Mutex::new(None), is_trading: AtomicBool::new(false), job_site: std::sync::Mutex::new(None), @@ -499,7 +500,10 @@ impl VillagerEntity { pub async fn set_villager_data(&self, data: VillagerData) { let old_profession = { - let mut villager_data = self.villager_data.lock().await; + let mut villager_data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let old_profession = villager_data.profession; *villager_data = data; old_profession @@ -634,7 +638,11 @@ impl VillagerEntity { use rand::{RngExt, SeedableRng, rngs::StdRng}; use std::borrow::Cow; - let villager_type = self.villager_data.lock().await.type_enum(); + let villager_type = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .type_enum(); let mut offers = self.offers.lock().await; if let Some(trade_set) = profession.trade_set(level) { @@ -737,7 +745,6 @@ impl VillagerEntity { let hero_amplifier = player .living_entity .get_effect(&StatusEffect::HERO_OF_THE_VILLAGE) - .await .map(|effect| i32::from(effect.amplifier)); let mut offers = self.offers.lock().await; @@ -798,7 +805,10 @@ impl VillagerEntity { }; let current_xp = self.xp.fetch_add(xp_gain, Ordering::Relaxed) + xp_gain; - let villager_data = *self.villager_data.lock().await; + let villager_data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let bedrock_metadata = Self::bedrock_metadata(villager_data, current_xp); self.get_entity().send_meta_data( &[Metadata::new( @@ -831,14 +841,17 @@ impl VillagerEntity { self.get_entity() .play_sound(pumpkin_data::sound::Sound::EntityVillagerYes); self.trade_sound_cooldown.store(20, Ordering::Relaxed); - *self.last_traded_player.lock().await = Some(player_uuid); + *self + .last_traded_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(player_uuid); if reward_exp { let position = self.get_entity().pos.load().add_raw(0.0, 0.5, 0.0); - ExperienceOrbEntity::spawn(world, position, reward_xp).await; + ExperienceOrbEntity::spawn(world, position, reward_xp); } if let Some(player) = world.get_player_by_uuid(player_uuid) { - trigger_trade_advancement(&player).await; + trigger_trade_advancement(&player); } } @@ -855,7 +868,10 @@ impl VillagerEntity { return; }; let offers = self.offers.lock().await.clone(); - let villager_data = *self.villager_data.lock().await; + let villager_data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let screen = player.current_screen_handler.lock().await.clone(); let mut screen = screen.lock().await; @@ -918,7 +934,11 @@ impl VillagerEntity { return; } - let profession = self.villager_data.lock().await.profession_enum(); + let profession = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .profession_enum(); if let Some(sound) = profession.work_sound() { self.get_entity().play_sound(sound); } @@ -971,7 +991,10 @@ impl VillagerEntity { #[expect(clippy::too_many_lines)] async fn update_job_site(&self, world: &crate::world::World) { - let data = *self.villager_data.lock().await; + let data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let profession = data.profession_enum(); let is_adult = self.get_entity().age.load(Ordering::Relaxed) >= 0; @@ -980,7 +1003,7 @@ impl VillagerEntity { world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .release(site, self.get_entity().entity_uuid); *self .job_site @@ -1006,7 +1029,7 @@ impl VillagerEntity { let valid = world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .claim(current_site, block, owner.clone(), expected) .is_some(); @@ -1020,7 +1043,11 @@ impl VillagerEntity { && data.level.0 <= 1 && profession != VillagerProfession::None { - let r#type = self.villager_data.lock().await.type_enum(); + let r#type = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .type_enum(); self.set_villager_data(VillagerData::new(r#type, VillagerProfession::None, 1)) .await; } @@ -1028,7 +1055,11 @@ impl VillagerEntity { } if self.get_job_site().is_none() { - let profession = self.villager_data.lock().await.profession_enum(); + let profession = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .profession_enum(); let expected = (profession != VillagerProfession::None).then_some(profession); let pos = self.get_entity().block_pos.load(); let start = BlockPos::new(pos.0.x - 10, pos.0.y - 4, pos.0.z - 10); @@ -1038,9 +1069,13 @@ impl VillagerEntity { let indexed_sites = world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .available_job_sites(pos, 48, expected); - let saved_sites = world.portal_poi.lock().await.get_in_square(pos, 48, None); + let saved_sites = world + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get_in_square(pos, 48, None); for position in indexed_sites.into_iter().chain(saved_sites) { let delta = position.0 - pos.0; if i64::from(delta.x).pow(2) + i64::from(delta.y).pow(2) + i64::from(delta.z).pow(2) @@ -1084,20 +1119,17 @@ impl VillagerEntity { let mut navigator = Navigator::default(); let mut claimed = None; for (_, position, block, _) in candidates.into_iter().take(5) { - if !navigator - .can_reach_within( - &self.mob_entity.living_entity, - position.to_centered_f64(), - 1.73, - ) - .await - { + if !navigator.can_reach_within( + &self.mob_entity.living_entity, + position.to_centered_f64(), + 1.73, + ) { continue; } if world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .claim(position, block, owner.clone(), expected) .is_some() { @@ -1124,7 +1156,11 @@ impl VillagerEntity { { let (block, _state) = world.get_block_and_state(&site); if let Some(claimed_profession) = profession_for_block(block) { - let profession = self.villager_data.lock().await.profession_enum(); + let profession = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .profession_enum(); if profession != VillagerProfession::None && profession != claimed_profession { return; } @@ -1135,7 +1171,11 @@ impl VillagerEntity { ); self.job_site_pending.store(false, Ordering::Relaxed); if profession == VillagerProfession::None { - let r#type = self.villager_data.lock().await.type_enum(); + let r#type = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .type_enum(); self.set_villager_data(VillagerData::new(r#type, claimed_profession, 1)) .await; } @@ -1165,7 +1205,10 @@ impl VillagerEntity { // Open the merchant screen and then send the current offers packet if let Some(sync_id) = player.open_handled_screen(self, None).await { let offers = self.offers.lock().await.clone(); - let villager_data = *self.villager_data.lock().await; + let villager_data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); self.send_trade_offers(player, sync_id, offers, villager_data) .await; } @@ -1411,16 +1454,336 @@ impl ScreenHandlerFactory for VillagerEntity { } } +impl VillagerEntity { + #[expect(clippy::too_many_lines)] + pub async fn async_mob_tick(&self) { + let world = self.get_entity().world.load(); + + let unhappy_counter = self.unhappy_counter.load(Ordering::Relaxed); + if unhappy_counter > 0 { + let unhappy_counter = unhappy_counter - 1; + self.unhappy_counter + .store(unhappy_counter, Ordering::Relaxed); + self.get_entity().send_meta_data( + &[Metadata::new( + tracked_data::villager::UNHAPPY_COUNTER, + VarInt(unhappy_counter), + )], + None, + ); + } + self.trade_sound_cooldown + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |cooldown| { + (cooldown > 0).then_some(cooldown - 1) + }) + .ok(); + + let last_traded_player = self + .last_traded_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); + if let Some(player_uuid) = last_traded_player { + let mut gossips = self.gossips.lock().await; + let value = gossips + .entry(player_uuid) + .or_default() + .entry(GossipType::Trading) + .or_default(); + *value = (*value + 2).min(GossipType::Trading.max_value()); + drop(gossips); + world.send_entity_status( + self.get_entity(), + pumpkin_data::entity::EntityStatus::VillagerHappy, + Some(ActorEventID::VillagerHappy), + ); + } + + if !self.is_trading.load(Ordering::Relaxed) + && self.merchant_update_timer.load(Ordering::Relaxed) > 0 + && self.merchant_update_timer.fetch_sub(1, Ordering::Relaxed) == 1 + { + if self + .increase_profession_level_on_update + .swap(false, Ordering::Relaxed) + { + let mut data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + data.level.0 += 1; + self.set_villager_data(data).await; + self.add_trades(data.profession_enum(), data.level.0).await; + } + self.mob_entity.living_entity.add_effect(Effect { + effect_type: &StatusEffect::REGENERATION, + duration: 200, + amplifier: 0, + ambient: false, + show_particles: true, + show_icon: true, + blend: false, + }); + } + + let (game_time, day_time, day) = { + let time = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (time.world_age, time.query_daytime(), time.query_day()) + }; + self.decay_gossips(game_time).await; + self.work_at_job_site(game_time, day_time, day).await; + + let age = self.get_entity().age.load(Ordering::Relaxed); + if age % 20 != 0 { + return; + } + self.update_job_site(&world).await; + + // 1. Bed / Sleeping logic (for all villagers: babies, nitwits, adults) + let is_sleeping = self.get_entity().pose.load() == EntityPose::Sleeping; + + // Check if current bed is still valid + if let Some(current_home) = self.get_home_pos() { + let (block, state) = world.get_block_and_state(¤t_home); + let valid = if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { + let bed_props = BedProperties::from_state_id(state.id, block); + bed_props.part == BedPart::Head + } else { + false + }; + + if !valid { + *self + .home_pos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + if is_sleeping { + // Wake up if bed was broken + self.get_entity().set_pose(EntityPose::Standing); + self.get_entity().send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, + )], + None, + ); + } + } + } + + // If no bed, search for one + if self.get_home_pos().is_none() { + let pos = self.get_entity().block_pos.load(); + let start = BlockPos::new(pos.0.x - 16, pos.0.y - 4, pos.0.z - 16); + let end = BlockPos::new(pos.0.x + 16, pos.0.y + 4, pos.0.z + 16); + + let aabb = BoundingBox::new( + Vector3::new( + pos.0.x as f64 - 32.0, + pos.0.y as f64 - 16.0, + pos.0.z as f64 - 32.0, + ), + Vector3::new( + pos.0.x as f64 + 32.0, + pos.0.y as f64 + 16.0, + pos.0.z as f64 + 32.0, + ), + ); + let nearby_entities = world.get_all_at_box(&aabb); + + let mut claimed_homes = Vec::new(); + for entity in nearby_entities { + if entity.get_entity().entity_id != self.get_entity().entity_id + && entity.get_entity().entity_type + == &pumpkin_data::entity::EntityType::VILLAGER + && let Some(home) = entity.get_home_pos() + { + claimed_homes.push(home); + } + } + + let mut best_home = None; + let mut best_dist = f64::MAX; + + for p in BlockPos::iterate(start, end) { + let (block, state) = world.get_block_and_state(&p); + if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { + let bed_props = BedProperties::from_state_id(state.id, block); + let bed_head_pos = if bed_props.part == BedPart::Head { + p + } else { + p.offset(bed_props.facing.to_offset()) + }; + + if claimed_homes.contains(&bed_head_pos) { + continue; + } + + let dist = bed_head_pos + .to_f64() + .squared_distance_to_vec(&self.get_entity().pos.load()); + if dist < best_dist { + best_dist = dist; + best_home = Some(bed_head_pos); + } + } + } + + if let Some(home) = best_home { + *self + .home_pos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(home); + } + } + + // Handle Sleeping/Waking up based on time + let is_sleeping = self.get_entity().pose.load() == EntityPose::Sleeping; + if let Some(home_pos) = self.get_home_pos() { + let time = world.get_time_of_day(); + let is_night = (12000..=23000).contains(&time); + + if is_night { + if !is_sleeping { + // Check distance to bed. If close enough, go to sleep + let dist = home_pos + .to_f64() + .squared_distance_to_vec(&self.get_entity().pos.load()); + if dist <= 4.0 { + // Within 2 blocks (squared distance 4.0) + let (block, state) = world.get_block_and_state(&home_pos); + if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { + let bed_props = BedProperties::from_state_id(state.id, block); + if !bed_props.occupied { + // Make bed occupied + BedBlock::set_occupied(true, &world, block, &home_pos, state.id); + + self.get_entity().set_pose(EntityPose::Sleeping); + self.get_entity().send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + Some(home_pos), + )], + None, + ); + } + } + } + } + } else if is_sleeping { + // It is day, wake up! + let (block, state) = world.get_block_and_state(&home_pos); + if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { + let bed_props = BedProperties::from_state_id(state.id, block); + if bed_props.occupied { + BedBlock::set_occupied(false, &world, block, &home_pos, state.id); + } + } + + self.get_entity().set_pose(EntityPose::Standing); + self.get_entity().send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, + )], + None, + ); + } + } else if is_sleeping { + // Wake up during the day + self.get_entity().set_pose(EntityPose::Standing); + self.get_entity().send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, + )], + None, + ); + } + + // 2. Iron Golem spawning logic (only for adults) + let profession = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .profession_enum(); + if profession != VillagerProfession::Nitwit && age >= 0 { + // Checked every 20 ticks, golem spawn check every ~100 ticks + if age % 100 == 0 && self.get_home().is_some() { + // Check if panicked or talked recently to spawn an Iron Golem + let has_bed = self.get_home().is_some(); + let has_worked = + game_time - self.last_worked_at_poi.load(Ordering::Relaxed) < 24000; + if has_bed && has_worked { + // Check nearby villagers + let my_pos = self.get_entity().pos.load(); + let bb = BoundingBox::new( + my_pos.sub_raw(16.0, 8.0, 16.0), + my_pos.add_raw(16.0, 8.0, 16.0), + ); + let nearby_villagers = world + .get_entities_at_box(&bb) + .iter() + .filter(|e| { + e.get_entity().entity_type + == &pumpkin_data::entity::EntityType::VILLAGER + }) + .count(); + + if nearby_villagers >= 3 { + // Check if an iron golem is already nearby + let nearby_golems = world + .get_entities_at_box(&bb) + .iter() + .filter(|e| { + e.get_entity().entity_type + == &pumpkin_data::entity::EntityType::IRON_GOLEM + }) + .count(); + + if nearby_golems == 0 { + // Attempt to spawn an Iron Golem + let spawn_pos = my_pos.add_raw(0.0, 0.5, 0.0); + let golem_entity = Entity::new( + world.clone(), + spawn_pos, + &pumpkin_data::entity::EntityType::IRON_GOLEM, + ); + let golem = crate::entity::passive::iron_golem::IronGolemEntity::new( + golem_entity, + ); + world.spawn_entity_non_save(golem as Arc); + world.send_entity_status( + self.get_entity(), + pumpkin_data::entity::EntityStatus::VillagerHappy, + Some(ActorEventID::VillagerHappy), + ); + } + } + } + } + } + } +} + impl Mob for VillagerEntity { #[expect(clippy::too_many_lines)] fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> crate::entity::NbtFuture<'a, ()> { Box::pin(async move { - let data = self.villager_data.lock().await; - let mut villager_data_nbt = NbtCompound::new(); - villager_data_nbt.put_int("Type", data.r#type.0); - villager_data_nbt.put_int("Profession", data.profession.0); - villager_data_nbt.put_int("Level", data.level.0); - nbt.put_compound("VillagerData", villager_data_nbt); + { + let data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut villager_data_nbt = NbtCompound::new(); + villager_data_nbt.put_int("Type", data.r#type.0); + villager_data_nbt.put_int("Profession", data.profession.0); + villager_data_nbt.put_int("Level", data.level.0); + nbt.put_compound("VillagerData", villager_data_nbt); + }; nbt.put_int("FoodLevel", self.food_level.load(Ordering::Relaxed)); nbt.put_int("Xp", self.xp.load(Ordering::Relaxed)); @@ -1434,11 +1797,17 @@ impl Mob for VillagerEntity { self.last_gossip_decay_time.load(Ordering::Relaxed), ); - let job_site_pos = *self - .job_site - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(pos) = job_site_pos { + let last_gossip_share = self.last_gossip_share_time.load(Ordering::Relaxed); + if last_gossip_share > 0 { + nbt.put_long("LastGossipShare", last_gossip_share); + } + + let last_worked = self.last_worked_at_poi.load(Ordering::Relaxed); + if last_worked > 0 { + nbt.put_long("LastWorkedAtPoi", last_worked); + } + + if let Some(pos) = self.get_job_site() { nbt.put_int("JobSiteX", pos.0.x); nbt.put_int("JobSiteY", pos.0.y); nbt.put_int("JobSiteZ", pos.0.z); @@ -1448,39 +1817,35 @@ impl Mob for VillagerEntity { ); } - let home_pos = *self - .home_pos - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(pos) = home_pos { + if let Some(pos) = self.get_home() { nbt.put_int("HomeX", pos.0.x); nbt.put_int("HomeY", pos.0.y); nbt.put_int("HomeZ", pos.0.z); } - // Save Offers - { - let offers = self.offers.lock().await; + let offers = self.offers.lock().await; + if !offers.is_empty() { let mut recipes = Vec::new(); for offer in offers.iter() { let mut recipe = NbtCompound::new(); - let mut buy = NbtCompound::new(); - offer.base_cost_a.0.write_item_stack(&mut buy); + let mut sell = NbtCompound::new(); + + let item_stack: &ItemStack = offer.base_cost_a.0.as_ref(); + item_stack.write_item_stack(&mut buy); recipe.put_compound("buy", buy); - if let Some(cost_b) = &offer.cost_b - && !cost_b.0.is_empty() - { + let item_stack: &ItemStack = offer.output.0.as_ref(); + item_stack.write_item_stack(&mut sell); + recipe.put_compound("sell", sell); + + if let Some(cost_b) = &offer.cost_b { let mut buy_b = NbtCompound::new(); - cost_b.0.write_item_stack(&mut buy_b); + let item_stack: &ItemStack = cost_b.0.as_ref(); + item_stack.write_item_stack(&mut buy_b); recipe.put_compound("buyB", buy_b); } - let mut sell_item = NbtCompound::new(); - offer.output.0.write_item_stack(&mut sell_item); - recipe.put_compound("sell", sell_item); - recipe.put_int("uses", offer.uses); recipe.put_int("maxUses", offer.max_uses); recipe.put_bool("rewardExp", offer.reward_exp); @@ -1489,48 +1854,42 @@ impl Mob for VillagerEntity { recipe.put_int("specialPrice", offer.special_price); recipe.put_int("demand", offer.demand); - recipes.push(pumpkin_nbt::tag::NbtTag::Compound(recipe)); + recipes.push(NbtTag::Compound(recipe)); } let mut offers_compound = NbtCompound::new(); - offers_compound.put("Recipes", pumpkin_nbt::tag::NbtTag::List(recipes)); + offers_compound.put("Recipes", NbtTag::List(recipes)); nbt.put_compound("Offers", offers_compound); - }; + } - // Inventory let inventory = self.inventory.lock().await; - let mut inventory_list = Vec::new(); - for stack_mutex in inventory.iter() { - let stack = stack_mutex.lock().await; - if !stack.is_empty() { - let mut item_nbt = NbtCompound::new(); - stack.write_item_stack(&mut item_nbt); - inventory_list.push(pumpkin_nbt::tag::NbtTag::Compound(item_nbt)); + if !inventory.is_empty() { + let mut inventory_list = Vec::new(); + for item in inventory.iter() { + let item = item.lock().await; + let mut item_compound = NbtCompound::new(); + item.write_item_stack(&mut item_compound); + inventory_list.push(NbtTag::Compound(item_compound)); } + nbt.put("Inventory", NbtTag::List(inventory_list)); } - nbt.put("Inventory", pumpkin_nbt::tag::NbtTag::List(inventory_list)); - // Gossips let gossips = self.gossips.lock().await; - let mut gossip_list = Vec::new(); - for (uuid, types) in gossips.iter() { - for (gtype, value) in types { - let mut gossip_nbt = NbtCompound::new(); - let uuid_val = uuid.as_u128(); - gossip_nbt.put( - "Target", - pumpkin_nbt::tag::NbtTag::IntArray(vec![ - (uuid_val >> 96) as i32, - ((uuid_val >> 64) & 0xFFFF_FFFF) as i32, - ((uuid_val >> 32) & 0xFFFF_FFFF) as i32, - (uuid_val & 0xFFFF_FFFF) as i32, - ]), - ); - gossip_nbt.put_string("Type", gtype.name().to_owned()); - gossip_nbt.put_int("Value", *value); - gossip_list.push(pumpkin_nbt::tag::NbtTag::Compound(gossip_nbt)); + if !gossips.is_empty() { + let mut gossip_list = Vec::new(); + for (uuid, entries) in gossips.iter() { + for (gossip_type, val) in entries { + let mut gossip_nbt = NbtCompound::new(); + let (u1, u2) = uuid.as_u64_pair(); + let uuid_array = + vec![(u1 >> 32) as i32, u1 as i32, (u2 >> 32) as i32, u2 as i32]; + gossip_nbt.put("Target", NbtTag::IntArray(uuid_array)); + gossip_nbt.put_string("Type", gossip_type.name().to_string()); + gossip_nbt.put_int("Value", *val); + gossip_list.push(NbtTag::Compound(gossip_nbt)); + } } + nbt.put("Gossips", NbtTag::List(gossip_list)); } - nbt.put("Gossips", pumpkin_nbt::tag::NbtTag::List(gossip_list)); }) } @@ -1538,7 +1897,10 @@ impl Mob for VillagerEntity { fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> crate::entity::NbtFuture<'a, ()> { Box::pin(async move { if let Some(villager_data_nbt) = nbt.get_compound("VillagerData") { - let mut data = self.villager_data.lock().await; + let mut data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(t) = villager_data_nbt.get_int("Type") { data.r#type = VarInt(t); } @@ -1716,7 +2078,10 @@ impl Mob for VillagerEntity { let mut metadata = Vec::new(); Metadata::new( tracked_data::villager::VILLAGER_DATA, - *self.villager_data.lock().await, + *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), ) .write(&mut metadata, &version) .ok()?; @@ -1730,7 +2095,10 @@ impl Mob for VillagerEntity { ) -> crate::entity::EntityBaseFuture<'_, Option> { Box::pin(async move { Some(Self::bedrock_metadata( - *self.villager_data.lock().await, + *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), self.xp.load(Ordering::Relaxed), )) }) @@ -1743,35 +2111,28 @@ impl Mob for VillagerEntity { .unwrap_or_else(std::sync::PoisonError::into_inner) } - fn is_job_site_pending(&self) -> crate::entity::EntityBaseFuture<'_, bool> { - Box::pin(async move { self.job_site_pending.load(Ordering::Relaxed) }) + fn is_job_site_pending(&self) -> bool { + self.job_site_pending.load(Ordering::Relaxed) } - fn release_pending_job_site( - &self, - position: BlockPos, - ) -> crate::entity::EntityBaseFuture<'_, ()> { - Box::pin(async move { - if self.get_job_site() != Some(position) - || !self.job_site_pending.load(Ordering::Relaxed) - { - return; - } - self.get_entity() - .world - .load() - .villager_poi + fn release_pending_job_site(&self, position: BlockPos) { + if self.get_job_site() != Some(position) || !self.job_site_pending.load(Ordering::Relaxed) { + return; + } + self.get_entity() + .world + .load() + .villager_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .release(position, self.get_entity().entity_uuid); + if self.get_job_site() == Some(position) { + *self + .job_site .lock() - .await - .release(position, self.get_entity().entity_uuid); - if self.get_job_site() == Some(position) { - *self - .job_site - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - self.job_site_pending.store(false, Ordering::Relaxed); - } - }) + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + self.job_site_pending.store(false, Ordering::Relaxed); + } } fn get_trading_player(&self) -> Option> { @@ -1793,304 +2154,66 @@ impl Mob for VillagerEntity { .unwrap_or_else(std::sync::PoisonError::into_inner) } - fn mob_init_data_tracker(&self) -> crate::entity::EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let data = *self.villager_data.lock().await; - let bedrock_metadata = Self::bedrock_metadata(data, self.xp.load(Ordering::Relaxed)); + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let bedrock_metadata = Self::bedrock_metadata(data, self.xp.load(Ordering::Relaxed)); + entity.send_meta_data( + &[Metadata::new(tracked_data::villager::VILLAGER_DATA, data)], + Some(&bedrock_metadata), + ); + if entity.age.load(Ordering::Relaxed) < 0 { entity.send_meta_data( - &[Metadata::new(tracked_data::villager::VILLAGER_DATA, data)], - Some(&bedrock_metadata), + &[Metadata::new(tracked_data::villager::BABY_ID, true)], + None, ); - if entity.age.load(Ordering::Relaxed) < 0 { - entity.send_meta_data( - &[Metadata::new(tracked_data::villager::BABY_ID, true)], - None, - ); - } - }) + } } - fn on_damage<'a>( - &'a self, + fn on_damage( + &self, _damage_type: pumpkin_data::damage::DamageType, - source: Option<&'a dyn EntityBase>, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - Box::pin(async move { - let Some(source) = source.filter(|source| { - source.get_entity().entity_type == &pumpkin_data::entity::EntityType::PLAYER - }) else { - return; - }; - let mut gossips = self.gossips.lock().await; + source: Option<&dyn EntityBase>, + ) { + let Some(source) = source.filter(|source| { + source.get_entity().entity_type == &pumpkin_data::entity::EntityType::PLAYER + }) else { + return; + }; + + let Some(player) = source.cast_any().downcast_ref::() else { + return; + }; + + if let Ok(mut gossips) = self.gossips.try_lock() { let value = gossips - .entry(source.get_entity().entity_uuid) + .entry(player.gameprofile.id) .or_default() .entry(GossipType::MinorNegative) .or_default(); *value = (*value + 25).min(GossipType::MinorNegative.max_value()); - drop(gossips); - self.get_entity().world.load().send_entity_status( - self.get_entity(), - pumpkin_data::entity::EntityStatus::VillagerAngry, - Some(ActorEventID::VillagerAngry), - ); - }) + } } - #[expect(clippy::too_many_lines)] - fn mob_tick<'a>( - &'a self, - _caller: &'a Arc, - ) -> crate::entity::EntityBaseFuture<'a, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); - - let unhappy_counter = self.unhappy_counter.load(Ordering::Relaxed); - if unhappy_counter > 0 { - let unhappy_counter = unhappy_counter - 1; - self.unhappy_counter - .store(unhappy_counter, Ordering::Relaxed); - self.get_entity().send_meta_data( - &[Metadata::new( - tracked_data::villager::UNHAPPY_COUNTER, - VarInt(unhappy_counter), - )], - None, - ); + fn mob_tick<'a>(&'a self, caller: &'a Arc) { + let caller_clone = caller.clone(); + tokio::spawn(async move { + if let Some(villager) = caller_clone.cast_any().downcast_ref::() { + villager.async_mob_tick().await; } - self.trade_sound_cooldown - .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |cooldown| { - (cooldown > 0).then_some(cooldown - 1) - }) - .ok(); - - let last_traded_player = self.last_traded_player.lock().await.take(); - if let Some(player_uuid) = last_traded_player { - let mut gossips = self.gossips.lock().await; - let value = gossips - .entry(player_uuid) - .or_default() - .entry(GossipType::Trading) - .or_default(); - *value = (*value + 2).min(GossipType::Trading.max_value()); - drop(gossips); - world.send_entity_status( - self.get_entity(), - pumpkin_data::entity::EntityStatus::VillagerHappy, - Some(ActorEventID::VillagerHappy), - ); - } - - if !self.is_trading.load(Ordering::Relaxed) - && self.merchant_update_timer.load(Ordering::Relaxed) > 0 - && self.merchant_update_timer.fetch_sub(1, Ordering::Relaxed) == 1 - { - if self - .increase_profession_level_on_update - .swap(false, Ordering::Relaxed) - { - let mut data = *self.villager_data.lock().await; - data.level.0 += 1; - self.set_villager_data(data).await; - self.add_trades(data.profession_enum(), data.level.0).await; - } - self.mob_entity - .living_entity - .add_effect(Effect { - effect_type: &StatusEffect::REGENERATION, - duration: 200, - amplifier: 0, - ambient: false, - show_particles: true, - show_icon: true, - blend: false, - }) - .await; - } - - let (game_time, day_time, day) = { - let time = world.level_time.lock().await; - (time.world_age, time.query_daytime(), time.query_day()) - }; - self.decay_gossips(game_time).await; - self.work_at_job_site(game_time, day_time, day).await; - - let age = self.get_entity().age.load(Ordering::Relaxed); - if age % 20 != 0 { - return; - } - self.update_job_site(&world).await; - - // 1. Bed / Sleeping logic (for all villagers: babies, nitwits, adults) - let is_sleeping = self.get_entity().pose.load() == EntityPose::Sleeping; - - // Check if current bed is still valid - if let Some(current_home) = self.get_home_pos() { - let (block, state) = world.get_block_and_state(¤t_home); - let valid = if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { - let bed_props = BedProperties::from_state_id(state.id, block); - bed_props.part == BedPart::Head - } else { - false - }; - - if !valid { - *self - .home_pos - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - if is_sleeping { - // Wake up if bed was broken - self.get_entity().set_pose(EntityPose::Standing); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - None::, - )], - None, - ); - } - } - } - - // If no bed, search for one - if self.get_home_pos().is_none() { - let pos = self.get_entity().block_pos.load(); - let start = BlockPos::new(pos.0.x - 16, pos.0.y - 4, pos.0.z - 16); - let end = BlockPos::new(pos.0.x + 16, pos.0.y + 4, pos.0.z + 16); - - let aabb = BoundingBox::new( - Vector3::new( - pos.0.x as f64 - 32.0, - pos.0.y as f64 - 16.0, - pos.0.z as f64 - 32.0, - ), - Vector3::new( - pos.0.x as f64 + 32.0, - pos.0.y as f64 + 16.0, - pos.0.z as f64 + 32.0, - ), - ); - let nearby_entities = world.get_all_at_box(&aabb); - - let mut claimed_homes = Vec::new(); - for entity in nearby_entities { - if entity.get_entity().entity_id != self.get_entity().entity_id - && entity.get_entity().entity_type - == &pumpkin_data::entity::EntityType::VILLAGER - && let Some(home) = entity.get_home_pos() - { - claimed_homes.push(home); - } - } - - let mut best_home = None; - let mut best_dist = f64::MAX; - - for p in BlockPos::iterate(start, end) { - let (block, state) = world.get_block_and_state(&p); - if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { - let bed_props = BedProperties::from_state_id(state.id, block); - let bed_head_pos = if bed_props.part == BedPart::Head { - p - } else { - p.offset(bed_props.facing.to_offset()) - }; - - if claimed_homes.contains(&bed_head_pos) { - continue; - } - - let dist = bed_head_pos - .to_f64() - .squared_distance_to_vec(&self.get_entity().pos.load()); - if dist < best_dist { - best_dist = dist; - best_home = Some(bed_head_pos); - } - } - } - - if let Some(home) = best_home { - *self - .home_pos - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(home); - } - } - - // Handle Sleeping/Waking up based on time - let is_sleeping = self.get_entity().pose.load() == EntityPose::Sleeping; - if let Some(home_pos) = self.get_home_pos() { - let time = world.level_time.lock().await.time_of_day; - let is_night = (12000..=23000).contains(&time); - - if is_night { - if !is_sleeping { - // Check distance to bed. If close enough, go to sleep - let dist = home_pos - .to_f64() - .squared_distance_to_vec(&self.get_entity().pos.load()); - if dist <= 4.0 { - // Within 2 blocks (squared distance 4.0) - let (block, state) = world.get_block_and_state(&home_pos); - if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { - let bed_props = BedProperties::from_state_id(state.id, block); - if !bed_props.occupied { - // Make bed occupied - BedBlock::set_occupied( - true, &world, block, &home_pos, state.id, - ) - .await; - - self.get_entity().set_pose(EntityPose::Sleeping); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - Some(home_pos), - )], - None, - ); - } - } - } - } - } else if is_sleeping { - // It is day, wake up! - let (block, state) = world.get_block_and_state(&home_pos); - if block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEDS) { - let bed_props = BedProperties::from_state_id(state.id, block); - if bed_props.occupied { - BedBlock::set_occupied(false, &world, block, &home_pos, state.id).await; - } - } - - self.get_entity().set_pose(EntityPose::Standing); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - None::, - )], - None, - ); - } - } - }) + }); } fn mob_interact<'a>( &'a self, player: &'a Arc, - item_stack: &'a mut pumpkin_data::item_stack::ItemStack, + _item_stack: &'a mut ItemStack, ) -> crate::entity::EntityBaseFuture<'a, bool> { - let player = player.clone(); Box::pin(async move { - if item_stack.item == &Item::VILLAGER_SPAWN_EGG - || self.mob_entity.living_entity.health.load() <= 0.0 - || self.is_trading.load(Ordering::Relaxed) - || self.get_entity().pose.load() == EntityPose::Sleeping - { + if self.is_trading.load(Ordering::Relaxed) { return false; } if self.get_entity().age.load(Ordering::Relaxed) < 0 { @@ -2098,38 +2221,37 @@ impl Mob for VillagerEntity { return true; } - let mut offers = self.offers.lock().await; - if offers.is_empty() { - let data = self.villager_data.lock().await; - if data.profession_enum() != VillagerProfession::None - && data.profession_enum() != VillagerProfession::Nitwit - { - let prof = data.profession_enum(); - let level = data.level.0; - drop(data); - drop(offers); - self.generate_trades(prof, level).await; - offers = self.offers.lock().await; + let trade_params = { + let offers = self.offers.lock().await; + if offers.is_empty() { + let data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (data.profession_enum() != VillagerProfession::None + && data.profession_enum() != VillagerProfession::Nitwit) + .then(|| (data.profession_enum(), data.level.0)) } else { - drop(data); + None } + }; + if let Some((prof, level)) = trade_params { + self.generate_trades(prof, level).await; } - if offers.is_empty() { + let has_offers = !self.offers.lock().await.is_empty(); + if !has_offers { self.set_unhappy(); return true; } - drop(offers); - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::TalkedToVillager as i32, - 1, - ) - .await; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::TalkedToVillager as i32, + 1, + ); - self.open_trading_screen(&player).await; + self.open_trading_screen(player).await; true }) diff --git a/crates/pumpkin/src/entity/passive/wandering_trader.rs b/crates/pumpkin/src/entity/passive/wandering_trader.rs index efb72a92c..f591c1a44 100644 --- a/crates/pumpkin/src/entity/passive/wandering_trader.rs +++ b/crates/pumpkin/src/entity/passive/wandering_trader.rs @@ -41,7 +41,7 @@ use crate::entity::ai::goal::look_at_entity::LookAtEntityGoal; use crate::entity::ai::goal::swim::SwimGoal; use crate::entity::ai::goal::trade_with_player::TradeWithPlayerGoal; use crate::entity::ai::goal::wander_around::WanderAroundGoal; -use crate::entity::ai::goal::{Controls, Goal, GoalFuture}; +use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::mob::{Mob, MobEntity, NIGHT_END, NIGHT_START}; @@ -111,7 +111,7 @@ fn add_offers_from_trade_set( pub struct WanderingTraderEntity { pub mob_entity: MobEntity, pub despawn_delay: AtomicI32, - pub wander_target: Mutex>, + pub wander_target: std::sync::Mutex>, pub offers: Mutex>, pub merchant_inventory: Arc, pub trading_player: std::sync::Mutex>, @@ -130,7 +130,7 @@ impl WanderingTraderEntity { let trader = Self { mob_entity, despawn_delay: AtomicI32::new(DEFAULT_DESPAWN_DELAY), - wander_target: Mutex::new(None), + wander_target: std::sync::Mutex::new(None), offers: Mutex::new(Vec::new()), merchant_inventory: Arc::new(SimpleInventory::new(3)), trading_player: std::sync::Mutex::new(None), @@ -272,12 +272,18 @@ impl WanderingTraderEntity { self.despawn_delay.store(delay, Ordering::Relaxed); } - pub async fn get_wander_target(&self) -> Option { - *self.wander_target.lock().await + pub fn get_wander_target(&self) -> Option { + *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } - pub async fn set_wander_target(&self, target: Option) { - *self.wander_target.lock().await = target; + pub fn set_wander_target(&self, target: Option) { + *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = target; } pub async fn generate_trades(&self) { @@ -463,11 +469,11 @@ impl WanderingTraderEntity { if reward_exp { let position = self.get_entity().pos.load().add_raw(0.0, 0.5, 0.0); - ExperienceOrbEntity::spawn(world, position, reward_amount).await; + ExperienceOrbEntity::spawn(world, position, reward_amount); } if let Some(player) = world.get_player_by_uuid(player_uuid) { - trigger_trade_advancement(&player).await; + trigger_trade_advancement(&player); } } } @@ -604,7 +610,10 @@ impl Mob for WanderingTraderEntity { fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { nbt.put_int("DespawnDelay", self.despawn_delay.load(Ordering::Relaxed)); - let wander_target = *self.wander_target.lock().await; + let wander_target = *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(target) = wander_target { nbt.put( "wander_target", @@ -656,14 +665,21 @@ impl Mob for WanderingTraderEntity { if let Some(target_arr) = nbt.get_int_array("wander_target") && target_arr.len() >= 3 { - *self.wander_target.lock().await = + *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(BlockPos::new(target_arr[0], target_arr[1], target_arr[2])); } else if let (Some(x), Some(y), Some(z)) = ( nbt.get_int("wander_target_x"), nbt.get_int("wander_target_y"), nbt.get_int("wander_target_z"), ) { - *self.wander_target.lock().await = Some(BlockPos::new(x, y, z)); + *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + Some(BlockPos::new(x, y, z)); } if let Some(offers_compound) = nbt.get_compound("Offers") @@ -736,13 +752,11 @@ impl Mob for WanderingTraderEntity { return false; } - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::TalkedToVillager as i32, - 1, - ) - .await; + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::TalkedToVillager as i32, + 1, + ); let mut offers = self.offers.lock().await; if offers.is_empty() { @@ -761,50 +775,48 @@ impl Mob for WanderingTraderEntity { }) } - fn mob_tick<'a>(&'a self, _caller: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Despawn delay handling (vanilla aiStep / maybeDespawn) - if !self.is_trading.load(Ordering::Relaxed) { - let delay = self.despawn_delay.load(Ordering::Relaxed); - if delay > 0 { - let new_delay = delay - 1; - self.despawn_delay.store(new_delay, Ordering::Relaxed); - if new_delay == 0 { - self.mob_entity.living_entity.entity.remove().await; - return; - } + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + // Despawn delay handling (vanilla aiStep / maybeDespawn) + if !self.is_trading.load(Ordering::Relaxed) { + let delay = self.despawn_delay.load(Ordering::Relaxed); + if delay > 0 { + let new_delay = delay - 1; + self.despawn_delay.store(new_delay, Ordering::Relaxed); + if new_delay == 0 { + self.mob_entity.living_entity.entity.remove(); + return; } } + } - // Trade sound cooldown - let cooldown = self.trade_sound_cooldown.load(Ordering::Relaxed); - if cooldown > 0 { - self.trade_sound_cooldown - .store(cooldown - 1, Ordering::Relaxed); - } + // Trade sound cooldown + let cooldown = self.trade_sound_cooldown.load(Ordering::Relaxed); + if cooldown > 0 { + self.trade_sound_cooldown + .store(cooldown - 1, Ordering::Relaxed); + } - // Ambient sound handling - if self.ambient_sound_timer.fetch_sub(1, Ordering::Relaxed) <= 0 { - let mut rng = rand::rng(); - self.ambient_sound_timer - .store(rng.random_range(80..=160), Ordering::Relaxed); - let sound = if self.is_trading.load(Ordering::Relaxed) { - Sound::EntityWanderingTraderTrade - } else { - Sound::EntityWanderingTraderAmbient - }; - self.mob_entity - .living_entity - .entity - .world - .load() - .play_sound( - sound, - SoundCategory::Neutral, - &self.mob_entity.living_entity.entity.pos.load(), - ); - } - }) + // Ambient sound handling + if self.ambient_sound_timer.fetch_sub(1, Ordering::Relaxed) <= 0 { + let mut rng = rand::rng(); + self.ambient_sound_timer + .store(rng.random_range(80..=160), Ordering::Relaxed); + let sound = if self.is_trading.load(Ordering::Relaxed) { + Sound::EntityWanderingTraderTrade + } else { + Sound::EntityWanderingTraderAmbient + }; + self.mob_entity + .living_entity + .entity + .world + .load() + .play_sound( + sound, + SoundCategory::Neutral, + &self.mob_entity.living_entity.entity.pos.load(), + ); + } } } @@ -822,44 +834,38 @@ impl LookAtTradingPlayerGoal { } impl Goal for LookAtTradingPlayerGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(player) = mob.get_trading_player() else { - return false; - }; - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let player_pos = player.get_entity().pos.load(); - mob_pos.squared_distance_to_vec(&player_pos) <= self.range * self.range - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let Some(player) = mob.get_trading_player() else { + return false; + }; + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let player_pos = player.get_entity().pos.load(); + mob_pos.squared_distance_to_vec(&player_pos) <= self.range * self.range } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(player) = mob.get_trading_player() else { - return false; - }; - let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); - let player_pos = player.get_entity().pos.load(); - mob_pos.squared_distance_to_vec(&player_pos) <= self.range * self.range - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(player) = mob.get_trading_player() else { + return false; + }; + let mob_pos = mob.get_mob_entity().living_entity.entity.pos.load(); + let player_pos = player.get_entity().pos.load(); + mob_pos.squared_distance_to_vec(&player_pos) <= self.range * self.range } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(player) = mob.get_trading_player() { - let player_pos = player.get_entity().pos.load(); - mob.get_mob_entity() - .look_control - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .look_at( - mob, - player_pos.x, - player.get_entity().get_eye_y(), - player_pos.z, - ); - } - }) + fn tick(&mut self, mob: &dyn Mob) { + if let Some(player) = mob.get_trading_player() { + let player_pos = player.get_entity().pos.load(); + mob.get_mob_entity() + .look_control + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .look_at( + mob, + player_pos.x, + player.get_entity().get_eye_y(), + player_pos.z, + ); + } } fn should_run_every_tick(&self) -> bool { @@ -902,99 +908,103 @@ impl WanderToPositionGoal { } impl Goal for WanderToPositionGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return false; - }; - let wander_target = *trader.wander_target.lock().await; - let Some(wander_pos) = wander_target else { - return false; - }; - let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); - Self::is_too_far_away(&wander_pos, &entity_pos, self.stop_distance) - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(trader) = self.trader.upgrade() else { + return false; + }; + let wander_target = *trader + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some(wander_pos) = wander_target else { + return false; + }; + let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); + Self::is_too_far_away(&wander_pos, &entity_pos, self.stop_distance) } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return false; - }; - let wander_target = *trader.wander_target.lock().await; - let Some(wander_pos) = wander_target else { - return false; - }; - let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); - Self::is_too_far_away(&wander_pos, &entity_pos, self.stop_distance) - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(trader) = self.trader.upgrade() else { + return false; + }; + let wander_target = *trader + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some(wander_pos) = wander_target else { + return false; + }; + let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); + Self::is_too_far_away(&wander_pos, &entity_pos, self.stop_distance) } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(trader) = self.trader.upgrade() { - *trader.wander_target.lock().await = None; - trader - .mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .stop(); - } - }) - } - - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return; - }; - let wander_target = *trader.wander_target.lock().await; - let Some(wander_pos) = wander_target else { - return; - }; - let is_idle = trader + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(trader) = self.trader.upgrade() { + *trader + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + trader .mob_entity .navigator .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) - .is_idle(); - if is_idle { - let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); - let center = Vector3::new( - wander_pos.0.x as f64 + 0.5, - wander_pos.0.y as f64 + 0.5, - wander_pos.0.z as f64 + 0.5, - ); - let target_pos = if Self::is_too_far_away(&wander_pos, &entity_pos, 10.0) { - let dx = center.x - entity_pos.x; - let dy = center.y - entity_pos.y; - let dz = center.z - entity_pos.z; - let len = (dx * dx + dy * dy + dz * dz).sqrt(); - if len > 0.0 { - Vector3::new( - entity_pos.x + (dx / len) * 10.0, - entity_pos.y + (dy / len) * 10.0, - entity_pos.z + (dz / len) * 10.0, - ) - } else { - center - } + .stop(); + } + } + + fn tick(&mut self, _mob: &dyn Mob) { + let Some(trader) = self.trader.upgrade() else { + return; + }; + let wander_target = *trader + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some(wander_pos) = wander_target else { + return; + }; + let is_idle = trader + .mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_idle(); + if is_idle { + let entity_pos = trader.mob_entity.living_entity.entity.pos.load(); + let center = Vector3::new( + wander_pos.0.x as f64 + 0.5, + wander_pos.0.y as f64 + 0.5, + wander_pos.0.z as f64 + 0.5, + ); + let target_pos = if Self::is_too_far_away(&wander_pos, &entity_pos, 10.0) { + let dx = center.x - entity_pos.x; + let dy = center.y - entity_pos.y; + let dz = center.z - entity_pos.z; + let len = (dx * dx + dy * dy + dz * dz).sqrt(); + if len > 0.0 { + Vector3::new( + entity_pos.x + (dx / len) * 10.0, + entity_pos.y + (dy / len) * 10.0, + entity_pos.z + (dz / len) * 10.0, + ) } else { center - }; - trader - .mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_progress(NavigatorGoal::new( - entity_pos, - target_pos, - self.speed_modifier, - )); - } - }) + } + } else { + center + }; + trader + .mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_progress(NavigatorGoal::new( + entity_pos, + target_pos, + self.speed_modifier, + )); + } } fn controls(&self) -> Controls { @@ -1014,42 +1024,36 @@ impl MoveTowardsRestrictionGoal { } impl Goal for MoveTowardsRestrictionGoal { - fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - mob_entity.has_position_target() && !mob_entity.is_in_position_target_range() - }) + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + mob_entity.has_position_target() && !mob_entity.is_in_position_target_range() } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - !mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .is_idle() - && mob_entity.has_position_target() - && !mob_entity.is_in_position_target_range() - }) + fn should_continue(&self, mob: &dyn Mob) -> bool { + let mob_entity = mob.get_mob_entity(); + !mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_idle() + && mob_entity.has_position_target() + && !mob_entity.is_in_position_target_range() } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let mob_entity = mob.get_mob_entity(); - let target = mob_entity.position_target.load(); - let entity_pos = mob_entity.living_entity.entity.pos.load(); - let dest = Vector3::new( - target.0.x as f64 + 0.5, - target.0.y as f64, - target.0.z as f64 + 0.5, - ); - mob_entity - .navigator - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_progress(NavigatorGoal::new(entity_pos, dest, self.speed)); - }) + fn start(&mut self, mob: &dyn Mob) { + let mob_entity = mob.get_mob_entity(); + let target = mob_entity.position_target.load(); + let entity_pos = mob_entity.living_entity.entity.pos.load(); + let dest = Vector3::new( + target.0.x as f64 + 0.5, + target.0.y as f64, + target.0.z as f64 + 0.5, + ); + mob_entity + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_progress(NavigatorGoal::new(entity_pos, dest, self.speed)); } fn controls(&self) -> Controls { @@ -1080,161 +1084,136 @@ impl WanderingTraderUseItemGoal { } impl Goal for WanderingTraderUseItemGoal { - fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return false; - }; - if !trader.mob_entity.living_entity.entity.is_alive() - || trader.is_trading.load(Ordering::Relaxed) - { - return false; - } - let world = trader.mob_entity.living_entity.entity.world.load(); - let day_time = world.get_time_of_day().await % 24000; - let is_dark = (NIGHT_START..=NIGHT_END).contains(&day_time); - let is_invisible = trader - .mob_entity - .living_entity - .get_effect(&StatusEffect::INVISIBILITY) - .await - .is_some(); + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + let Some(trader) = self.trader.upgrade() else { + return false; + }; + if !trader.mob_entity.living_entity.entity.is_alive() + || trader.is_trading.load(Ordering::Relaxed) + { + return false; + } + let world = trader.mob_entity.living_entity.entity.world.load(); + let day_time = world.get_time_of_day() % 24000; + let is_dark = (NIGHT_START..=NIGHT_END).contains(&day_time); + let is_invisible = trader + .mob_entity + .living_entity + .has_effect(&StatusEffect::INVISIBILITY); - if is_dark && !is_invisible { - self.goal_type = Some(PotionGoalType::Invisibility); - return true; - } - if !is_dark && is_invisible { - self.goal_type = Some(PotionGoalType::Milk); - return true; - } - false - }) + if is_dark && !is_invisible { + self.goal_type = Some(PotionGoalType::Invisibility); + return true; + } + if !is_dark && is_invisible { + self.goal_type = Some(PotionGoalType::Milk); + return true; + } + false } - fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return false; - }; - self.timer > 0 - && trader.mob_entity.living_entity.entity.is_alive() - && !trader.is_trading.load(Ordering::Relaxed) - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + let Some(trader) = self.trader.upgrade() else { + return false; + }; + self.timer > 0 + && trader.mob_entity.living_entity.entity.is_alive() + && !trader.is_trading.load(Ordering::Relaxed) } - fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return; - }; - self.timer = 32; - let stack = match self.goal_type { - Some(PotionGoalType::Invisibility) => create_invisibility_potion(), - Some(PotionGoalType::Milk) => ItemStack::new(1, &Item::MILK_BUCKET), - None => return, - }; - let mut equip = trader - .mob_entity - .living_entity - .entity_equipment - .lock() - .await; + fn start(&mut self, _mob: &dyn Mob) { + let Some(trader) = self.trader.upgrade() else { + return; + }; + self.timer = 32; + let stack = match self.goal_type { + Some(PotionGoalType::Invisibility) => create_invisibility_potion(), + Some(PotionGoalType::Milk) => ItemStack::new(1, &Item::MILK_BUCKET), + None => return, + }; + if let Ok(mut equip) = trader.mob_entity.living_entity.entity_equipment.try_lock() { equip.put(&EquipmentSlot::MAIN_HAND, stack.clone()); drop(equip); trader .mob_entity .living_entity .send_equipment_changes(&[(EquipmentSlot::MAIN_HAND, stack)]); - }) + } } - fn tick<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - let Some(trader) = self.trader.upgrade() else { - return; + fn tick(&mut self, _mob: &dyn Mob) { + let Some(trader) = self.trader.upgrade() else { + return; + }; + self.timer -= 1; + if self.timer > 0 && self.timer % 4 == 0 { + let sound = match self.goal_type { + Some(PotionGoalType::Invisibility) => Sound::EntityWanderingTraderDrinkPotion, + Some(PotionGoalType::Milk) => Sound::EntityWanderingTraderDrinkMilk, + None => return, }; - self.timer -= 1; - if self.timer > 0 && self.timer % 4 == 0 { - let sound = match self.goal_type { - Some(PotionGoalType::Invisibility) => Sound::EntityWanderingTraderDrinkPotion, - Some(PotionGoalType::Milk) => Sound::EntityWanderingTraderDrinkMilk, - None => return, - }; - trader - .mob_entity - .living_entity - .entity - .world - .load() - .play_sound( - sound, - SoundCategory::Neutral, - &trader.mob_entity.living_entity.entity.pos.load(), - ); - } - if self.timer == 0 { - match self.goal_type { - Some(PotionGoalType::Invisibility) => { - trader - .mob_entity - .living_entity - .add_effect(Effect { - effect_type: &StatusEffect::INVISIBILITY, - duration: 6000, - amplifier: 0, - ambient: false, - show_particles: true, - show_icon: true, - blend: false, - }) - .await; - trader - .mob_entity - .living_entity - .entity - .world - .load() - .play_sound( - Sound::EntityWanderingTraderDisappeared, - SoundCategory::Neutral, - &trader.mob_entity.living_entity.entity.pos.load(), - ); - } - Some(PotionGoalType::Milk) => { - trader - .mob_entity - .living_entity - .remove_effect(&StatusEffect::INVISIBILITY) - .await; - trader - .mob_entity - .living_entity - .entity - .world - .load() - .play_sound( - Sound::EntityWanderingTraderReappeared, - SoundCategory::Neutral, - &trader.mob_entity.living_entity.entity.pos.load(), - ); - } - None => {} + trader + .mob_entity + .living_entity + .entity + .world + .load() + .play_sound( + sound, + SoundCategory::Neutral, + &trader.mob_entity.living_entity.entity.pos.load(), + ); + } + if self.timer == 0 { + match self.goal_type { + Some(PotionGoalType::Invisibility) => { + trader.mob_entity.living_entity.add_effect(Effect { + effect_type: &StatusEffect::INVISIBILITY, + duration: 6000, + amplifier: 0, + ambient: false, + show_particles: true, + show_icon: true, + blend: false, + }); + trader + .mob_entity + .living_entity + .entity + .world + .load() + .play_sound( + Sound::EntityWanderingTraderDisappeared, + SoundCategory::Neutral, + &trader.mob_entity.living_entity.entity.pos.load(), + ); } + Some(PotionGoalType::Milk) => { + trader + .mob_entity + .living_entity + .remove_effect(&StatusEffect::INVISIBILITY); + trader + .mob_entity + .living_entity + .entity + .world + .load() + .play_sound( + Sound::EntityWanderingTraderReappeared, + SoundCategory::Neutral, + &trader.mob_entity.living_entity.entity.pos.load(), + ); + } + None => {} } - }) + } } - fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async move { - if let Some(trader) = self.trader.upgrade() { - let empty = ItemStack::EMPTY; - let mut equip = trader - .mob_entity - .living_entity - .entity_equipment - .lock() - .await; + fn stop(&mut self, _mob: &dyn Mob) { + if let Some(trader) = self.trader.upgrade() { + let empty = ItemStack::EMPTY; + if let Ok(mut equip) = trader.mob_entity.living_entity.entity_equipment.try_lock() { equip.put(&EquipmentSlot::MAIN_HAND, empty.clone()); drop(equip); trader @@ -1242,9 +1221,9 @@ impl Goal for WanderingTraderUseItemGoal { .living_entity .send_equipment_changes(&[(EquipmentSlot::MAIN_HAND, empty.clone())]); } - self.goal_type = None; - self.timer = 0; - }) + } + self.goal_type = None; + self.timer = 0; } } diff --git a/crates/pumpkin/src/entity/passive/wolf.rs b/crates/pumpkin/src/entity/passive/wolf.rs index 4c90d95f0..412543428 100644 --- a/crates/pumpkin/src/entity/passive/wolf.rs +++ b/crates/pumpkin/src/entity/passive/wolf.rs @@ -12,7 +12,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, ageable::AgeableMob, ai::goal::{ active_target::ActiveTargetGoal, avoid_entity::AvoidEntityGoal, beg::BegGoal, @@ -290,47 +290,45 @@ impl Mob for WolfEntity { self.variant.store(variant, Ordering::Relaxed); } - fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let is_baby = entity.age.load(Ordering::Relaxed) < 0; - if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::BABY_ID, - true, - )], - None, - ); - } + fn mob_init_data_tracker(&self) { + let entity = self.get_entity(); + let is_baby = entity.age.load(Ordering::Relaxed) < 0; + if is_baby { entity.send_meta_data( &[Metadata::new( - pumpkin_data::tracked_data::wolf::TAMEABLE_FLAGS, - self.get_tame_flags(), + pumpkin_data::tracked_data::wolf::BABY_ID, + true, )], None, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::COLLAR_COLOR, - VarInt(self.collar_color.load(Ordering::Relaxed) as i32), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::WOLF_VARIANT_ID, - VarInt(self.variant.load(Ordering::Relaxed) as i32), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::OWNER_UUID, - self.get_owner(), - )], - None, - ); - }) + } + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::wolf::TAMEABLE_FLAGS, + self.get_tame_flags(), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::wolf::COLLAR_COLOR, + VarInt(self.collar_color.load(Ordering::Relaxed) as i32), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::wolf::WOLF_VARIANT_ID, + VarInt(self.variant.load(Ordering::Relaxed) as i32), + )], + None, + ); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::wolf::OWNER_UUID, + self.get_owner(), + )], + None, + ); } } diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index b8f403d50..af2b79da7 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -225,8 +225,8 @@ use pumpkin_inventory::player::{ player_inventory::PlayerInventory, player_screen_handler::PlayerScreenHandler, }; use pumpkin_inventory::screen_handler::{ - ClickType, InventoryPlayer, PlayerFuture, ScreenHandler, ScreenHandlerFactory, - ScreenHandlerListener, + BoxFuture, ClickType, InventoryPlayer, PlayerFuture, ScreenHandler, ScreenHandlerBehaviour, + ScreenHandlerFactory, ScreenHandlerListener, }; use pumpkin_inventory::sync_handler::SyncHandler; use pumpkin_macros::send_cancellable; @@ -788,6 +788,7 @@ pub struct Player { pub current_screen_handler: Mutex>>, pub screen_handler_sync_id: AtomicU8, pub screen_handler_listener: Arc, + pub inventory_changed: Arc, pub screen_handler_sync_handler: Arc, pub tab_list_header: Mutex, pub tab_list_footer: Mutex, @@ -898,7 +899,7 @@ impl Player { Some(skin) } - #[expect(clippy::too_many_lines)] + #[expect(clippy::too_many_lines, clippy::items_after_statements)] pub async fn new( client: Arc, gameprofile: GameProfile, @@ -906,9 +907,21 @@ impl Player { world: Arc, gamemode: GameMode, ) -> Self { - struct ScreenListener; + let inventory_changed = Arc::new(AtomicBool::new(true)); - impl ScreenHandlerListener for ScreenListener {} + struct ScreenListener(Arc); + + impl ScreenHandlerListener for ScreenListener { + fn on_slot_update<'a>( + &'a self, + _screen_handler: &'a ScreenHandlerBehaviour, + _slot: u8, + _stack: ItemStack, + ) -> BoxFuture<'a, ()> { + self.0.store(true, Ordering::Relaxed); + Box::pin(async {}) + } + } let server = world.server.upgrade().unwrap_or_else(|| { tracing::error!("server inactive"); @@ -927,6 +940,16 @@ impl Player { matches!(gamemode, GameMode::Creative | GameMode::Spectator), Ordering::Relaxed, ); + living_entity + .entity + .no_physics + .store(gamemode == GameMode::Spectator, Ordering::Relaxed); + if gamemode == GameMode::Spectator { + living_entity + .entity + .on_ground + .store(false, Ordering::Relaxed); + } let inventory = Arc::new(PlayerInventory::new( living_entity.entity_equipment.clone(), @@ -1058,7 +1081,8 @@ impl Player { player_screen_handler: player_screen_handler.clone(), current_screen_handler: Mutex::new(player_screen_handler), screen_handler_sync_id: AtomicU8::new(0), - screen_handler_listener: Arc::new(ScreenListener), + screen_handler_listener: Arc::new(ScreenListener(inventory_changed.clone())), + inventory_changed, screen_handler_sync_handler: Arc::new(SyncHandler::new()), tab_list_header: Mutex::new(TextComponent::text("")), tab_list_footer: Mutex::new(TextComponent::text("")), @@ -1227,8 +1251,7 @@ impl Player { pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, 1, - ) - .await; + ); let inventory = self.ender_chest_inventory(); self.open_handled_screen( &crate::block::blocks::ender_chest::EnderChestScreenFactory { @@ -1336,7 +1359,7 @@ impl Player { let config = &server.advanced_config.pvp; let inventory = self.inventory(); - let item_stack = inventory.held_item().await; + let item_stack = inventory.held_item(); let base_damage = self .living_entity @@ -1429,21 +1452,19 @@ impl Player { if let Some(strength) = self .living_entity .get_effect(&pumpkin_data::effect::StatusEffect::STRENGTH) - .await { damage += 3.0 * (f64::from(strength.amplifier) + 1.0); } if let Some(weakness) = self .living_entity .get_effect(&pumpkin_data::effect::StatusEffect::WEAKNESS) - .await { damage -= 4.0 * (f64::from(weakness.amplifier) + 1.0); } damage = damage.max(0.0); let pos = victim_entity.pos.load(); - let attack_type = AttackType::new(self, attack_cooldown_progress as f32).await; + let attack_type = AttackType::new(self, attack_cooldown_progress as f32); if matches!(attack_type, AttackType::Critical) { damage *= 1.5; @@ -1455,21 +1476,18 @@ impl Player { damage += 1.5 * f64::from(fall_distance); } - if !victim - .damage_with_context( - &*victim, - damage as f32, - if is_mace_smash { - DamageType::MACE_SMASH - } else { - DamageType::PLAYER_ATTACK - }, - None, - Some(self), - Some(self), - ) - .await - { + if !victim.damage_with_context( + &*victim, + damage as f32, + if is_mace_smash { + DamageType::MACE_SMASH + } else { + DamageType::PLAYER_ATTACK + }, + None, + Some(self), + Some(self), + ) { world.play_sound( Sound::EntityPlayerAttackNodamage, SoundCategory::Players, @@ -1479,7 +1497,7 @@ impl Player { } if damage >= 100.0 { - self.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::DealtOverkillDamage).await; + self.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::DealtOverkillDamage); } if let Some(enchantments) = item_stack.get_data_component::() { @@ -1545,16 +1563,14 @@ impl Player { if other_victim.get_entity().entity_id != victim_entity.entity_id && other_victim.get_entity().entity_id != attacker_entity.entity_id { - other_victim - .damage_with_context( - other_victim.as_ref(), - sweep_damage, - DamageType::PLAYER_ATTACK, - None, - Some(self), - Some(self), - ) - .await; + other_victim.damage_with_context( + other_victim.as_ref(), + sweep_damage, + DamageType::PLAYER_ATTACK, + None, + Some(self), + Some(self), + ); } } } @@ -1668,8 +1684,7 @@ impl Player { statistics::StatisticCategory::Broken, updated_stack.item.id as i32, 1, - ) - .await; + ); self.world().send_entity_status( &self.living_entity.entity, super::equipment_break_status(slot), @@ -1713,7 +1728,6 @@ impl Player { let damage = self .inventory() .held_item() - .await .get_data_component::() .map_or(0, |tool| tool.damage_per_block as i32); @@ -1882,13 +1896,11 @@ impl Player { let new_charges = charges - 1; let mut new_props = anchor_props; new_props.charges = new_charges; - world - .set_block_state( - pos, - new_props.to_state_id(block), - pumpkin_world::world::BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + pos, + new_props.to_state_id(block), + pumpkin_world::world::BlockFlags::NOTIFY_ALL, + ); return Some(CalculatedRespawnPoint { position: spawn_pos, @@ -2012,11 +2024,10 @@ impl Player { /// Check if a position is valid for respawning (vanilla Dismounting.findRespawnPos logic). /// Returns the spawn position if valid, None otherwise. fn find_respawn_pos(world: &Arc, pos: &BlockPos) -> Option> { - let state = world.get_block_state(pos); + let (block, state) = world.get_block_and_state(pos); let below_state = world.get_block_state(&pos.down()); // Check if block at position is invalid for spawn (e.g., inside solid block) - let block = world.get_block(pos); if block.has_tag(&tag::Block::MINECRAFT_INVALID_SPAWN_INSIDE) { return None; } @@ -2079,15 +2090,14 @@ impl Player { self.sleeping_since.store(Some(0)); } - pub async fn get_off_ground_speed(&self) -> f64 { + pub fn get_off_ground_speed(&self) -> f64 { let sprinting = self.get_entity().is_sprinting(); - if !self.get_entity().has_vehicle().await { - let fly_speed = { - let abilities = self.abilities.lock().await; - - abilities.flying.then_some(f64::from(abilities.fly_speed)) - }; + if !self.get_entity().has_vehicle() { + let fly_speed = + self.abilities.try_lock().ok().and_then(|abilities| { + abilities.flying.then_some(f64::from(abilities.fly_speed)) + }); if let Some(flying) = fly_speed { return if sprinting { flying * 2.0 } else { flying }; @@ -2097,9 +2107,8 @@ impl Player { if sprinting { 0.025_999_999 } else { 0.02 } } - pub async fn is_flying(&self) -> bool { - let abilities = self.abilities.lock().await; - abilities.flying + pub fn is_flying(&self) -> bool { + self.abilities.try_lock().is_ok_and(|a| a.flying) } fn is_sleeping(&self) -> bool { @@ -2107,7 +2116,7 @@ impl Player { self.sleeping_since.load().is_some() } - async fn is_swimming(&self, flying: bool) -> bool { + fn is_swimming(&self, flying: bool) -> bool { let entity = self.get_entity(); let touching_water = entity.touching_water.load(Ordering::Relaxed); let can_start_swimming = entity.water_height.load() > self.living_entity.get_swim_height(); @@ -2117,7 +2126,7 @@ impl Player { && entity.is_sprinting() && !entity.on_ground.load(Ordering::Relaxed) && !flying - && !entity.has_vehicle().await + && !entity.has_vehicle() } const fn is_auto_spin_attack() -> bool { @@ -2136,15 +2145,15 @@ impl Player { .is_space_empty(aabb.contract_all(1.0E-7)) } - pub async fn update_player_pose(&self) { + pub fn update_player_pose(&self) { let entity = self.get_entity(); if !self.can_fit_pose(EntityPose::Swimming) { return; } - let flying = self.is_flying().await; - let swimming = self.is_swimming(flying).await; - entity.set_swimming(swimming).await; + let flying = self.is_flying(); + let swimming = self.is_swimming(flying); + entity.set_swimming(swimming); let desired_pose = if self.is_sleeping() { EntityPose::Sleeping } else if swimming { @@ -2160,7 +2169,7 @@ impl Player { }; let new_pose = if self.gamemode.load() == GameMode::Spectator - || entity.has_vehicle().await + || entity.has_vehicle() || self.can_fit_pose(desired_pose) { desired_pose @@ -2175,9 +2184,9 @@ impl Player { } } - pub async fn wake_up(&self) { + pub fn wake_up(&self) { let world = self.world(); - let respawn_point = self.respawn_point.lock().await; + let respawn_point = self.respawn_point.try_lock().ok().and_then(|r| r.clone()); let Some(respawn_point) = respawn_point.as_ref() else { warn!("Player waking up should have it's respawn point set on the bed"); return; @@ -2191,11 +2200,11 @@ impl Player { player_arc, respawn_point.position, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } let (bed, bed_state) = world.get_block_and_state_id(&respawn_point.position); - BedBlock::set_occupied(false, &world, bed, &respawn_point.position, bed_state).await; + BedBlock::set_occupied(false, &world, bed, &respawn_point.position, bed_state); self.living_entity.entity.set_pose(EntityPose::Standing); self.living_entity.entity.set_pos(self.position()); @@ -2211,8 +2220,7 @@ impl Player { statistics::StatisticCategory::Custom, statistics::CustomStatistic::TimeSinceRest as i32, 0, - ) - .await; + ); let chunk_pos = self.living_entity.entity.chunk_pos.load(); world.broadcast_to_chunk( @@ -2351,7 +2359,14 @@ impl Player { } #[expect(clippy::too_many_lines)] - pub async fn tick(self: &Arc, server: &Server) { + pub fn tick<'a>(&'a self, server: &'a Server) { + if self.is_spectator() { + self.living_entity + .entity + .on_ground + .store(false, Ordering::Relaxed); + } + if let Some(camera_id) = self.camera_target_id.load() { if camera_id == self.entity_id() { self.camera_target_id.store(None); @@ -2366,40 +2381,49 @@ impl Player { let player_pos = self.living_entity.entity.pos.load(); if player_pos != target_pos { self.living_entity.entity.set_pos(target_pos); - crate::world::chunker::update_position(self).await; + if let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) { + tokio::spawn(async move { + crate::world::chunker::update_position(&p).await; + }); + } } } else { // Target no longer exists, reset camera back to player self.camera_target_id.store(None); - self.send_client_packet(&CSetCamera::new(self.entity_id().into())) - .await; + self.try_send_client_packet(&CSetCamera::new(self.entity_id().into())); } } } - let current_screen_handler = self.current_screen_handler.lock().await.clone(); - let invalid_merchant = { - let screen_handler = current_screen_handler.lock().await; - screen_handler.as_any().is::() - && !screen_handler.can_use(self.as_ref()) - }; - if invalid_merchant { - self.close_handled_screen().await; - } else { - current_screen_handler - .lock() - .await - .send_content_updates() - .await; + if let Ok(current_screen_handler_guard) = self.current_screen_handler.try_lock() { + let current_screen_handler = current_screen_handler_guard.clone(); + drop(current_screen_handler_guard); + let is_invalid = current_screen_handler + .try_lock() + .is_ok_and(|screen_handler| { + screen_handler.as_any().is::() + && !screen_handler.can_use(self) + }); + + if is_invalid { + if let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) { + tokio::spawn(async move { + p.close_handled_screen().await; + }); + } + } else { + tokio::spawn(async move { + current_screen_handler + .lock() + .await + .send_content_updates() + .await; + }); + } } - // if self.client.closed.load(Ordering::Relaxed) { - // return; - // } - // Statistics updates - { - let mut stats = self.stats.lock().await; + if let Ok(mut stats) = self.stats.try_lock() { stats.increment_custom(statistics::CustomStatistic::PlayTime, 1); stats.increment_custom(statistics::CustomStatistic::TotalWorldTime, 1); stats.increment_custom(statistics::CustomStatistic::TimeSinceDeath, 1); @@ -2409,32 +2433,33 @@ impl Player { } } + if let Ok(mut xp) = self.experience_pick_up_delay.try_lock() + && *xp > 0 { - let mut xp = self.experience_pick_up_delay.lock().await; - if *xp > 0 { - *xp -= 1; - } + *xp -= 1; } - let (chunk_of_chunks, total_sent_chunks) = { - let mut chunk_manager = self.chunk_manager.lock().await; - chunk_manager.pull_new_chunks(); - let chunks = if let ClientPlatform::Java(java_client) = self.client.as_ref() { - if java_client.version.load() >= JavaMinecraftVersion::V_1_20_2 { - // Java clients (1.20.2+) use the chunk batching protocol. - // If we have sent too many chunks without receiving an ack, we stop sending chunks. - chunk_manager - .can_send_chunk() - .then(|| chunk_manager.next_chunk()) + let (chunk_of_chunks, total_sent_chunks) = self.chunk_manager.try_lock().map_or_else( + |_| (None, 0), + |mut chunk_manager| { + chunk_manager.pull_new_chunks(); + let chunks = if let ClientPlatform::Java(java_client) = self.client.as_ref() { + if java_client.version.load() >= JavaMinecraftVersion::V_1_20_2 { + // Java clients (1.20.2+) use the chunk batching protocol. + // If we have sent too many chunks without receiving an ack, we stop sending chunks. + chunk_manager + .can_send_chunk() + .then(|| chunk_manager.next_chunk()) + } else { + // Java clients < 1.20.2 do not have chunk batching/ack packets. + // Send chunks every tick directly based on chunks_per_tick. + (!chunk_manager.chunk_queue.is_empty()).then(|| chunk_manager.next_chunk()) + } } else { - // Java clients < 1.20.2 do not have chunk batching/ack packets. - // Send chunks every tick directly based on chunks_per_tick. (!chunk_manager.chunk_queue.is_empty()).then(|| chunk_manager.next_chunk()) - } - } else { - (!chunk_manager.chunk_queue.is_empty()).then(|| chunk_manager.next_chunk()) - }; - (chunks, chunk_manager.sent_chunks_count()) - }; + }; + (chunks, chunk_manager.sent_chunks_count()) + }, + ); if let Some(chunk_of_chunks) = chunk_of_chunks && !chunk_of_chunks.is_empty() { @@ -2447,14 +2472,13 @@ impl Player { && total_sent_chunks > 4 { if let Ok(data) = bedrock_client.serialize_packet(&CPlayStatus::PlayerSpawn) { - bedrock_client.enqueue_packet(data).await; + bedrock_client.try_enqueue_packet(data); } self.bedrock_spawned.store(true, Ordering::Relaxed); self.set_client_loaded(true); - self.send_health().await; + self.send_health(); if self.living_entity.health.load() <= 0.0 { - self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn) - .await; + self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn); } } } @@ -2469,73 +2493,77 @@ impl Player { self.sleeping_since.store(Some(sleeping_since + 1)); } - if self.mining.load(Ordering::Relaxed) { - let pos = *self.mining_pos.lock().await; - let world = self.world(); - let state = world.get_block_state(&pos); - // Is the block broken? - if state.is_air() { - self.stop_mining().await; - } else { - let finished = self - .continue_mining( - pos, - &world, - state, - self.start_mining_time.load(Ordering::Relaxed), - ) - .await; - if finished && matches!(self.client.as_ref(), ClientPlatform::Bedrock(_)) { - self.stop_mining().await; + let player_arc = self.world().get_player_by_uuid(self.gameprofile.id); + if self.mining.load(Ordering::Relaxed) + && let Some(p) = player_arc.clone() + { + let world_clone = p.world(); + let server_clone = world_clone.server.upgrade(); + tokio::spawn(async move { + let pos = *p.mining_pos.lock().await; + let world = p.world(); + let state = world.get_block_state(&pos); + // Is the block broken? + if state.is_air() { + p.stop_mining().await; + } else { + let finished = p + .continue_mining( + pos, + &world, + state, + p.start_mining_time.load(Ordering::Relaxed), + ) + .await; + if finished && matches!(p.client.as_ref(), ClientPlatform::Bedrock(_)) { + p.stop_mining().await; - let block = Block::from_state_id(state.id); - let can_harvest = self.can_harvest(state, block).await; - let flags = if can_harvest { - pumpkin_world::world::BlockFlags::NOTIFY_NEIGHBORS - } else { - pumpkin_world::world::BlockFlags::SKIP_DROPS - | pumpkin_world::world::BlockFlags::NOTIFY_NEIGHBORS - }; - if world - .break_block(&pos, Some(self.clone()), flags) - .await - .is_some() - { - server - .block_registry - .broken(&world, block, self, &pos, server, state) - .await; - self.apply_tool_damage_for_block_break(state).await; - if can_harvest { - self.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; + let block = Block::from_state_id(state.id); + let can_harvest = p.can_harvest(state, block); + let flags = if can_harvest { + pumpkin_world::world::BlockFlags::NOTIFY_NEIGHBORS + } else { + pumpkin_world::world::BlockFlags::SKIP_DROPS + | pumpkin_world::world::BlockFlags::NOTIFY_NEIGHBORS + }; + if world.break_block(&pos, Some(p.clone()), flags).is_some() { + if let Some(server) = server_clone { + server + .block_registry + .broken(&world, block, &p, &pos, &server, state); + } + p.apply_tool_damage_for_block_break(state).await; + if can_harvest { + p.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; + } } } } - } + }); } self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed); - // Player.aiStep resets fall distance while flying before the normal - // living-entity tick, unless the player is riding another entity. - let flying = self.abilities.lock().await.flying; - if flying && !self.living_entity.entity.has_vehicle().await { - self.living_entity.fall_distance.store(0.0); + if let Some(ref p_arc) = player_arc { + let caller: Arc = p_arc.clone(); + self.living_entity.tick(&caller, server); + self.breath_manager.tick(p_arc); + self.hunger_manager.tick(p_arc); } - 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; - self.hunger_manager.tick(self).await; - self.check_inventory_advancements().await; - self.advancements.lock().await.flush_dirty(self, true); + self.update_player_pose(); + self.check_inventory_advancements(); + if let Some(ref p_arc) = player_arc + && let Ok(mut adv) = self.advancements.try_lock() + { + adv.flush_dirty(p_arc, true); + } // experience handling - self.tick_experience().await; - self.tick_health().await; - self.tick_raid_omen().await; - self.tick_maps(server).await; + self.tick_experience(); + self.tick_health(); + self.tick_raid_omen(); + self.tick_maps(server); // Anti-spam counter decay let anti_spam = &server.advanced_config.chat.anti_spam; @@ -2554,16 +2582,21 @@ impl Player { let idle_timeout_minutes = server.player_idle_timeout.load(Ordering::Relaxed); if idle_timeout_minutes > 0 { let idle_duration = now.duration_since(self.last_action_time.load()); - if idle_duration >= Duration::from_secs(idle_timeout_minutes as u64 * 60) { - self.kick( - DisconnectReason::KickedForIdle, - TextComponent::translate_cross( - translation::java::MULTIPLAYER_DISCONNECT_IDLING, - translation::java::MULTIPLAYER_DISCONNECT_IDLING, - [], - ), - ) - .await; + if idle_duration >= Duration::from_secs(idle_timeout_minutes as u64 * 60) + && let Some(ref p_arc) = player_arc + { + let p = p_arc.clone(); + tokio::spawn(async move { + p.kick( + DisconnectReason::KickedForIdle, + TextComponent::translate_cross( + translation::java::MULTIPLAYER_DISCONNECT_IDLING, + translation::java::MULTIPLAYER_DISCONNECT_IDLING, + [], + ), + ) + .await; + }); } } } @@ -2576,7 +2609,7 @@ impl Player { starting_time: i32, ) -> bool { let time = self.tick_counter.load(Ordering::Relaxed) - starting_time; - let speed = block::calc_block_breaking(self, state, Block::from_state_id(state.id)).await; + let speed = block::calc_block_breaking(self, state, Block::from_state_id(state.id)); let total_progress = speed * (time + 1) as f32; let stage = (total_progress * 10.0) as i32; let stage = stage.min(9); @@ -2641,6 +2674,11 @@ impl Player { } } + #[must_use] + pub fn is_spectator(&self) -> bool { + self.gamemode.load() == GameMode::Spectator + } + #[must_use] pub fn supports_player_loaded(&self) -> bool { match self.client.as_ref() { @@ -2882,17 +2920,16 @@ impl Player { } } - pub async fn increment_stat( - &self, - category: statistics::StatisticCategory, - stat: i32, - amount: i32, - ) { - self.stats.lock().await.increment(category, stat, amount); + pub fn increment_stat(&self, category: statistics::StatisticCategory, stat: i32, amount: i32) { + if let Ok(mut stats) = self.stats.try_lock() { + stats.increment(category, stat, amount); + } } - pub async fn set_stat(&self, category: statistics::StatisticCategory, stat: i32, value: i32) { - self.stats.lock().await.set(category, stat, value); + pub fn set_stat(&self, category: statistics::StatisticCategory, stat: i32, value: i32) { + if let Ok(mut stats) = self.stats.try_lock() { + stats.set(category, stat, value); + } } pub async fn get_stat(&self, category: statistics::StatisticCategory, stat: i32) -> i32 { @@ -2904,19 +2941,17 @@ impl Player { .await } - pub async fn set_custom_stat(&self, stat: statistics::CustomStatistic, value: i32) { - self.set_stat(statistics::StatisticCategory::Custom, stat as i32, value) - .await; + pub fn set_custom_stat(&self, stat: statistics::CustomStatistic, value: i32) { + self.set_stat(statistics::StatisticCategory::Custom, stat as i32, value); } - pub async fn increment_custom_stat(&self, stat: statistics::CustomStatistic, amount: i32) { - self.increment_stat(statistics::StatisticCategory::Custom, stat as i32, amount) - .await; + pub fn increment_custom_stat(&self, stat: statistics::CustomStatistic, amount: i32) { + self.increment_stat(statistics::StatisticCategory::Custom, stat as i32, amount); } pub async fn get_movement_statistic(&self) -> statistics::CustomStatistic { let entity = self.get_entity(); - if entity.has_vehicle().await { + if entity.has_vehicle() { let vehicle = entity.vehicle.lock().await; if let Some(vehicle) = vehicle.as_ref() { let entity_type = vehicle.get_entity().entity_type; @@ -2959,7 +2994,7 @@ impl Player { } } - if self.is_flying().await { + if self.is_flying() { return statistics::CustomStatistic::FlyOneCm; } @@ -3049,36 +3084,40 @@ impl Player { lock.game_rules.advance_time }; - let l_world = world.level_time.lock().await; - if let Some((custom_time, relative)) = self.per_player_time.load() { - let time_of_day = if relative { - (l_world.time_of_day as u64 + custom_time) as i64 - } else { - custom_time as i64 - }; - let paused = l_world.paused || !advance_time; - let rate = if paused { 0.0 } else { l_world.rate }; - self.client - .enqueue_packet_editioned( - &CUpdateTime::new_clock( + let (clock_packet, time_packet) = { + let l_world = world + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some((custom_time, relative)) = self.per_player_time.load() { + let time_of_day = if relative { + (l_world.time_of_day as u64 + custom_time) as i64 + } else { + custom_time as i64 + }; + let paused = l_world.paused || !advance_time; + let rate = if paused { 0.0 } else { l_world.rate }; + ( + CUpdateTime::new_clock( l_world.world_age, 0, time_of_day, l_world.partial_tick, rate, ), - &CSetTime::new(time_of_day as _), + CSetTime::new(time_of_day as _), ) - .await; - return; - } + } else { + let (total_ticks, partial_tick, rate) = l_world.pack_network_state(advance_time); + ( + CUpdateTime::new_clock(l_world.world_age, 0, total_ticks, partial_tick, rate), + CSetTime::new(l_world.query_daytime() as _), + ) + } + }; - let (total_ticks, partial_tick, rate) = l_world.pack_network_state(advance_time); self.client - .enqueue_packet_editioned( - &CUpdateTime::new_clock(l_world.world_age, 0, total_ticks, partial_tick, rate), - &CSetTime::new(l_world.query_daytime() as _), - ) + .enqueue_packet_editioned(&clock_packet, &time_packet) .await; } @@ -3165,6 +3204,14 @@ impl Player { .await; } + pub fn try_send_client_packet(&self, packet: &C) { + if let ClientPlatform::Java(client) = self.client.as_ref() + && let Ok(data) = client.serialize_packet(packet) + { + client.try_enqueue_packet(data); + } + } + pub async fn send_client_packet(&self, packet: &C) { if let ClientPlatform::Java(client) = self.client.as_ref() && let Ok(data) = client.serialize_packet(packet) @@ -3323,19 +3370,19 @@ impl Player { self.get_saturation() } - pub async fn set_food_saturation(&self, saturation: f32) { - self.set_saturation(saturation).await; + pub fn set_food_saturation(&self, saturation: f32) { + self.set_saturation(saturation); } pub fn get_food_exhaustion(&self) -> f32 { self.get_exhaustion() } - pub async fn set_food_exhaustion(&self, exhaustion: f32) { - self.set_exhaustion(exhaustion).await; + pub fn set_food_exhaustion(&self, exhaustion: f32) { + self.set_exhaustion(exhaustion); } - pub async fn get_target_block( + pub fn get_target_block( &self, world: &Arc, max_distance: f64, @@ -3353,11 +3400,7 @@ impl Player { pitch_rad.cos() * yaw_rad.sin(), ); let end_pos = eye_pos + dir * max_distance; - let res = world - .raycast(eye_pos, end_pos, async |pos, w| { - !w.get_block_state(pos).is_air() - }) - .await; + let res = world.raycast(eye_pos, end_pos, |pos, w| !w.get_block_state(pos).is_air()); res.map(|(pos, _)| pos) } @@ -3433,11 +3476,11 @@ impl Player { if new_world.dimension == pumpkin_data::dimension::Dimension::THE_NETHER { self.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::EnterDimension { dimension: "the_nether".to_string(), - }).await; + }); } else if new_world.dimension == pumpkin_data::dimension::Dimension::THE_END { self.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::EnterDimension { dimension: "the_end".to_string(), - }).await; + }); } let last_pos = self.living_entity.entity.last_pos.load(); @@ -3504,7 +3547,7 @@ impl Player { self.on_screen_handler_opened(self.player_screen_handler.clone()).await; - self.send_health().await; + self.send_health(); new_world.send_world_info(&player, position, yaw, pitch).await; } @@ -3694,36 +3737,52 @@ impl Player { .add_exhaustion(exhaustion_event.exhaustion); } - pub async fn heal(&self, additional_health: f32) { + pub fn heal(&self, additional_health: f32) { self.living_entity.heal(additional_health); - self.send_health().await; + self.send_health(); } - pub async fn damage( + pub fn damage( &self, caller: &dyn crate::entity::EntityBase, amount: f32, damage_type: pumpkin_data::damage::DamageType, ) -> bool { - self.living_entity.damage(caller, amount, damage_type).await + self.damage_with_context(caller, amount, damage_type, None, None, None) } - pub async fn damage_generic(&self, amount: f32) -> bool { - use pumpkin_data::damage::DamageType; + pub fn damage_with_context( + &self, + caller: &dyn crate::entity::EntityBase, + amount: f32, + damage_type: pumpkin_data::damage::DamageType, + position: Option>, + source: Option<&dyn crate::entity::EntityBase>, + cause: Option<&dyn crate::entity::EntityBase>, + ) -> bool { + if self.abilities.try_lock().is_ok_and(|a| a.invulnerable) + && damage_type != pumpkin_data::damage::DamageType::GENERIC_KILL + && damage_type != pumpkin_data::damage::DamageType::OUT_OF_WORLD + { + return false; + } self.living_entity - .damage(self, amount, DamageType::GENERIC) - .await + .damage_with_context(caller, amount, damage_type, position, source, cause) } - pub async fn kill(&self) { + pub fn damage_generic(&self, amount: f32) -> bool { + use pumpkin_data::damage::DamageType; + self.living_entity.damage(self, amount, DamageType::GENERIC) + } + + pub fn kill(&self) { use pumpkin_data::damage::DamageType; let health = self.living_entity.health.load(); self.living_entity - .damage(self, health + 10.0, DamageType::OUT_OF_WORLD) - .await; + .damage(self, health + 10.0, DamageType::OUT_OF_WORLD); } - pub async fn send_health(&self) { + pub fn send_health(&self) { if !self.has_client_loaded() { return; } @@ -3740,7 +3799,7 @@ impl Player { modifiers: Vec::new(), }; - self.enqueue_packet_editioned( + self.try_enqueue_packet_editioned( &CSetHealth::new( self.living_entity.health.load(), self.hunger_manager.level.load().into(), @@ -3770,29 +3829,28 @@ impl Player { ], tick: VarULong(self.tick_counter.load(Ordering::Relaxed).max(0) as u64), }, - ) - .await; + ); } - async fn send_bedrock_respawn_state(&self, state: RespawnState) { + fn send_bedrock_respawn_state(&self, state: RespawnState) { if let ClientPlatform::Bedrock(client) = self.client.as_ref() { let entity = self.get_entity(); let position = entity.pos.load(); - client - .send_packet(&SBedrockRespawn { - position: Vector3::new( - position.x as f32, - position.y as f32 + entity.entity_type.eye_height, - position.z as f32, - ), - state, - player_runtime_id: VarULong(self.entity_id() as u64), - }) - .await; + if let Ok(data) = client.serialize_packet(&SBedrockRespawn { + position: Vector3::new( + position.x as f32, + position.y as f32 + entity.entity_type.eye_height, + position.z as f32, + ), + state, + player_runtime_id: VarULong(self.entity_id() as u64), + }) { + client.try_enqueue_packet(data); + } } } - pub async fn tick_health(&self) { + pub fn tick_health(&self) { if !self.has_client_loaded() { return; } @@ -3810,17 +3868,17 @@ impl Player { self.last_sent_food.store(food, Ordering::Relaxed); self.last_food_saturation .store(saturation == 0.0, Ordering::Relaxed); - self.send_health().await; + self.send_health(); } } - pub async fn tick_raid_omen(&self) { + pub fn tick_raid_omen(&self) { if self.is_spectator() { return; } - if let Some(bad_omen) = self.get_effect(&StatusEffect::BAD_OMEN).await - && !self.has_effect(&StatusEffect::RAID_OMEN).await + if let Some(bad_omen) = self.get_effect(&StatusEffect::BAD_OMEN) + && !self.has_effect(&StatusEffect::RAID_OMEN) { let world = self.world(); let player_pos = self.living_entity.entity.block_pos.load(); @@ -3829,7 +3887,7 @@ impl Player { let village_pos = world .villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .get_nearest_job_site(player_pos, 64) .or_else(|| { world.raids.try_lock().ok().and_then(|raids| { @@ -3840,33 +3898,34 @@ impl Player { }); if let Some(pos) = village_pos { - self.living_entity - .remove_effect(&StatusEffect::BAD_OMEN) - .await; - self.set_raid_omen_position(pos); - let effect = Effect { - effect_type: &StatusEffect::RAID_OMEN, - duration: 600, - amplifier: bad_omen.amplifier, - ambient: false, - show_particles: true, - show_icon: true, - blend: true, - }; - self.add_effect(effect).await; + if let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) { + let bad_omen_amplifier = bad_omen.amplifier; + p.living_entity.remove_effect(&StatusEffect::BAD_OMEN); + p.set_raid_omen_position(pos); + let effect = Effect { + effect_type: &StatusEffect::RAID_OMEN, + duration: 600, + amplifier: bad_omen_amplifier, + ambient: false, + show_particles: true, + show_icon: true, + blend: true, + }; + p.add_effect(effect); + } world.play_sound(Sound::BlockBellResonate, SoundCategory::Neutral, &pos_f64); } } } - pub async fn set_health(&self, health: f32) { + pub fn set_health(&self, health: f32) { self.living_entity.set_health(health); - self.send_health().await; + self.send_health(); } - pub async fn set_max_health(&self, max_health: f32) { - self.living_entity.set_max_health(max_health).await; - self.send_health().await; + pub fn set_max_health(&self, max_health: f32) { + self.living_entity.set_max_health(max_health); + self.send_health(); } pub fn get_food_level(&self) -> u8 { @@ -3886,16 +3945,16 @@ impl Player { return; } self.hunger_manager.set_level(food_event.food_level); - self.send_health().await; + self.send_health(); } pub fn get_saturation(&self) -> f32 { self.hunger_manager.saturation.load() } - pub async fn set_saturation(&self, saturation: f32) { + pub fn set_saturation(&self, saturation: f32) { self.hunger_manager.set_saturation(saturation); - self.send_health().await; + self.send_health(); } pub async fn set_allow_flight(&self, allow: bool) { @@ -3904,6 +3963,9 @@ impl Player { } pub async fn set_flying(&self, flying: bool) { + if flying { + self.living_entity.fall_distance.store(0.0); + } self.abilities.lock().await.flying = flying; self.send_abilities_update().await; } @@ -3927,17 +3989,17 @@ impl Player { self.hunger_manager.get_exhaustion() } - pub async fn set_exhaustion(&self, exhaustion: f32) { + pub fn set_exhaustion(&self, exhaustion: f32) { self.hunger_manager.set_exhaustion(exhaustion); - self.send_health().await; + self.send_health(); } pub fn get_absorption(&self) -> f32 { self.living_entity.get_absorption() } - pub async fn set_absorption(&self, absorption: f32) { - self.living_entity.set_absorption(absorption).await; + pub fn set_absorption(&self, absorption: f32) { + self.living_entity.set_absorption(absorption); } pub fn get_ip(&self) -> String { @@ -4082,21 +4144,25 @@ impl Player { } } + #[allow(dead_code)] async fn handle_killed(&self, death_msg: TextComponent) { self.trigger_advancement( crate::entity::player::advancement::trigger::AdvancementTrigger::PlayerKilled, - ) - .await; + ); let block_pos = self.position().to_block_pos(); let keep_inventory = { self.world().level_info.load().game_rules.keep_inventory }; if !keep_inventory { - let mut main_inv = self.inventory().main_inventory.write().await; + let mut main_inv = self + .inventory() + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); for item in main_inv.iter_mut() { if !item.is_empty() { let stack = std::mem::replace(item, ItemStack::EMPTY.clone()); - self.world().drop_stack(&block_pos, stack).await; + self.world().drop_stack(&block_pos, stack); } } } @@ -4118,10 +4184,8 @@ impl Player { }, ) .await; - self.send_health().await; - - self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn) - .await; + self.send_health(); + self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn); } pub async fn set_gamemode(self: &Arc, gamemode: GameMode) -> bool { @@ -4162,7 +4226,7 @@ impl Player { if gamemode == GameMode::Creative { self.get_entity().extinguish(); - self.get_entity().set_on_fire(false).await; + self.get_entity().set_on_fire(false); } // Stop elytra flight and reset sneaking when switching to spectator mode @@ -4174,6 +4238,8 @@ impl Player { if entity.is_sneaking() { entity.set_sneaking(false).await; } + entity.on_ground.store(false, Ordering::Relaxed); + self.living_entity.fall_distance.store(0.0); } if gamemode != GameMode::Spectator && self.camera_target_id.load().is_some() { @@ -4187,6 +4253,10 @@ impl Player { matches!(gamemode, GameMode::Creative | GameMode::Spectator), Ordering::Relaxed, ); + self.living_entity.entity.no_physics.store( + gamemode == GameMode::Spectator, + Ordering::Relaxed, + ); self.living_entity .entity .world @@ -4245,32 +4315,20 @@ impl Player { ); } - pub async fn can_harvest(&self, state: &BlockState, block: &'static Block) -> bool { - !state.tool_required() - || self - .inventory() - .held_item() - .await - .is_correct_for_drops(block) + pub fn can_harvest(&self, state: &BlockState, block: &'static Block) -> bool { + !state.tool_required() || self.inventory().held_item().is_correct_for_drops(block) } - pub async fn get_mining_speed(&self, block: &'static Block) -> f32 { - let mut speed = self.inventory().held_item().await.get_speed(block); + pub fn get_mining_speed(&self, block: &'static Block) -> f32 { + let mut speed = self.inventory().held_item().get_speed(block); // Haste - if self.living_entity.has_effect(&StatusEffect::HASTE).await - || self - .living_entity - .has_effect(&StatusEffect::CONDUIT_POWER) - .await + if self.living_entity.has_effect(&StatusEffect::HASTE) + || self.living_entity.has_effect(&StatusEffect::CONDUIT_POWER) { - speed *= ((self.get_haste_amplifier().await + 1) as f32).mul_add(0.2, 1.0); + speed *= ((self.get_haste_amplifier() + 1) as f32).mul_add(0.2, 1.0); } // Fatigue - if let Some(fatigue) = self - .living_entity - .get_effect(&StatusEffect::MINING_FATIGUE) - .await - { + if let Some(fatigue) = self.living_entity.get_effect(&StatusEffect::MINING_FATIGUE) { let fatigue_speed = match fatigue.amplifier { 0 => 0.3, 1 => 0.09, @@ -4286,17 +4344,13 @@ impl Player { speed } - async fn get_haste_amplifier(&self) -> u32 { + fn get_haste_amplifier(&self) -> u32 { let mut i = 0; let mut j = 0; - if let Some(effect) = self.living_entity.get_effect(&StatusEffect::HASTE).await { + if let Some(effect) = self.living_entity.get_effect(&StatusEffect::HASTE) { i = effect.amplifier; } - if let Some(effect) = self - .living_entity - .get_effect(&StatusEffect::CONDUIT_POWER) - .await - { + if let Some(effect) = self.living_entity.get_effect(&StatusEffect::CONDUIT_POWER) { j = effect.amplifier; } u32::from(i.max(j)) @@ -4318,19 +4372,17 @@ impl Player { .await; } - pub async fn drop_item(&self, item_stack: ItemStack) { + pub fn drop_item(&self, item_stack: ItemStack) { self.increment_stat( statistics::StatisticCategory::Dropped, item_stack.item.id as i32, item_stack.item_count as i32, - ) - .await; + ); self.increment_stat( statistics::StatisticCategory::Custom, statistics::CustomStatistic::Drop as i32, 1, - ) - .await; + ); let item_pos = self.living_entity.entity.pos.load() + Vector3::new(0.0, self.living_entity.entity.get_eye_height() - 0.3, 0.0); let entity = Entity::new(self.world(), item_pos, &EntityType::ITEM); @@ -4355,11 +4407,11 @@ impl Player { let item_entity = Arc::new(ItemEntity::new_with_velocity( entity, item_stack, velocity, 40, )); - self.world().spawn_entity(item_entity).await; + self.world().spawn_entity(item_entity); } pub async fn drop_held_item(&self, drop_stack: bool) { - let mut item_stack = self.inventory().held_item().await; + let mut item_stack = self.inventory().held_item(); if item_stack.is_empty() { return; @@ -4375,7 +4427,7 @@ impl Player { crate::plugin::api::events::player::player_drop_item::PlayerDropItemEvent::new( player_arc, dropped_stack.item.registry_key.to_string(), - dropped_stack.item_count as u8, + dropped_stack.item_count, ); server.plugin_manager.fire(&server, &mut event).await; if event.cancelled { @@ -4385,9 +4437,9 @@ impl Player { item_stack.decrement(drop_amount); let updated_stack = item_stack.clone(); - self.inventory().set_held_item(updated_stack.clone()).await; + self.inventory().set_held_item(updated_stack.clone()); - self.drop_item(dropped_stack).await; + self.drop_item(dropped_stack); let inv: Arc = self.inventory.clone(); let screen_binding = self.current_screen_handler.lock().await; @@ -4412,7 +4464,7 @@ impl Player { return; } } - let (main_hand_item, off_hand_item) = self.inventory.swap_item().await; + let (main_hand_item, off_hand_item) = self.inventory.swap_item(); let equipment = &[ (EquipmentSlot::MAIN_HAND, main_hand_item), (EquipmentSlot::OFF_HAND, off_hand_item), @@ -4446,7 +4498,7 @@ impl Player { self.enqueue_packet_editioned(&je_packet, &be_packet).await; } - pub async fn tick_experience(&self) { + pub fn tick_experience(&self) { if !self.has_client_loaded() { return; } @@ -4458,28 +4510,28 @@ impl Player { self.last_sent_xp.store(level, Ordering::Relaxed); - self.send_client_packet(&CSetExperience::new( + self.try_send_client_packet(&CSetExperience::new( progress.clamp(0.0, 1.0), level.into(), points.into(), - )) - .await; + )); } } - pub async fn tick_maps(&self, server: &Server) { + pub fn tick_maps(&self, server: &Server) { use pumpkin_data::data_component_impl::MapIdImpl; use pumpkin_data::item::Item; for hand in Hand::all() { - let stack = self.inventory().get_stack_in_hand(hand).await; + let stack = self.inventory().get_stack_in_hand(hand); if stack.item.id == Item::FILLED_MAP.id && let Some(map_id_comp) = stack.get_data_component::() { let map_id = map_id_comp.id; - if let Some(map_data_arc) = server.map_manager.get_map(map_id) { - let mut map_data = map_data_arc.lock().await; + if let Some(map_data_arc) = server.map_manager.get_map(map_id) + && let Ok(mut map_data) = map_data_arc.try_lock() + { map_data.update(self); let tick_count = self.tick_counter.load(Ordering::Relaxed); @@ -4535,15 +4587,14 @@ impl Player { data: &*map_data.colors, }); - self.send_client_packet(&CMapItemData { + self.try_send_client_packet(&CMapItemData { map_id: VarInt(map_id), scale: map_data.scale, tracking_position: true, locked: map_data.locked, icons: Some(&icons), data, - }) - .await; + }); map_data.dirty = false; } } @@ -4570,7 +4621,7 @@ impl Player { self.experience_progress.store(progress.clamp(0.0, 1.0)); self.experience_points.store(points, Ordering::Relaxed); self.last_sent_xp.store(-1, Ordering::Relaxed); - self.tick_experience().await; + self.tick_experience(); if self.has_client_loaded() { self.send_client_packet(&CSetExperience::new( @@ -4603,20 +4654,24 @@ impl Player { self.set_experience(new_level, progress, points).await; } - pub async fn add_effect(&self, effect: Effect) { - self.living_entity.add_effect(effect).await; + pub fn add_effect(&self, effect: Effect) { + self.living_entity.add_effect(effect); } - pub async fn has_effect(&self, effect_type: &'static StatusEffect) -> bool { - self.living_entity.has_effect(effect_type).await + pub fn has_effect(&self, effect_type: &'static StatusEffect) -> bool { + self.living_entity.has_effect(effect_type) } - pub async fn get_effect(&self, effect_type: &'static StatusEffect) -> Option { - self.living_entity.get_effect(effect_type).await + pub fn get_effect(&self, effect_type: &'static StatusEffect) -> Option { + self.living_entity.get_effect(effect_type) } - pub async fn get_active_effects(&self) -> Vec { - let effects = self.living_entity.active_effects.lock().await; + pub fn get_active_effects(&self) -> Vec { + let effects = self + .living_entity + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); effects.values().cloned().collect() } @@ -4634,9 +4689,16 @@ impl Player { } pub async fn send_active_effects(&self) { - let effects = self.living_entity.active_effects.lock().await; - for effect in effects.values() { - self.send_effect(effect.clone()).await; + let effects: Vec<_> = self + .living_entity + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .values() + .cloned() + .collect(); + for effect in effects { + self.send_effect(effect).await; } } @@ -4681,7 +4743,7 @@ impl Player { ) .await; - self.living_entity.remove_effect(effect_type).await + self.living_entity.remove_effect(effect_type) // TODO broadcast metadata } @@ -4689,8 +4751,16 @@ impl Player { pub async fn remove_all_effects(&self) -> bool { let mut succeeded = false; let mut effect_list = vec![]; - for effect in self.living_entity.active_effects.lock().await.keys() { - effect_list.push(*effect); + let effects: Vec<_> = self + .living_entity + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .keys() + .copied() + .collect(); + for effect in effects { + effect_list.push(effect); let effect_id = VarInt(i32::from(effect.id)); self.send_client_packet( &pumpkin_protocol::java::client::play::CRemoveMobEffect::new( @@ -4704,7 +4774,7 @@ impl Player { // Need to remove effects afterward here because there would be a deadlock if this is done in the for loop. for effect in effect_list { - self.living_entity.remove_effect(effect).await; + self.living_entity.remove_effect(effect); } succeeded @@ -5464,7 +5534,7 @@ impl Player { } /// Swing the hand of the player - pub async fn swing_hand(&self, hand: Hand, all: bool) { + pub fn swing_hand(&self, hand: Hand, all: bool) { let world = self.world(); let entity_id = self.entity_id(); @@ -5486,11 +5556,9 @@ impl Player { }; if all { - world.broadcast_editioned(&je_packet, &be_packet).await; + world.broadcast_editioned(&je_packet, &be_packet); } else { - world - .broadcast_packet_except_editioned(&[self.gameprofile.id], &je_packet, &be_packet) - .await; + world.broadcast_packet_except_editioned(&[self.gameprofile.id], &je_packet, &be_packet); } } @@ -5653,26 +5721,35 @@ impl Player { .await } - pub async fn has_advancement( + pub fn has_advancement( &self, advancement: &'static pumpkin_data::advancement::Advancement, ) -> bool { - let advancements = self.advancements.lock().await; - advancements - .progress - .map - .get(advancement) - .is_some_and(crate::entity::player::advancement::AdvancementProgress::is_done) + self.advancements.try_lock().is_ok_and(|advancements| { + advancements + .progress + .map + .get(advancement) + .is_some_and(crate::entity::player::advancement::AdvancementProgress::is_done) + }) } - pub async fn has_item_in_inventory(&self, item: &pumpkin_data::item::Item) -> bool { - let main_inv = self.inventory.main_inventory.read().await; + pub fn has_item_in_inventory(&self, item: &pumpkin_data::item::Item) -> bool { + let main_inv = self + .inventory + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); for stack in main_inv.iter() { if !stack.is_empty() && stack.item.id == item.id { return true; } } - let equipment = self.inventory.entity_equipment.lock().await; + let equipment = self + .inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for stack in equipment.equipment.values() { if !stack.is_empty() && stack.item.id == item.id { return true; @@ -5681,20 +5758,24 @@ impl Player { false } - pub async fn trigger_advancement_criterion( + pub fn trigger_advancement_criterion( &self, advancement: &'static pumpkin_data::advancement::Advancement, criterion: &str, ) { - let mut advancements = self.advancements.lock().await; - advancements.award(advancement, criterion); + if let Ok(mut advancements) = self.advancements.try_lock() { + advancements.award(advancement, criterion); + } } - pub async fn check_inventory_advancements(&self) { - self.trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::InventoryChanged, - ) - .await; + pub fn check_inventory_advancements(&self) { + if self.inventory_changed.swap(false, Ordering::Relaxed) + && let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) + { + p.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::InventoryChanged, + ); + } } } @@ -5712,50 +5793,60 @@ impl NBTStorage for PlayerInventory { // Create inventory list with the correct capacity (inventory size) let mut items: Vec = Vec::with_capacity(41); - let main_inv = self.main_inventory.read().await; - for (i, stack) in main_inv.iter().enumerate() { - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - item_compound.put_byte("Slot", i as i8); - stack.write_item_stack(&mut item_compound); - items.push(NbtTag::Compound(item_compound)); + { + let main_inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (i, stack) in main_inv.iter().enumerate() { + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + item_compound.put_byte("Slot", i as i8); + stack.write_item_stack(&mut item_compound); + items.push(NbtTag::Compound(item_compound)); + } } } let mut equipment_compound = NbtCompound::new(); - let equipment_guard = self.entity_equipment.lock().await; - for (slot, stack) in &equipment_guard.equipment { - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - stack.write_item_stack(&mut item_compound); - let vanilla_slot = match slot { - EquipmentSlot::Feet(_) => { - equipment_compound.put_compound("feet", item_compound.clone()); - Some(100i8) + { + let equipment_guard = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (slot, stack) in &equipment_guard.equipment { + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + stack.write_item_stack(&mut item_compound); + let vanilla_slot = match slot { + EquipmentSlot::Feet(_) => { + equipment_compound.put_compound("feet", item_compound.clone()); + Some(100i8) + } + EquipmentSlot::Legs(_) => { + equipment_compound.put_compound("legs", item_compound.clone()); + Some(101i8) + } + EquipmentSlot::Chest(_) => { + equipment_compound.put_compound("chest", item_compound.clone()); + Some(102i8) + } + EquipmentSlot::Head(_) => { + equipment_compound.put_compound("head", item_compound.clone()); + Some(103i8) + } + EquipmentSlot::OffHand(_) => { + equipment_compound.put_compound("offhand", item_compound.clone()); + Some(-106i8) + } + _ => None, + }; + if let Some(slot_byte) = vanilla_slot { + let mut inv_item_compound = NbtCompound::new(); + inv_item_compound.put_byte("Slot", slot_byte); + stack.write_item_stack(&mut inv_item_compound); + items.push(NbtTag::Compound(inv_item_compound)); } - EquipmentSlot::Legs(_) => { - equipment_compound.put_compound("legs", item_compound.clone()); - Some(101i8) - } - EquipmentSlot::Chest(_) => { - equipment_compound.put_compound("chest", item_compound.clone()); - Some(102i8) - } - EquipmentSlot::Head(_) => { - equipment_compound.put_compound("head", item_compound.clone()); - Some(103i8) - } - EquipmentSlot::OffHand(_) => { - equipment_compound.put_compound("offhand", item_compound.clone()); - Some(-106i8) - } - _ => None, - }; - if let Some(slot_byte) = vanilla_slot { - let mut inv_item_compound = NbtCompound::new(); - inv_item_compound.put_byte("Slot", slot_byte); - stack.write_item_stack(&mut inv_item_compound); - items.push(NbtTag::Compound(inv_item_compound)); } } } @@ -5869,37 +5960,16 @@ impl NBTStorage for EnderChestInventory { impl NBTStorageInit for EnderChestInventory {} impl EntityBase for Player { - fn damage_with_context<'a>( - &'a self, - caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + caller: &dyn EntityBase, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.abilities.lock().await.invulnerable - && damage_type != DamageType::GENERIC_KILL - && damage_type != DamageType::OUT_OF_WORLD - { - return false; - } - // TODO: Implement shield blocking durability. - let result = self - .living_entity - .damage_with_context(caller, amount, damage_type, position, source, cause) - .await; - if result { - let health = self.living_entity.health.load(); - if health <= 0.0 { - let death_message = - LivingEntity::get_death_message(caller, damage_type, source, cause).await; - self.handle_killed(death_message).await; - } - } - result - }) + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + self.damage_with_context(caller, amount, damage_type, position, source, cause) } fn teleport( @@ -6191,6 +6261,16 @@ impl EntityBase for Player { matches!(gamemode, GameMode::Creative | GameMode::Spectator), Ordering::Relaxed, ); + self.living_entity + .entity + .no_physics + .store(gamemode == GameMode::Spectator, Ordering::Relaxed); + if gamemode == GameMode::Spectator { + self.living_entity + .entity + .on_ground + .store(false, Ordering::Relaxed); + } self.has_played_before.store( nbt.get_bool("HasPlayedBefore").unwrap_or(false), @@ -6264,10 +6344,8 @@ impl EntityBase for Player { (level * 7).min(100) as u32 } - fn tick_in_void<'a>(&'a self, dyn_self: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.living_entity.tick_in_void(dyn_self).await; - }) + fn tick_in_void(&self, dyn_self: &dyn EntityBase) { + self.living_entity.tick_in_void(dyn_self); } } @@ -6667,7 +6745,7 @@ impl InventoryPlayer for Player { fn drop_item(&self, item: ItemStack, _retain_ownership: bool) -> PlayerFuture<'_, ()> { Box::pin(async move { - self.drop_item(item).await; + self.drop_item(item); }) } @@ -6735,7 +6813,7 @@ impl InventoryPlayer for Player { .inventory .main_inventory .read() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .iter() .map(NetworkItemStackDescriptor::from) .collect(); @@ -7032,7 +7110,7 @@ impl InventoryPlayer for Player { amount: i32, ) -> PlayerFuture<'_, ()> { Box::pin(async move { - self.increment_stat(category, stat_id, amount).await; + self.increment_stat(category, stat_id, amount); }) } diff --git a/crates/pumpkin/src/entity/player/advancement.rs b/crates/pumpkin/src/entity/player/advancement.rs index fe185e982..087247c73 100644 --- a/crates/pumpkin/src/entity/player/advancement.rs +++ b/crates/pumpkin/src/entity/player/advancement.rs @@ -469,10 +469,7 @@ impl PlayerAdvancement { ], ); - player - .world() - .broadcast_editioned(&je_packet, &be_packet) - .await; + player.world().broadcast_editioned(&je_packet, &be_packet); }); } } diff --git a/crates/pumpkin/src/entity/player/advancement/trigger.rs b/crates/pumpkin/src/entity/player/advancement/trigger.rs index f5b8f7293..0cc5dd5e1 100644 --- a/crates/pumpkin/src/entity/player/advancement/trigger.rs +++ b/crates/pumpkin/src/entity/player/advancement/trigger.rs @@ -26,23 +26,22 @@ pub enum AdvancementTrigger { impl Player { #[allow(clippy::collapsible_if, clippy::too_many_lines)] - pub async fn trigger_advancement(&self, trigger: AdvancementTrigger) { + pub fn trigger_advancement(&self, trigger: AdvancementTrigger) { use pumpkin_data::advancement::Advancement; use pumpkin_data::item::Item; match trigger { AdvancementTrigger::InventoryChanged => { - if !self.has_advancement(Advancement::STORY_ROOT).await { - if self.has_item_in_inventory(&Item::CRAFTING_TABLE).await { + if !self.has_advancement(Advancement::STORY_ROOT) { + if self.has_item_in_inventory(&Item::CRAFTING_TABLE) { self.trigger_advancement_criterion( Advancement::STORY_ROOT, "crafting_table", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_MINE_STONE).await { + if !self.has_advancement(Advancement::STORY_MINE_STONE) { let stone_items = [ &Item::COBBLESTONE, &Item::STONE, @@ -54,65 +53,59 @@ impl Player { &Item::BLACKSTONE, ]; for item in stone_items { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::STORY_MINE_STONE, "get_stone", - ) - .await; + ); break; } } } - if !self.has_advancement(Advancement::STORY_UPGRADE_TOOLS).await { - if self.has_item_in_inventory(&Item::STONE_PICKAXE).await { + if !self.has_advancement(Advancement::STORY_UPGRADE_TOOLS) { + if self.has_item_in_inventory(&Item::STONE_PICKAXE) { self.trigger_advancement_criterion( Advancement::STORY_UPGRADE_TOOLS, "stone_pickaxe", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_SMELT_IRON).await { - if self.has_item_in_inventory(&Item::IRON_INGOT).await { - self.trigger_advancement_criterion(Advancement::STORY_SMELT_IRON, "iron") - .await; + if !self.has_advancement(Advancement::STORY_SMELT_IRON) { + if self.has_item_in_inventory(&Item::IRON_INGOT) { + self.trigger_advancement_criterion(Advancement::STORY_SMELT_IRON, "iron"); } } - if !self.has_advancement(Advancement::STORY_IRON_TOOLS).await { - if self.has_item_in_inventory(&Item::IRON_PICKAXE).await { + if !self.has_advancement(Advancement::STORY_IRON_TOOLS) { + if self.has_item_in_inventory(&Item::IRON_PICKAXE) { self.trigger_advancement_criterion( Advancement::STORY_IRON_TOOLS, "iron_pickaxe", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_MINE_DIAMOND).await { - if self.has_item_in_inventory(&Item::DIAMOND).await { + if !self.has_advancement(Advancement::STORY_MINE_DIAMOND) { + if self.has_item_in_inventory(&Item::DIAMOND) { self.trigger_advancement_criterion( Advancement::STORY_MINE_DIAMOND, "diamond", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_LAVA_BUCKET).await { - if self.has_item_in_inventory(&Item::LAVA_BUCKET).await { + if !self.has_advancement(Advancement::STORY_LAVA_BUCKET) { + if self.has_item_in_inventory(&Item::LAVA_BUCKET) { self.trigger_advancement_criterion( Advancement::STORY_LAVA_BUCKET, "lava_bucket", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_OBTAIN_ARMOR).await { + if !self.has_advancement(Advancement::STORY_OBTAIN_ARMOR) { let armor = [ (&Item::IRON_HELMET, "iron_helmet"), (&Item::IRON_CHESTPLATE, "iron_chestplate"), @@ -120,17 +113,16 @@ impl Player { (&Item::IRON_BOOTS, "iron_boots"), ]; for (item, criterion) in armor { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::STORY_OBTAIN_ARMOR, criterion, - ) - .await; + ); } } } - if !self.has_advancement(Advancement::STORY_SHINY_GEAR).await { + if !self.has_advancement(Advancement::STORY_SHINY_GEAR) { let armor = [ (&Item::DIAMOND_HELMET, "diamond_helmet"), (&Item::DIAMOND_CHESTPLATE, "diamond_chestplate"), @@ -138,104 +130,74 @@ impl Player { (&Item::DIAMOND_BOOTS, "diamond_boots"), ]; for (item, criterion) in armor { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::STORY_SHINY_GEAR, criterion, - ) - .await; + ); } } } - if !self.has_advancement(Advancement::STORY_FORM_OBSIDIAN).await { - if self.has_item_in_inventory(&Item::OBSIDIAN).await { + if !self.has_advancement(Advancement::STORY_FORM_OBSIDIAN) { + if self.has_item_in_inventory(&Item::OBSIDIAN) { self.trigger_advancement_criterion( Advancement::STORY_FORM_OBSIDIAN, "obsidian", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_GET_WITHER_SKULL) - .await - { - if self - .has_item_in_inventory(&Item::WITHER_SKELETON_SKULL) - .await - { + if !self.has_advancement(Advancement::NETHER_GET_WITHER_SKULL) { + if self.has_item_in_inventory(&Item::WITHER_SKELETON_SKULL) { self.trigger_advancement_criterion( Advancement::NETHER_GET_WITHER_SKULL, "wither_skull", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_OBTAIN_ANCIENT_DEBRIS) - .await - { - if self.has_item_in_inventory(&Item::ANCIENT_DEBRIS).await { + if !self.has_advancement(Advancement::NETHER_OBTAIN_ANCIENT_DEBRIS) { + if self.has_item_in_inventory(&Item::ANCIENT_DEBRIS) { self.trigger_advancement_criterion( Advancement::NETHER_OBTAIN_ANCIENT_DEBRIS, "ancient_debris", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_OBTAIN_BLAZE_ROD) - .await - { - if self.has_item_in_inventory(&Item::BLAZE_ROD).await { + if !self.has_advancement(Advancement::NETHER_OBTAIN_BLAZE_ROD) { + if self.has_item_in_inventory(&Item::BLAZE_ROD) { self.trigger_advancement_criterion( Advancement::NETHER_OBTAIN_BLAZE_ROD, "blaze_rod", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_OBTAIN_CRYING_OBSIDIAN) - .await - { - if self.has_item_in_inventory(&Item::CRYING_OBSIDIAN).await { + if !self.has_advancement(Advancement::NETHER_OBTAIN_CRYING_OBSIDIAN) { + if self.has_item_in_inventory(&Item::CRYING_OBSIDIAN) { self.trigger_advancement_criterion( Advancement::NETHER_OBTAIN_CRYING_OBSIDIAN, "crying_obsidian", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_NETHERITE_ARMOR) - .await - { - if self.has_item_in_inventory(&Item::NETHERITE_HELMET).await - && self - .has_item_in_inventory(&Item::NETHERITE_CHESTPLATE) - .await - && self.has_item_in_inventory(&Item::NETHERITE_LEGGINGS).await - && self.has_item_in_inventory(&Item::NETHERITE_BOOTS).await + if !self.has_advancement(Advancement::NETHER_NETHERITE_ARMOR) { + if self.has_item_in_inventory(&Item::NETHERITE_HELMET) + && self.has_item_in_inventory(&Item::NETHERITE_CHESTPLATE) + && self.has_item_in_inventory(&Item::NETHERITE_LEGGINGS) + && self.has_item_in_inventory(&Item::NETHERITE_BOOTS) { self.trigger_advancement_criterion( Advancement::NETHER_NETHERITE_ARMOR, "netherite_armor", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::HUSBANDRY_TACTICAL_FISHING) - .await - { + if !self.has_advancement(Advancement::HUSBANDRY_TACTICAL_FISHING) { let fish_buckets = [ (&Item::COD_BUCKET, "cod_bucket"), (&Item::SALMON_BUCKET, "salmon_bucket"), @@ -243,157 +205,129 @@ impl Player { (&Item::TROPICAL_FISH_BUCKET, "tropical_fish_bucket"), ]; for (item, criterion) in fish_buckets { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_TACTICAL_FISHING, criterion, - ) - .await; + ); } } } - if !self - .has_advancement(Advancement::HUSBANDRY_AXOLOTL_IN_A_BUCKET) - .await - { - if self.has_item_in_inventory(&Item::AXOLOTL_BUCKET).await { + if !self.has_advancement(Advancement::HUSBANDRY_AXOLOTL_IN_A_BUCKET) { + if self.has_item_in_inventory(&Item::AXOLOTL_BUCKET) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_AXOLOTL_IN_A_BUCKET, "axolotl_bucket", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::HUSBANDRY_TADPOLE_IN_A_BUCKET) - .await - { - if self.has_item_in_inventory(&Item::TADPOLE_BUCKET).await { + if !self.has_advancement(Advancement::HUSBANDRY_TADPOLE_IN_A_BUCKET) { + if self.has_item_in_inventory(&Item::TADPOLE_BUCKET) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_TADPOLE_IN_A_BUCKET, "tadpole_bucket", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::HUSBANDRY_OBTAIN_NETHERITE_HOE) - .await - { - if self.has_item_in_inventory(&Item::NETHERITE_HOE).await { + if !self.has_advancement(Advancement::HUSBANDRY_OBTAIN_NETHERITE_HOE) { + if self.has_item_in_inventory(&Item::NETHERITE_HOE) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_OBTAIN_NETHERITE_HOE, "netherite_hoe", - ) - .await; + ); } } - if !self.has_advancement(Advancement::STORY_ENCHANT_ITEM).await { - let mut has_enchanted = false; - let main_inv = self.inventory().main_inventory.read().await; - for stack in main_inv.iter() { - if !stack.is_empty() && stack.has_enchantments() { - has_enchanted = true; - break; - } - } + if !self.has_advancement(Advancement::STORY_ENCHANT_ITEM) { + let has_enchanted = { + let main_inv = self + .inventory() + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + main_inv + .iter() + .any(|stack| !stack.is_empty() && stack.has_enchantments()) + }; if has_enchanted { self.trigger_advancement_criterion( Advancement::STORY_ENCHANT_ITEM, "enchanted_item", - ) - .await; + ); } } - if !self.has_advancement(Advancement::NETHER_BREW_POTION).await { + if !self.has_advancement(Advancement::NETHER_BREW_POTION) { let potions = [&Item::POTION, &Item::SPLASH_POTION, &Item::LINGERING_POTION]; for item in potions { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::NETHER_BREW_POTION, "potion", - ) - .await; + ); break; } } } - if !self - .has_advancement(Advancement::NETHER_CREATE_BEACON) - .await - { - if self.has_item_in_inventory(&Item::BEACON).await { + if !self.has_advancement(Advancement::NETHER_CREATE_BEACON) { + if self.has_item_in_inventory(&Item::BEACON) { self.trigger_advancement_criterion( Advancement::NETHER_CREATE_BEACON, "beacon", - ) - .await; + ); } } - if !self - .has_advancement(Advancement::NETHER_CREATE_FULL_BEACON) - .await - { - if self.has_item_in_inventory(&Item::BEACON).await { + if !self.has_advancement(Advancement::NETHER_CREATE_FULL_BEACON) { + if self.has_item_in_inventory(&Item::BEACON) { self.trigger_advancement_criterion( Advancement::NETHER_CREATE_FULL_BEACON, "beacon", - ) - .await; + ); } } - if !self.has_advancement(Advancement::END_ELYTRA).await { - if self.has_item_in_inventory(&Item::ELYTRA).await { - self.trigger_advancement_criterion(Advancement::END_ELYTRA, "elytra") - .await; + if !self.has_advancement(Advancement::END_ELYTRA) { + if self.has_item_in_inventory(&Item::ELYTRA) { + self.trigger_advancement_criterion(Advancement::END_ELYTRA, "elytra"); } } - if !self.has_advancement(Advancement::END_DRAGON_EGG).await { - if self.has_item_in_inventory(&Item::DRAGON_EGG).await { + if !self.has_advancement(Advancement::END_DRAGON_EGG) { + if self.has_item_in_inventory(&Item::DRAGON_EGG) { self.trigger_advancement_criterion( Advancement::END_DRAGON_EGG, "dragon_egg", - ) - .await; + ); } } - if !self.has_advancement(Advancement::END_DRAGON_BREATH).await { - if self.has_item_in_inventory(&Item::DRAGON_BREATH).await { + if !self.has_advancement(Advancement::END_DRAGON_BREATH) { + if self.has_item_in_inventory(&Item::DRAGON_BREATH) { self.trigger_advancement_criterion( Advancement::END_DRAGON_BREATH, "dragon_breath", - ) - .await; + ); } } - if !self.has_advancement(Advancement::END_FIND_END_CITY).await { + if !self.has_advancement(Advancement::END_FIND_END_CITY) { let city_items = [&Item::SHULKER_SHELL, &Item::CHORUS_FRUIT]; for item in city_items { - if self.has_item_in_inventory(item).await { + if self.has_item_in_inventory(item) { self.trigger_advancement_criterion( Advancement::END_FIND_END_CITY, "in_city", - ) - .await; + ); break; } } } - if !self - .has_advancement(Advancement::NETHER_EXPLORE_NETHER) - .await - { + if !self.has_advancement(Advancement::NETHER_EXPLORE_NETHER) { let pos = self.position().to_block_pos(); let biome = self.world().level.get_rough_biome(&pos); let biome_resource = format!("minecraft:{}", biome.registry_id); @@ -408,15 +342,11 @@ impl Player { self.trigger_advancement_criterion( Advancement::NETHER_EXPLORE_NETHER, &biome_resource, - ) - .await; + ); } } - if !self - .has_advancement(Advancement::ADVENTURE_ADVENTURING_TIME) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_ADVENTURING_TIME) { let pos = self.position().to_block_pos(); let biome = self.world().level.get_rough_biome(&pos); let biome_resource = format!("minecraft:{}", biome.registry_id); @@ -481,8 +411,7 @@ impl Player { self.trigger_advancement_criterion( Advancement::ADVENTURE_ADVENTURING_TIME, &biome_resource, - ) - .await; + ); } } } @@ -492,38 +421,28 @@ impl Player { self.trigger_advancement_criterion( Advancement::ADVENTURE_KILL_A_MOB, &entity_type_resource, - ) - .await; + ); self.trigger_advancement_criterion( Advancement::ADVENTURE_KILL_ALL_MOBS, &entity_type_resource, - ) - .await; - if !self.has_advancement(Advancement::ADVENTURE_ROOT).await { + ); + if !self.has_advancement(Advancement::ADVENTURE_ROOT) { self.trigger_advancement_criterion( Advancement::ADVENTURE_ROOT, "killed_something", - ) - .await; + ); } } AdvancementTrigger::SleptInBed => { - if !self - .has_advancement(Advancement::ADVENTURE_SLEEP_IN_BED) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_SLEEP_IN_BED) { self.trigger_advancement_criterion( Advancement::ADVENTURE_SLEEP_IN_BED, "slept_in_bed", - ) - .await; + ); } } AdvancementTrigger::FishedItem { item_id } => { - if !self - .has_advancement(Advancement::HUSBANDRY_FISHY_BUSINESS) - .await - { + if !self.has_advancement(Advancement::HUSBANDRY_FISHY_BUSINESS) { let fishes = [ ("minecraft:cod", "cod"), ("minecraft:salmon", "salmon"), @@ -535,18 +454,14 @@ impl Player { self.trigger_advancement_criterion( Advancement::HUSBANDRY_FISHY_BUSINESS, criterion, - ) - .await; + ); break; } } } } AdvancementTrigger::PlacedBlock { block_id } => { - if !self - .has_advancement(Advancement::HUSBANDRY_PLANT_SEED) - .await - { + if !self.has_advancement(Advancement::HUSBANDRY_PLANT_SEED) { let seed_blocks = [ ("minecraft:wheat", "wheat"), ("minecraft:pumpkin_stem", "pumpkin_stem"), @@ -561,8 +476,7 @@ impl Player { self.trigger_advancement_criterion( Advancement::HUSBANDRY_PLANT_SEED, criterion, - ) - .await; + ); break; } } @@ -570,44 +484,36 @@ impl Player { } AdvancementTrigger::EnterDimension { dimension } => { if dimension == "the_nether" { - if !self - .has_advancement(Advancement::STORY_ENTER_THE_NETHER) - .await - { + if !self.has_advancement(Advancement::STORY_ENTER_THE_NETHER) { self.trigger_advancement_criterion( Advancement::STORY_ENTER_THE_NETHER, "entered_nether", - ) - .await; + ); } - if !self.has_advancement(Advancement::NETHER_ROOT).await { + if !self.has_advancement(Advancement::NETHER_ROOT) { self.trigger_advancement_criterion( Advancement::NETHER_ROOT, "entered_nether", - ) - .await; + ); } } else if dimension == "the_end" { - if !self.has_advancement(Advancement::STORY_ENTER_THE_END).await { + if !self.has_advancement(Advancement::STORY_ENTER_THE_END) { self.trigger_advancement_criterion( Advancement::STORY_ENTER_THE_END, "entered_end", - ) - .await; + ); } - if !self.has_advancement(Advancement::END_ROOT).await { - self.trigger_advancement_criterion(Advancement::END_ROOT, "entered_end") - .await; + if !self.has_advancement(Advancement::END_ROOT) { + self.trigger_advancement_criterion(Advancement::END_ROOT, "entered_end"); } } } AdvancementTrigger::ConsumeItem { item_id } => { - if !self.has_advancement(Advancement::HUSBANDRY_ROOT).await { + if !self.has_advancement(Advancement::HUSBANDRY_ROOT) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_ROOT, "consumed_item", - ) - .await; + ); } let food_name = item_id.strip_prefix("minecraft:").unwrap_or(&item_id); if Advancement::HUSBANDRY_BALANCED_DIET @@ -617,143 +523,105 @@ impl Player { self.trigger_advancement_criterion( Advancement::HUSBANDRY_BALANCED_DIET, food_name, - ) - .await; + ); } } AdvancementTrigger::PlayerKilled => { - if !self.has_advancement(Advancement::ADVENTURE_ROOT).await { + if !self.has_advancement(Advancement::ADVENTURE_ROOT) { self.trigger_advancement_criterion( Advancement::ADVENTURE_ROOT, "killed_by_something", - ) - .await; + ); } } AdvancementTrigger::DeflectedDamage => { - if !self.has_advancement(Advancement::STORY_DEFLECT_ARROW).await { + if !self.has_advancement(Advancement::STORY_DEFLECT_ARROW) { self.trigger_advancement_criterion( Advancement::STORY_DEFLECT_ARROW, "deflected_projectile", - ) - .await; + ); } } AdvancementTrigger::LaunchedEyeOfEnder => { - if !self - .has_advancement(Advancement::STORY_FOLLOW_ENDER_EYE) - .await - { + if !self.has_advancement(Advancement::STORY_FOLLOW_ENDER_EYE) { self.trigger_advancement_criterion( Advancement::STORY_FOLLOW_ENDER_EYE, "in_stronghold", - ) - .await; + ); } } AdvancementTrigger::GlowedSign => { - if !self - .has_advancement(Advancement::HUSBANDRY_MAKE_A_SIGN_GLOW) - .await - { + if !self.has_advancement(Advancement::HUSBANDRY_MAKE_A_SIGN_GLOW) { self.trigger_advancement_criterion( Advancement::HUSBANDRY_MAKE_A_SIGN_GLOW, "make_a_sign_glow", - ) - .await; + ); } } AdvancementTrigger::BredAnimal { parent_type } => { - self.trigger_advancement_criterion(Advancement::HUSBANDRY_BREED_AN_ANIMAL, "bred") - .await; + self.trigger_advancement_criterion(Advancement::HUSBANDRY_BREED_AN_ANIMAL, "bred"); self.trigger_advancement_criterion( Advancement::HUSBANDRY_BRED_ALL_ANIMALS, &parent_type, - ) - .await; + ); } AdvancementTrigger::EnterBlock { block_id: _ } => {} AdvancementTrigger::DealtOverkillDamage => { - if !self - .has_advancement(Advancement::ADVENTURE_OVEROVERKILL) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_OVEROVERKILL) { self.trigger_advancement_criterion( Advancement::ADVENTURE_OVEROVERKILL, "overoverkill", - ) - .await; + ); } } AdvancementTrigger::SniperDuel => { - if !self - .has_advancement(Advancement::ADVENTURE_SNIPER_DUEL) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_SNIPER_DUEL) { self.trigger_advancement_criterion( Advancement::ADVENTURE_SNIPER_DUEL, "killed_skeleton", - ) - .await; + ); } } AdvancementTrigger::TwoBirdsOneArrow => { - if !self - .has_advancement(Advancement::ADVENTURE_TWO_BIRDS_ONE_ARROW) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_TWO_BIRDS_ONE_ARROW) { self.trigger_advancement_criterion( Advancement::ADVENTURE_TWO_BIRDS_ONE_ARROW, "two_birds", - ) - .await; + ); } } AdvancementTrigger::Arbalistic => { - if !self - .has_advancement(Advancement::ADVENTURE_ARBALISTIC) - .await - { + if !self.has_advancement(Advancement::ADVENTURE_ARBALISTIC) { self.trigger_advancement_criterion( Advancement::ADVENTURE_ARBALISTIC, "arbalistic", - ) - .await; + ); } } AdvancementTrigger::Bullseye => { - if !self.has_advancement(Advancement::ADVENTURE_BULLSEYE).await { - self.trigger_advancement_criterion(Advancement::ADVENTURE_BULLSEYE, "bullseye") - .await; + if !self.has_advancement(Advancement::ADVENTURE_BULLSEYE) { + self.trigger_advancement_criterion(Advancement::ADVENTURE_BULLSEYE, "bullseye"); } } AdvancementTrigger::CuredZombieVillager => { - if !self - .has_advancement(Advancement::STORY_CURE_ZOMBIE_VILLAGER) - .await - { + if !self.has_advancement(Advancement::STORY_CURE_ZOMBIE_VILLAGER) { self.trigger_advancement_criterion( Advancement::STORY_CURE_ZOMBIE_VILLAGER, "cured_zombie", - ) - .await; + ); } } AdvancementTrigger::TradedWithVillager => { - if !self.has_advancement(Advancement::ADVENTURE_TRADE).await { - self.trigger_advancement_criterion(Advancement::ADVENTURE_TRADE, "traded") - .await; + if !self.has_advancement(Advancement::ADVENTURE_TRADE) { + self.trigger_advancement_criterion(Advancement::ADVENTURE_TRADE, "traded"); } if self.living_entity.entity.pos.load().y >= 319.0 - && !self - .has_advancement(Advancement::ADVENTURE_TRADE_AT_WORLD_HEIGHT) - .await + && !self.has_advancement(Advancement::ADVENTURE_TRADE_AT_WORLD_HEIGHT) { self.trigger_advancement_criterion( Advancement::ADVENTURE_TRADE_AT_WORLD_HEIGHT, "trade_at_world_height", - ) - .await; + ); } } } diff --git a/crates/pumpkin/src/entity/predicate/mod.rs b/crates/pumpkin/src/entity/predicate/mod.rs index 2fb3414ba..0f07b4919 100644 --- a/crates/pumpkin/src/entity/predicate/mod.rs +++ b/crates/pumpkin/src/entity/predicate/mod.rs @@ -1,5 +1,4 @@ use crate::entity::{Entity, EntityBase}; -use std::pin::Pin; pub enum EntityPredicate<'a> { ValidEntity, @@ -14,61 +13,47 @@ pub enum EntityPredicate<'a> { } impl EntityPredicate<'_> { - pub fn test<'b>( - &'b self, - entity: &'b Entity, - ) -> Pin + Send + 'b>> { - Box::pin(async move { - match self { - EntityPredicate::ValidEntity => entity.is_alive(), - EntityPredicate::ValidLivingEntity => { - entity.is_alive() && entity.get_living_entity().is_some() - } - EntityPredicate::NotMounted => { - entity.is_alive() - && !entity.has_passengers().await - && !entity.has_vehicle().await - } - EntityPredicate::ValidInventories => { - // TODO: implement - false - } - EntityPredicate::ExceptCreativeOrSpectator => entity - .get_player() - .is_some_and(|player| player.is_spectator() || player.is_creative()), - EntityPredicate::ExceptSpectator => !entity.is_spectator(), - EntityPredicate::CanCollide => { - EntityPredicate::ExceptSpectator.test(entity).await - && entity.is_collidable(None) - } - EntityPredicate::CanHit => { - EntityPredicate::ExceptSpectator.test(entity).await && entity.can_hit() - } - EntityPredicate::Rides(target_entity) => { - let target: &Entity = target_entity; - - let mut opt_vehicle_arc = { - let vehicle_lock = entity.vehicle.lock().await; - vehicle_lock.clone() - }; - - while let Some(vehicle_arc) = opt_vehicle_arc { - let vehicle_entity_base: &dyn EntityBase = &*vehicle_arc; - let target_base: &dyn EntityBase = target; - - if std::ptr::eq(vehicle_entity_base, target_base) { - return false; - } - - opt_vehicle_arc = { - let vehicle_lock = - vehicle_entity_base.get_entity().vehicle.lock().await; - vehicle_lock.clone() - } - } - true - } + #[must_use] + pub fn test(&self, entity: &Entity) -> bool { + match self { + EntityPredicate::ValidEntity => entity.is_alive(), + EntityPredicate::ValidLivingEntity => { + entity.is_alive() && entity.get_living_entity().is_some() } - }) + EntityPredicate::NotMounted => { + entity.is_alive() && !entity.has_passengers() && !entity.has_vehicle() + } + EntityPredicate::ValidInventories => { + // TODO: implement + false + } + EntityPredicate::ExceptCreativeOrSpectator => entity + .get_player() + .is_some_and(|player| player.is_spectator() || player.is_creative()), + EntityPredicate::ExceptSpectator => !entity.is_spectator(), + EntityPredicate::CanCollide => { + EntityPredicate::ExceptSpectator.test(entity) && entity.is_collidable(None) + } + EntityPredicate::CanHit => { + EntityPredicate::ExceptSpectator.test(entity) && entity.can_hit() + } + EntityPredicate::Rides(target_entity) => { + let target: &Entity = target_entity; + + let mut opt_vehicle_arc = entity.get_vehicle(); + + while let Some(vehicle_arc) = opt_vehicle_arc { + let vehicle_entity_base: &dyn EntityBase = &*vehicle_arc; + let target_base: &dyn EntityBase = target; + + if std::ptr::eq(vehicle_entity_base, target_base) { + return false; + } + + opt_vehicle_arc = vehicle_entity_base.get_entity().get_vehicle(); + } + true + } + } } } diff --git a/crates/pumpkin/src/entity/projectile/arrow.rs b/crates/pumpkin/src/entity/projectile/arrow.rs index dbd77e68b..21fcc5052 100644 --- a/crates/pumpkin/src/entity/projectile/arrow.rs +++ b/crates/pumpkin/src/entity/projectile/arrow.rs @@ -1,12 +1,10 @@ use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering}; -use tokio::sync::RwLock; use crate::entity::projectile::ProjectileHit; use crate::{ - entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity, player::Player, - }, + entity::{Entity, EntityBase, NbtFuture, living::LivingEntity, player::Player}, server::Server, }; use pumpkin_data::damage::DamageType; @@ -121,11 +119,9 @@ impl ArrowEntity { Some(shooter.entity_id), ); if let Some(server) = entity.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut launch_event).await; - }); - }); + server + .plugin_manager + .fire_blocking(&server, &mut launch_event); } Self { @@ -278,7 +274,10 @@ impl EntityBase for ArrowEntity { nbt: &'a mut pumpkin_nbt::compound::NbtCompound, ) -> NbtFuture<'a, ()> { Box::pin(async move { - let item_stack = self.item_stack.read().await; + let item_stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); Self::write_item_stack_nbt(&item_stack, nbt); }) } @@ -289,270 +288,269 @@ impl EntityBase for ArrowEntity { ) -> NbtFuture<'a, ()> { Box::pin(async move { if let Some(item_stack) = Self::read_item_stack_nbt(nbt) { - *self.item_stack.write().await = item_stack; + *self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) = item_stack; } }) } #[allow(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let world = entity.world.load(); + fn tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { + let entity = self.get_entity(); + let world = entity.world.load(); - // Handle shake time - let shake = self.shake_time.load(Ordering::Relaxed); - if shake > 0 { - self.shake_time.store(shake - 1, Ordering::Relaxed); + // Handle shake time + let shake = self.shake_time.load(Ordering::Relaxed); + if shake > 0 { + self.shake_time.store(shake - 1, Ordering::Relaxed); + } + + if self.in_ground.load(Ordering::Relaxed) { + // Increment in-ground time and life + let _in_ground_time = self.in_ground_time.fetch_add(1, Ordering::Relaxed); + let life = self.life.fetch_add(1, Ordering::Relaxed); + + // Despawn after enough time + if life >= Self::DESPAWN_TIME { + entity.remove(); } + return; + } - if self.in_ground.load(Ordering::Relaxed) { - // Increment in-ground time and life - let _in_ground_time = self.in_ground_time.fetch_add(1, Ordering::Relaxed); - let life = self.life.fetch_add(1, Ordering::Relaxed); + // Arrow is flying + let start_pos = entity.pos.load(); + let mut velocity = entity.velocity.load(); - // Despawn after enough time - if life >= Self::DESPAWN_TIME { - entity.remove().await; - } - return; - } + // Apply gravity + velocity.y -= Self::GRAVITY; - // Arrow is flying - let start_pos = entity.pos.load(); - let mut velocity = entity.velocity.load(); + // Apply inertia (air resistance or water drag) + let inertia = if entity.touching_water.load(Ordering::Relaxed) { + Self::WATER_INERTIA + } else { + Self::AIR_INERTIA + }; + velocity = velocity.multiply(inertia, inertia, inertia); - // Apply gravity - velocity.y -= Self::GRAVITY; + entity.velocity.store(velocity); - // Apply inertia (air resistance or water drag) - let inertia = if entity.touching_water.load(Ordering::Relaxed) { - Self::WATER_INERTIA - } else { - Self::AIR_INERTIA - }; - velocity = velocity.multiply(inertia, inertia, inertia); + // Update rotation based on velocity + let len = velocity.horizontal_length(); + entity.set_rotation( + velocity.x.atan2(velocity.z) as f32 * 57.295_776, + velocity.y.atan2(len) as f32 * 57.295_776, + ); - entity.velocity.store(velocity); + // Move arrow + let new_pos = start_pos.add(&velocity); + entity.set_pos(new_pos); - // Update rotation based on velocity - let len = velocity.horizontal_length(); - entity.set_rotation( - velocity.x.atan2(velocity.z) as f32 * 57.295_776, - velocity.y.atan2(len) as f32 * 57.295_776, + // Spawn critical particle trail while arrow is flying and critical + if self.is_critical.load(Ordering::Relaxed) { + world.spawn_particle( + entity.pos.load(), + Vector3::new(0.0f32, 0.0f32, 0.0f32), + 0.0, + 1, + Particle::Crit, ); + } - // Move arrow - let new_pos = start_pos.add(&velocity); - entity.set_pos(new_pos); + // Broadcast velocity update + let packet = CEntityVelocity::new(entity.entity_id.into(), velocity); - // Spawn critical particle trail while arrow is flying and critical - if self.is_critical.load(Ordering::Relaxed) { - world.spawn_particle( - entity.pos.load(), - Vector3::new(0.0f32, 0.0f32, 0.0f32), - 0.0, - 1, - Particle::Crit, - ); - } + let chunk_pos = entity.chunk_pos.load(); + world.broadcast_to_chunk(chunk_pos, &packet); - // Broadcast velocity update - let packet = CEntityVelocity::new(entity.entity_id.into(), velocity); + // Check for collisions using raycasting + let search_box = BoundingBox::new( + Vector3::new( + start_pos.x.min(new_pos.x), + start_pos.y.min(new_pos.y), + start_pos.z.min(new_pos.z), + ), + Vector3::new( + start_pos.x.max(new_pos.x), + start_pos.y.max(new_pos.y), + start_pos.z.max(new_pos.z), + ), + ) + .expand(0.3, 0.3, 0.3); - let chunk_pos = entity.chunk_pos.load(); - world.broadcast_to_chunk(chunk_pos, &packet); + let mut closest_t = 1.0f64; + let mut hit = None; - // Check for collisions using raycasting - let search_box = BoundingBox::new( - Vector3::new( - start_pos.x.min(new_pos.x), - start_pos.y.min(new_pos.y), - start_pos.z.min(new_pos.z), - ), - Vector3::new( - start_pos.x.max(new_pos.x), - start_pos.y.max(new_pos.y), - start_pos.z.max(new_pos.z), - ), - ) - .expand(0.3, 0.3, 0.3); + // Block collisions + let (block_cols, block_positions) = + world.get_block_collisions(search_box, self.get_entity()); + for (idx, bb) in block_cols.iter().enumerate() { + if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, bb) + && t < closest_t + { + closest_t = t; - let mut closest_t = 1.0f64; - let mut hit = None; - - // Block collisions - let (block_cols, block_positions) = world - .get_block_collisions(search_box, self.get_entity()) - .await; - for (idx, bb) in block_cols.iter().enumerate() { - if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, bb) - && t < closest_t - { - closest_t = t; - - // Map back to block pos - let mut curr = 0; - for (len, pos) in &block_positions { - curr += len; - if idx < curr { - let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); - hit = Some(ProjectileHit::Block { - pos: *pos, - face: get_hit_face(hit_pos, *pos), - hit_pos, - normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), - }); - break; - } + // Map back to block pos + let mut curr = 0; + for (len, pos) in &block_positions { + curr += len; + if idx < curr { + let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); + hit = Some(ProjectileHit::Block { + pos: *pos, + face: get_hit_face(hit_pos, *pos), + hit_pos, + normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), + }); + break; } } } + } - // Entity collisions - let candidates = world.get_entities_at_box(&search_box); - for cand in candidates { - if self.should_skip_collision(entity, &cand) { - continue; - } - - let ebb = cand.get_entity().bounding_box.load().expand(0.3, 0.3, 0.3); - if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, &ebb) - && t < closest_t - { - closest_t = t; - let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); - hit = Some(ProjectileHit::Entity { - entity: cand.clone(), - hit_pos, - normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), - }); - } + // Entity collisions + let candidates = world.get_entities_at_box(&search_box); + for cand in candidates { + if self.should_skip_collision(entity, &cand) { + continue; } - // Handle hit - if let Some(h) = hit { - // Ensure hit is only processed once - if self.has_hit.swap(true, Ordering::SeqCst) { - return; - } - - caller.on_hit(h).await; - } - }) - } - - #[allow(clippy::too_many_lines)] - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let (hit_pos, hit_entity) = match hit { - ProjectileHit::Block { hit_pos, .. } => (hit_pos, None), - ProjectileHit::Entity { - ref entity, + let ebb = cand.get_entity().bounding_box.load().expand(0.3, 0.3, 0.3); + if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, &ebb) + && t < closest_t + { + closest_t = t; + let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); + hit = Some(ProjectileHit::Entity { + entity: cand.clone(), hit_pos, - .. - } => (hit_pos, Some(entity.get_entity().entity_id)), - }; - let mut hit_event = - crate::plugin::api::events::entity::projectile_hit::ProjectileHitEvent::new( - self.entity.entity_id, - hit_pos, - hit_entity, - ); - if let Some(server) = self.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut hit_event).await; + normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), + }); } - if hit_event.cancelled { + } + + // Handle hit + if let Some(h) = hit { + // Ensure hit is only processed once + if self.has_hit.swap(true, Ordering::SeqCst) { return; } - let entity = self.get_entity(); - let world = entity.world.load(); + caller.on_hit(h); + } + } - match hit { - ProjectileHit::Block { - pos, - face: _, - hit_pos, - .. - } => { - // Arrow hit a block - stick into it - self.in_ground.store(true, Ordering::Relaxed); - self.shake_time.store(7, Ordering::Relaxed); - *self - .last_block_pos - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(pos); + #[allow(clippy::too_many_lines)] + fn on_hit(&self, hit: ProjectileHit) { + let (hit_pos, hit_entity) = match hit { + ProjectileHit::Block { hit_pos, .. } => (hit_pos, None), + ProjectileHit::Entity { + ref entity, + hit_pos, + .. + } => (hit_pos, Some(entity.get_entity().entity_id)), + }; + let mut hit_event = + crate::plugin::api::events::entity::projectile_hit::ProjectileHitEvent::new( + self.entity.entity_id, + hit_pos, + hit_entity, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut hit_event); + } - let block = world.get_block(&pos); - if block == &pumpkin_data::Block::TARGET { - let player_opt = self.owner_id.and_then(|id| world.get_player_by_id(id)); - if let Some(player) = player_opt { - player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::Bullseye).await; - } - } + let entity = self.get_entity(); + let world = entity.world.load(); - // Stop the arrow - entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); - entity.set_pos(hit_pos); + match hit { + ProjectileHit::Block { + pos, + face: _, + hit_pos, + .. + } => { + // Arrow hit a block - stick into it + self.in_ground.store(true, Ordering::Relaxed); + self.shake_time.store(7, Ordering::Relaxed); + *self + .last_block_pos + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(pos); - // Play sound - let sound_packet = CSoundEffect::new( - IdOr::Id(Sound::EntityArrowHit as u16), - SoundCategory::Neutral, - &hit_pos, - 1.0, - 1.0, - 0.0, + let block = world.get_block(&pos); + if block == &pumpkin_data::Block::TARGET + && let Some(player) = self.owner_id.and_then(|id| world.get_player_by_id(id)) + { + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::Bullseye, ); - let chunk_pos = entity.chunk_pos.load(); - world.broadcast_to_chunk(chunk_pos, &sound_packet); - - // Reset critical flag - self.is_critical.store(false, Ordering::Relaxed); } - ProjectileHit::Entity { - entity: target, - hit_pos, - .. - } => { - // Calculate damage - let velocity = entity.velocity.load(); - let power = velocity.length(); - let mut damage = (power * self.base_damage).ceil() as i32; - // Apply critical hit bonus - if self.is_critical.load(Ordering::Relaxed) { - let bonus = (rand::random::() % (damage / 2 + 2) as u32) as i32; - damage = damage.saturating_add(bonus); - } - if self.is_flame.load(Ordering::Relaxed) { - target.get_entity().set_on_fire_for_ticks(100); - } + // Stop the arrow + entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); + entity.set_pos(hit_pos); - let damage_succeeded = target - .damage_with_context( - &*target, - damage as f32, - DamageType::ARROW, - Some(hit_pos), - None, - Some(self), - ) - .await; + // Play sound + let sound_packet = CSoundEffect::new( + IdOr::Id(Sound::EntityArrowHit as u16), + SoundCategory::Neutral, + &hit_pos, + 1.0, + 1.0, + 0.0, + ); + let chunk_pos = entity.chunk_pos.load(); + world.broadcast_to_chunk(chunk_pos, &sound_packet); - if let Some(living) = target.get_living_entity() { - let punch = self.punch_level.load(Ordering::Relaxed); + // Reset critical flag + self.is_critical.store(false, Ordering::Relaxed); + } + ProjectileHit::Entity { + entity: target, + hit_pos, + .. + } => { + // Calculate damage + let velocity = entity.velocity.load(); + let power = velocity.length(); + let mut damage = (power * self.base_damage).ceil() as i32; + + // Apply critical hit bonus + if self.is_critical.load(Ordering::Relaxed) { + let bonus = (rand::random::() % (damage / 2 + 2) as u32) as i32; + damage = damage.saturating_add(bonus); + } + if self.is_flame.load(Ordering::Relaxed) { + target.get_entity().set_on_fire_for_ticks(100); + } + + let punch = self.punch_level.load(Ordering::Relaxed); + let is_spectral = entity.entity_type.id == EntityType::SPECTRAL_ARROW.id; + let entity_type: &'static EntityType = entity.entity_type; + let owner_id = self.owner_id; + let target_clone = target.clone(); + let world_clone = world.clone(); + let pierce = self.pierce_level.load(Ordering::Relaxed); + + tokio::spawn(async move { + let damage_succeeded = target_clone.damage_with_context( + &*target_clone, + damage as f32, + DamageType::ARROW, + Some(hit_pos), + None, + None, + ); + + if let Some(living) = target_clone.get_living_entity() { if punch > 0 - && let Some(owner_id) = self.owner_id - && let Some(owner_entity) = world.get_entity_by_id(owner_id) + && let Some(owner_id) = owner_id + && let Some(owner_entity) = world_clone.get_entity_by_id(owner_id) { crate::entity::combat::handle_knockback( owner_entity.get_entity(), - target.as_ref(), + target_clone.as_ref(), f64::from(punch) * 0.6, ); } @@ -566,10 +564,10 @@ impl EntityBase for ArrowEntity { 1.0, 0.0, ); - world.broadcast_packet_all(&sound_packet); + world_clone.broadcast_packet_all(&sound_packet); if Self::should_apply_post_hurt_effects(damage_succeeded) { - let item_stack = self.item_stack.read().await.clone(); + let item_stack = ItemStack::new(1, Self::default_item(entity_type)); let scale = item_stack .get_data_component::() .map_or(1.0, |component| component.scale); @@ -580,25 +578,22 @@ impl EntityBase for ArrowEntity { ), scale, crate::item::potion::PotionApplicationSource::Arrow, - ) - .await; + ); - if entity.entity_type.id == EntityType::SPECTRAL_ARROW.id { - living.add_effect(Self::spectral_glowing_effect()).await; + if is_spectral { + living.add_effect(Self::spectral_glowing_effect()); } } } + }); - // Check pierce level - let pierce = self.pierce_level.load(Ordering::Relaxed); - if pierce == 0 { - // No piercing - remove arrow - entity.remove().await; - } - // If piercing > 0, arrow continues (TODO: would need to track pierced entities) + // Check pierce level + if pierce == 0 { + // No piercing - remove arrow + entity.remove(); } } - }) + } } fn get_entity(&self) -> &Entity { @@ -610,34 +605,35 @@ impl EntityBase for ArrowEntity { None } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Only allow picking up grounded arrows - if !self.in_ground.load(Ordering::Relaxed) { - return; - } + fn on_player_collision(&self, player: &Arc) { + // Only allow picking up grounded arrows + if !self.in_ground.load(Ordering::Relaxed) { + return; + } - if player.living_entity.health.load() <= 0.0 { - return; - } + if player.living_entity.health.load() <= 0.0 { + return; + } - // Check pickup rules - match self.pickup { - ArrowPickup::Disallowed => return, - ArrowPickup::CreativeOnly if !player.is_creative() => return, - _ => {} - } + // Check pickup rules + match self.pickup { + ArrowPickup::Disallowed => return, + ArrowPickup::CreativeOnly if !player.is_creative() => return, + _ => {} + } - // Try to insert an arrow into the player's inventory - let item_stack = self.item_stack.read().await; - let mut stack = Self::pickup_item_stack(&item_stack); - if player.is_creative() || player.inventory.insert_stack_anywhere(&mut stack).await { - player.living_entity.pickup(&self.entity, 1); + // Try to insert an arrow into the player's inventory + let item_stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut stack = Self::pickup_item_stack(&item_stack); + if player.is_creative() || player.inventory.insert_stack_anywhere(&mut stack) { + player.living_entity.pickup(&self.entity, 1); - // Remove arrow entity after pickup - self.get_entity().remove().await; - } - }) + // Remove arrow entity after pickup + self.get_entity().remove(); + } } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/projectile/egg.rs b/crates/pumpkin/src/entity/projectile/egg.rs index 2e3ed01f0..c55b0754a 100644 --- a/crates/pumpkin/src/entity/projectile/egg.rs +++ b/crates/pumpkin/src/entity/projectile/egg.rs @@ -1,11 +1,9 @@ -use std::sync::Arc; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, RwLock}; use crate::plugin::player::egg_throw::PlayerEggThrowEvent; use crate::{ - entity::{ - Entity, EntityBase, EntityBaseFuture, projectile::ThrownItemEntity, r#type::from_type, - }, + entity::{Entity, EntityBase, projectile::ThrownItemEntity, r#type::from_type}, server::Server, }; use pumpkin_data::entity::{EntityStatus, EntityType}; @@ -15,7 +13,6 @@ use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; -use tokio::sync::RwLock; use uuid::Uuid; const MAX_EGG_HATCH_EVENT_SPAWNS: usize = 16; @@ -56,35 +53,35 @@ impl EggEntity { } /// Set the item stack shown by this thrown egg - pub async fn set_item_stack(&self, item_stack: ItemStack) { - let mut write = self.item_stack.write().await; + pub fn set_item_stack(&self, item_stack: ItemStack) { + let mut write = self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); *write = item_stack; } } impl EntityBase for EggEntity { - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let stack = self.item_stack.read().await; + fn init_data_tracker(&self) { + let entity = self.get_entity(); + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // Sync the item stack so the client renders the correct color/variant - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::egg::ITEM_STACK, - &ItemStackSerializer::from(stack.clone()), - )], - None, - ); - }) + // Sync the item stack so the client renders the correct color/variant + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::egg::ITEM_STACK, + &ItemStackSerializer::from(stack.clone()), + )], + None, + ); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -94,81 +91,78 @@ impl EntityBase for EggEntity { fn get_living_entity(&self) -> Option<&crate::entity::living::LivingEntity> { None } - fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); - let hit_pos = hit.hit_pos(); - let normal = hit.normal(); + fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) { + let world = self.get_entity().world.load(); + let hit_pos = hit.hit_pos(); + let normal = hit.normal(); - // Chicken spawn position offset slightly from hit position - let spawn_pos = hit_pos.add(&normal.multiply(0.5, 0.5, 0.5)); + // Chicken spawn position offset slightly from hit position + let spawn_pos = hit_pos.add(&normal.multiply(0.5, 0.5, 0.5)); - // Play egg break particles - world.send_entity_status( - self.get_entity(), - EntityStatus::Death, - Some(ActorEventID::Death), + // Play egg break particles + world.send_entity_status( + self.get_entity(), + EntityStatus::Death, + Some(ActorEventID::Death), + ); + + // Decide spawn count per probabilities: + // r == 0 -> spawn 4 (1/256) + // r in 1..31 -> spawn 1 (31/256) + // else -> 0 + let r: u8 = rand::random(); // 0..=255 + let mut to_spawn = if r == 0 { 4usize } else { usize::from(r < 32) }; + let mut hatching = to_spawn > 0; + let mut hatching_type: &'static EntityType = &EntityType::CHICKEN; + + let owner_id = self.thrown.owner_id; + let entity_uuid = self.get_entity().entity_uuid; + let variant_name = { + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stack + .get_data_component::() + .map(|comp| comp.value.clone()) + }; + + if let Some(owner_id) = owner_id + && let Some(player) = world.get_player_by_id(owner_id) + && let Some(server) = world.server.upgrade() + { + let mut event = PlayerEggThrowEvent::new( + player, + entity_uuid, + hatching, + to_spawn as u8, + hatching_type, ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + hatching = false; + } else { + hatching = event.hatching; + to_spawn = (event.num_hatches as usize).min(MAX_EGG_HATCH_EVENT_SPAWNS); + hatching_type = event.hatching_type; + } + } - // Decide spawn count per probabilities: - // r == 0 -> spawn 4 (1/256) - // r in 1..31 -> spawn 1 (31/256) - // else -> 0 - let r: u8 = rand::random(); // 0..=255 - let mut to_spawn = if r == 0 { 4usize } else { usize::from(r < 32) }; - let mut hatching = to_spawn > 0; - let mut hatching_type: &'static EntityType = &EntityType::CHICKEN; + if hatching && to_spawn > 0 { + for _ in 0..to_spawn { + let mob = from_type(hatching_type, spawn_pos, &world, Uuid::new_v4()); - if let Some(owner_id) = self.thrown.owner_id - && let Some(player) = world.get_player_by_id(owner_id) - && let Some(server) = world.server.upgrade() - { - let mut event = PlayerEggThrowEvent::new( - player, - self.get_entity().entity_uuid, - hatching, - to_spawn as u8, - hatching_type, - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - hatching = false; - } else { - hatching = event.hatching; - to_spawn = (event.num_hatches as usize).min(MAX_EGG_HATCH_EVENT_SPAWNS); - hatching_type = event.hatching_type; + let yaw = rand::random::() * 360.0; + let new_entity = mob.get_entity(); + new_entity.set_rotation(yaw, 0.0); + new_entity.set_age(-24000); + if let Some(name) = &variant_name { + mob.set_variant_name(name); } + + world.spawn_entity(mob); } - - // Spawn chickens in a separate task to prevent stack overflow - if hatching && to_spawn > 0 { - let world_clone = world.clone(); - let spawn_pos_clone = spawn_pos; - - let variant_name = { - let stack = self.item_stack.read().await; - stack.get_data_component::() - .map(|comp| comp.value.clone()) - }; - - tokio::spawn(async move { - for _ in 0..to_spawn { - let mob = - from_type(hatching_type, spawn_pos_clone, &world_clone, Uuid::new_v4()); - - let yaw = rand::random::() * 360.0; - let new_entity = mob.get_entity(); - new_entity.set_rotation(yaw, 0.0); - new_entity.set_age(-24000); - if let Some(name) = &variant_name { - mob.set_variant_name(name); - } - - world_clone.spawn_entity(mob).await; - } - }); - } - }) + } } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/projectile/ender_pearl.rs b/crates/pumpkin/src/entity/projectile/ender_pearl.rs index 3c87d9793..aff527783 100644 --- a/crates/pumpkin/src/entity/projectile/ender_pearl.rs +++ b/crates/pumpkin/src/entity/projectile/ender_pearl.rs @@ -4,7 +4,7 @@ use std::sync::atomic::AtomicBool; use crate::entity::projectile::ProjectileHit; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, EntityType, mob::endermite::EndermiteEntity, + Entity, EntityBase, EntityType, mob::endermite::EndermiteEntity, projectile::ThrownItemEntity, }, server::Server, @@ -47,12 +47,8 @@ impl EnderPearlEntity { } impl EntityBase for EnderPearlEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -66,16 +62,43 @@ impl EntityBase for EnderPearlEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let world = entity.world.load(); + fn on_hit(&self, hit: ProjectileHit) { + let entity = self.get_entity(); + let world = entity.world.load(); - let attacker = self - .thrown - .owner_id - .and_then(|id| world.get_entity_by_id(id)); + let attacker = self + .thrown + .owner_id + .and_then(|id| world.get_entity_by_id(id)); + // Spawn portal particles at hit position + let hit_pos = hit.hit_pos(); + for _ in 0..PARTICLE_COUNT { + let offset = Vector3::new( + rand::random::() as f64 - 0.5, + rand::random::() as f64 * 2.0, + rand::random::() as f64 - 0.5, + ); + let speed = Vector3::new( + rand::random::() - 0.5, + 0.0, + rand::random::() - 0.5, + ); + + world.spawn_particle( + hit_pos.add(&offset), + Vector3::new(speed.x as f32, speed.y as f32, speed.z as f32), + 1.0, + 1, + Particle::Portal, + ); + } + + let world_clone = world.clone(); + let owner_id = self.thrown.owner_id; + let teleport_pos = entity.last_pos.load(); + + tokio::spawn(async move { if let ( ProjectileHit::Entity { entity: hit_entity, @@ -86,43 +109,18 @@ impl EntityBase for EnderPearlEntity { ) = (&hit, attacker) { let victim_ref = &**hit_entity; - hit_entity - .damage_with_context( - victim_ref, - 0.0, - DamageType::THROWN, - Some(*hit_pos), - Some(owner.get_entity()), - Some(victim_ref), - ) - .await; - } - - // Spawn portal particles at hit position - let hit_pos = hit.hit_pos(); - for _ in 0..PARTICLE_COUNT { - let offset = Vector3::new( - rand::random::() as f64 - 0.5, - rand::random::() as f64 * 2.0, - rand::random::() as f64 - 0.5, - ); - let speed = Vector3::new( - rand::random::() - 0.5, + hit_entity.damage_with_context( + victim_ref, 0.0, - rand::random::() - 0.5, - ); - - world.spawn_particle( - hit_pos.add(&offset), - Vector3::new(speed.x as f32, speed.y as f32, speed.z as f32), - 1.0, - 1, - Particle::Portal, + DamageType::THROWN, + Some(*hit_pos), + Some(owner.get_entity()), + Some(victim_ref), ); } - if let Some(owner_id) = self.thrown.owner_id - && let Some(owner) = world.get_entity_by_id(owner_id) + if let Some(owner_id) = owner_id + && let Some(owner) = world_clone.get_entity_by_id(owner_id) && owner.get_entity().is_alive() && owner.get_living_entity().is_none_or(|living| { living.health.load() > 0.0 @@ -130,19 +128,16 @@ impl EntityBase for EnderPearlEntity { }) { let should_spawn_endermite = rand::random::() < ENDERMITE_SPAWN_CHANCE; - if world.should_spawn_monsters() && should_spawn_endermite { + if world_clone.should_spawn_monsters() && should_spawn_endermite { let entity = Entity::new( - world.clone(), + world_clone.clone(), owner.get_entity().pos.load(), &EntityType::ENDERMITE, ); let endermite = EndermiteEntity::new(entity); - world.spawn_entity(endermite).await; + world_clone.spawn_entity(endermite); } - // Teleport position should be position of entity from last tick (tick before collision) - let teleport_pos = entity.last_pos.load(); - // In vanilla, teleport handles everything including sound owner .clone() @@ -150,28 +145,26 @@ impl EntityBase for EnderPearlEntity { teleport_pos, Some(owner.get_entity().yaw.load()), Some(owner.get_entity().pitch.load()), - world.clone(), + world_clone.clone(), ) .await; // Play teleport sound at new position - world.play_sound( + world_clone.play_sound( Sound::EntityPlayerTeleport, SoundCategory::Players, &teleport_pos, ); // Deal 5 damage to owner - owner - .damage( - owner.as_ref(), - 5.0, - pumpkin_data::damage::DamageType::ENDER_PEARL, - ) - .await; + owner.damage( + owner.as_ref(), + 5.0, + pumpkin_data::damage::DamageType::ENDER_PEARL, + ); } + }); - world.send_entity_status(entity, EntityStatus::Death, Some(ActorEventID::Death)); - }) + world.send_entity_status(entity, EntityStatus::Death, Some(ActorEventID::Death)); } } diff --git a/crates/pumpkin/src/entity/projectile/evoker_fangs.rs b/crates/pumpkin/src/entity/projectile/evoker_fangs.rs index dea86c88e..dbb78661c 100644 --- a/crates/pumpkin/src/entity/projectile/evoker_fangs.rs +++ b/crates/pumpkin/src/entity/projectile/evoker_fangs.rs @@ -5,7 +5,7 @@ use pumpkin_data::damage::DamageType; use pumpkin_data::sound::Sound; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture}, + entity::{Entity, EntityBase, NbtFuture}, server::Server, }; @@ -52,47 +52,36 @@ impl EntityBase for EvokerFangsEntity { }) } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - let world = entity.world.load(); + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { + let entity = &self.entity; + let world = entity.world.load(); - let warmup = self.warmup_ticks.load(Ordering::Relaxed); - let life = self.life_ticks.fetch_add(1, Ordering::Relaxed) + 1; + let warmup = self.warmup_ticks.load(Ordering::Relaxed); + let life = self.life_ticks.fetch_add(1, Ordering::Relaxed) + 1; - if life >= warmup { - if !self.has_bitten.swap(true, Ordering::SeqCst) { - entity.play_sound(Sound::EntityEvokerFangsAttack); + if life >= warmup { + if !self.has_bitten.swap(true, Ordering::SeqCst) { + entity.play_sound(Sound::EntityEvokerFangsAttack); - let bb = entity.bounding_box.load().expand(0.2, 0.2, 0.2); - let candidates = world.get_entities_at_box(&bb); + let bb = entity.bounding_box.load().expand(0.2, 0.2, 0.2); + let candidates = world.get_entities_at_box(&bb); - for cand in candidates { - let cand_ent = cand.get_entity(); - if Some(cand_ent.entity_id) == self.owner_id { - continue; - } + for cand in candidates { + let cand_ent = cand.get_entity(); + if Some(cand_ent.entity_id) == self.owner_id { + continue; + } - if cand_ent.entity_id != entity.entity_id { - let cand_clone = cand.clone(); - tokio::spawn(async move { - let _ = cand_clone - .damage(cand_clone.as_ref(), 6.0, DamageType::MAGIC) - .await; - }); - } + if cand_ent.entity_id != entity.entity_id { + let _ = cand.damage(cand.as_ref(), 6.0, DamageType::MAGIC); } } - - if life > warmup + 20 { - entity.remove().await; - } } - }) + + if life > warmup + 20 { + entity.remove(); + } + } } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/eye_of_ender.rs b/crates/pumpkin/src/entity/projectile/eye_of_ender.rs index 8ef3902ca..a1fd2247d 100644 --- a/crates/pumpkin/src/entity/projectile/eye_of_ender.rs +++ b/crates/pumpkin/src/entity/projectile/eye_of_ender.rs @@ -13,11 +13,11 @@ use pumpkin_protocol::{ codec::item_stack_seralizer::ItemStackSerializer, java::client::play::Metadata, }; use pumpkin_util::math::vector3::Vector3; +use std::sync::Mutex; use std::sync::{ Arc, atomic::{AtomicBool, AtomicU32, Ordering}, }; -use tokio::sync::Mutex; use super::{Entity, EntityBase}; @@ -54,7 +54,7 @@ impl EyeOfEnder { /// Aim the eye at `target`, clamping to [`TOO_FAR_DISTANCE`] if necessary, /// and randomly decide whether it should drop its item on expiry. - pub async fn signal_to(&self, target: Vector3) { + pub fn signal_to(&self, target: Vector3) { let pos = self.entity.pos.load(); let delta = target.sub(&pos); let horizontal_dist = delta.x.hypot(delta.z); @@ -69,7 +69,10 @@ impl EyeOfEnder { target }; - *self.target.lock().await = Some(clamped_target); + *self + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(clamped_target); self.life.store(0, Ordering::Relaxed); // 4-in-5 chance to survive (drop item); 1-in-5 plays the break effect. @@ -119,83 +122,86 @@ fn lerp(t: f64, start: f64, end: f64) -> f64 { } impl EntityBase for EyeOfEnder { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; - entity.tick(caller, server).await; + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + let entity = &self.entity; + entity.tick(caller, server); - // Advance position by current velocity. - let velocity = entity.velocity.load(); - let new_pos = entity.pos.load().add(&velocity); + // Advance position by current velocity. + let velocity = entity.velocity.load(); + let new_pos = entity.pos.load().add(&velocity); - // Server-side: steer toward target if one is set. - { - let target_guard = self.target.lock().await; - if let Some(target) = *target_guard { - let new_velo = Self::compute_new_velocity(velocity, new_pos, target); - entity.velocity.store(new_velo); - } + // Server-side: steer toward target if one is set. + { + let target_guard = self + .target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(target) = *target_guard { + let new_velo = Self::compute_new_velocity(velocity, new_pos, target); + entity.velocity.store(new_velo); } + } - entity.set_pos(new_pos); - entity.send_pos_rot(); + entity.set_pos(new_pos); + entity.send_pos_rot(); - // Tick lifetime and handle expiry. - let life = self.life.fetch_add(1, Ordering::Relaxed) + 1; - if life > MAX_LIFE { - entity.play_sound(Sound::EntityEnderEyeDeath); - entity.remove().await; + // Tick lifetime and handle expiry. + let life = self.life.fetch_add(1, Ordering::Relaxed) + 1; + if life > MAX_LIFE { + entity.play_sound(Sound::EntityEnderEyeDeath); + entity.remove(); - if self.survive_after_death.load(Ordering::Relaxed) { - // Drop the item at the current position. - let item_stack = self.item_stack.lock().await.clone(); - let world = entity.world.load(); - let entity = Entity::new(world.clone(), new_pos, &EntityType::ITEM); - let item_entity = Arc::new(ItemEntity::new(entity, item_stack)); - world.spawn_entity(item_entity).await; - } else { - entity.world.load().sync_world_event( - WorldEvent::ParticlesEyeOfEnderDeath, - new_pos.to_block_pos(), - 0, - ); - } + if self.survive_after_death.load(Ordering::Relaxed) { + // Drop the item at the current position. + let item_stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let world = entity.world.load(); + let entity = Entity::new(world.clone(), new_pos, &EntityType::ITEM); + let item_entity = Arc::new(ItemEntity::new(entity, item_stack)); + world.spawn_entity(item_entity); + } else { + entity.world.load().sync_world_event( + WorldEvent::ParticlesEyeOfEnderDeath, + new_pos.to_block_pos(), + 0, + ); } - }) + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::eye_of_ender::ITEM_STACK, - &ItemStackSerializer::from(self.item_stack.lock().await.clone()), - )], - None, - ); - }) + fn init_data_tracker(&self) { + self.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::eye_of_ender::ITEM_STACK, + &ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), + )], + None, + ); } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { // Eye of Ender is not attackable. - Box::pin(async { false }) + false } - fn on_player_collision<'a>(&'a self, _player: &'a Arc) -> EntityBaseFuture<'a, ()> { + fn on_player_collision(&self, _player: &Arc) { // Eye of Ender cannot be picked up. - Box::pin(async {}) } fn get_entity(&self) -> &Entity { @@ -227,7 +233,12 @@ impl EntityBase for EyeOfEnder { if client.version.load() >= pumpkin_data::packet::CURRENT_MC_VERSION { let metadata = Metadata::new( pumpkin_data::tracked_data::eye_of_ender::ITEM_STACK, - ItemStackSerializer::from(self.item_stack.lock().await.clone()), + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), ); let mut data = Vec::new(); if metadata.write(&mut data, &client.version.load()).is_ok() { diff --git a/crates/pumpkin/src/entity/projectile/fireball.rs b/crates/pumpkin/src/entity/projectile/fireball.rs index 2307df96f..7cf78694a 100644 --- a/crates/pumpkin/src/entity/projectile/fireball.rs +++ b/crates/pumpkin/src/entity/projectile/fireball.rs @@ -1,6 +1,5 @@ -use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use tokio::sync::RwLock; +use std::sync::{Arc, RwLock}; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; @@ -12,7 +11,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, projectile::{ProjectileHit, ThrownItemEntity}, projectile_deflection::ProjectileDeflectionType, }, @@ -100,11 +99,14 @@ impl FireballEntity { ItemStack::new(1, &Item::FIRE_CHARGE) } - pub async fn get_item(&self) -> ItemStack { - self.item_stack.read().await.clone() + pub fn get_item(&self) -> ItemStack { + self.item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - pub async fn set_item(&self, source: ItemStack) { + pub fn set_item(&self, source: ItemStack) { let new_item = if source.item_count == 0 { Self::get_default_item() } else { @@ -112,7 +114,10 @@ impl FireballEntity { item.item_count = 1; item }; - *self.item_stack.write().await = new_item.clone(); + *self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) = new_item.clone(); self.get_entity().send_meta_data( &[Metadata::new( @@ -189,49 +194,44 @@ impl EntityBase for FireballEntity { }) } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let stack = self.item_stack.read().await; + fn init_data_tracker(&self) { + let entity = self.get_entity(); + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fireball::ITEM_STACK, - &ItemStackSerializer::from(stack.clone()), - )], - None, - ); - }) + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::fireball::ITEM_STACK, + &ItemStackSerializer::from(stack.clone()), + )], + None, + ); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let mut velocity = entity.velocity.load(); + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + let entity = self.get_entity(); + let mut velocity = entity.velocity.load(); - let inertia = if entity.touching_water.load(Ordering::Relaxed) { - WATER_INERTIA - } else { - AIR_INERTIA - }; + let inertia = if entity.touching_water.load(Ordering::Relaxed) { + WATER_INERTIA + } else { + AIR_INERTIA + }; - let accel = self.get_acceleration_power(); - let speed = velocity.length(); - if speed > 1e-6 { - let norm = velocity.normalize(); - velocity = norm - .multiply(accel, accel, accel) - .add(&velocity) - .multiply(inertia, inertia, inertia); - entity.velocity.store(velocity); - } + let accel = self.get_acceleration_power(); + let speed = velocity.length(); + if speed > 1e-6 { + let norm = velocity.normalize(); + velocity = norm + .multiply(accel, accel, accel) + .add(&velocity) + .multiply(inertia, inertia, inertia); + entity.velocity.store(velocity); + } - self.thrown.process_tick(caller, server).await; - }) + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -246,33 +246,24 @@ impl EntityBase for FireballEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); + fn on_hit(&self, hit: ProjectileHit) { + let world = self.get_entity().world.load(); - if let ProjectileHit::Entity { ref entity, .. } = hit { - let entity_clone = entity.clone(); + if let ProjectileHit::Entity { ref entity, .. } = hit { + entity.get_entity().set_on_fire_for(5.0); + let _ = entity.damage( + entity.as_ref(), + 6.0, + pumpkin_data::damage::DamageType::FIREBALL, + ); + } - tokio::spawn(async move { - entity_clone.get_entity().set_on_fire_for(5.0); - let _ = entity_clone - .damage( - entity_clone.as_ref(), - 6.0, - pumpkin_data::damage::DamageType::FIREBALL, - ) - .await; - }); - } - - let hit_pos = hit.hit_pos(); + let hit_pos = hit.hit_pos(); + let power = self.get_explosion_power(); + tokio::spawn(async move { world - .explode( - hit_pos, - self.get_explosion_power(), - crate::world::ExplosionInteraction::Mob, - ) + .explode(hit_pos, power, crate::world::ExplosionInteraction::Mob) .await; - }) + }); } } diff --git a/crates/pumpkin/src/entity/projectile/firework_rocket.rs b/crates/pumpkin/src/entity/projectile/firework_rocket.rs index 1ad6b4c00..63d01ff18 100644 --- a/crates/pumpkin/src/entity/projectile/firework_rocket.rs +++ b/crates/pumpkin/src/entity/projectile/firework_rocket.rs @@ -1,5 +1,5 @@ use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, projectile::ThrownItemEntity}, + entity::{Entity, EntityBase, projectile::ThrownItemEntity}, server::Server, world::World, }; @@ -80,7 +80,7 @@ impl FireworkRocketEntity { rocket } - pub async fn explode_and_remove(&self, world: &World) { + pub fn explode_and_remove(&self, world: &World) { let entity = self.get_entity(); world.send_entity_status( entity, @@ -90,56 +90,50 @@ impl FireworkRocketEntity { // TODO: Explode/colors - entity.remove().await; + entity.remove(); } } impl EntityBase for FireworkRocketEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.entity.process_tick(caller, server).await; + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.entity.process_tick(caller, server); - let entity = self.get_entity(); - let world = entity.world.load(); - let mut velocity = entity.velocity.load(); + let entity = self.get_entity(); + let world = entity.world.load(); + let mut velocity = entity.velocity.load(); - if let Some(shooter_id) = self.entity.owner_id { - // Check if the player who fired this rocket still exists in the world - if let Some(shooter) = world.get_entity_by_id(shooter_id) { - let shooter = shooter.get_entity(); + if let Some(shooter_id) = self.entity.owner_id { + // Check if the player who fired this rocket still exists in the world + if let Some(shooter) = world.get_entity_by_id(shooter_id) { + let shooter = shooter.get_entity(); - // Logic for boosting Elytra flight - if shooter.is_fall_flying() { - let rotation = shooter.rotation().to_f64(); - let shooter_vel = shooter.velocity.load(); + // Logic for boosting Elytra flight + if shooter.is_fall_flying() { + let rotation = shooter.rotation().to_f64(); + let shooter_vel = shooter.velocity.load(); - let new_shooter_vel = - shooter_vel + (rotation * 0.1 + (rotation * 1.5 - shooter_vel) * 0.5); + let new_shooter_vel = + shooter_vel + (rotation * 0.1 + (rotation * 1.5 - shooter_vel) * 0.5); - shooter.set_velocity(new_shooter_vel); + shooter.set_velocity(new_shooter_vel); - entity.set_pos(shooter.pos.load()); - entity.set_velocity(new_shooter_vel); - } + entity.set_pos(shooter.pos.load()); + entity.set_velocity(new_shooter_vel); } - } else { - // Standard firework rocket flight logic - velocity.x *= 1.15; - velocity.z *= 1.15; - velocity.y += 0.04; - entity.set_velocity(velocity); } + } else { + // Standard firework rocket flight logic + velocity.x *= 1.15; + velocity.z *= 1.15; + velocity.y += 0.04; + entity.set_velocity(velocity); + } - // Increment life and check for explosion - let current_life = self.life.fetch_add(1, Ordering::Relaxed); - if current_life > self.life_time.load(Ordering::Relaxed) { - self.explode_and_remove(&world).await; - } - }) + // Increment life and check for explosion + let current_life = self.life.fetch_add(1, Ordering::Relaxed); + if current_life > self.life_time.load(Ordering::Relaxed) { + self.explode_and_remove(&world); + } } fn get_entity(&self) -> &crate::entity::Entity { diff --git a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs index af4cc720e..4a11ac94a 100644 --- a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs +++ b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use crate::entity::projectile::{ProjectileHit, is_projectile}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity, player::Player}, + entity::{Entity, EntityBase, living::LivingEntity, player::Player}, server::Server, }; use pumpkin_data::item_stack::ItemStack; @@ -43,7 +43,7 @@ impl FishingBobberEntity { } } - pub async fn reel_in(&self, player: &Player) -> i32 { + pub fn reel_in(&self, player: &Player) -> i32 { use pumpkin_data::item::Item; let world = self.entity.world.load(); let hooked_id = self.hooked_entity_id.load(Ordering::Relaxed); @@ -64,25 +64,21 @@ impl FishingBobberEntity { if self.bite_countdown.load(Ordering::Relaxed) > 0 { // Caught something! - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::FishCaught as i32, - 1, - ) - .await; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::FishCaught as i32, + 1, + ); // TODO: Use actual loot tables. For now, just give a raw cod. let item_stack = ItemStack::new(1, &Item::COD); // player.inventory().add_item(item_stack).await; // Need public add_item - player - .trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::FishedItem { - item_id: format!("minecraft:{}", item_stack.item.registry_key), - }, - ) - .await; + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::FishedItem { + item_id: format!("minecraft:{}", item_stack.item.registry_key), + }, + ); world.play_sound( Sound::EntityExperienceOrbPickup, @@ -96,7 +92,7 @@ impl FishingBobberEntity { } #[expect(clippy::too_many_lines)] - pub async fn process_tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { + pub fn process_tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { let entity = self.get_entity(); let world = entity.world.load(); @@ -184,9 +180,7 @@ impl FishingBobberEntity { .expand(0.3, 0.3, 0.3); // Basic block collision to stop bobber - let (block_cols, _) = world - .get_block_collisions(search_box, caller.as_ref()) - .await; + let (block_cols, _) = world.get_block_collisions(search_box, caller.as_ref()); if !block_cols.is_empty() { self.in_ground.store(true, Ordering::Relaxed); entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); @@ -236,19 +230,11 @@ impl EntityBase for FishingBobberEntity { fn cast_any(&self) -> &dyn std::any::Any { self } - fn on_hit(&self, _hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.has_hit.store(true, Ordering::Relaxed); - }) + fn on_hit(&self, _hit: ProjectileHit) { + self.has_hit.store(true, Ordering::Relaxed); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.process_tick(caller, server).await; - }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.process_tick(caller, server); } } diff --git a/crates/pumpkin/src/entity/projectile/lingering_potion.rs b/crates/pumpkin/src/entity/projectile/lingering_potion.rs index 5657c03b8..ad1e04ea3 100644 --- a/crates/pumpkin/src/entity/projectile/lingering_potion.rs +++ b/crates/pumpkin/src/entity/projectile/lingering_potion.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, RwLock}; use crate::entity::projectile::splash_potion::extinguish_fire_if_water_potion; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, projectile::ThrownItemEntity}, + entity::{Entity, EntityBase, projectile::ThrownItemEntity}, server::Server, }; use pumpkin_data::entity::EntityStatus; @@ -13,7 +13,6 @@ use pumpkin_protocol::java::client::play::CWorldEvent; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector2::{Vector2, to_chunk_pos}; use pumpkin_util::math::vector3::Vector3; -use tokio::sync::RwLock; use uuid::Uuid; const GRAVITY: f64 = 0.05; @@ -55,37 +54,37 @@ impl LingeringPotionEntity { } } - pub async fn set_item_stack(&self, item_stack: ItemStack) { - let mut write = self.item_stack.write().await; + pub fn set_item_stack(&self, item_stack: ItemStack) { + let mut write = self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); *write = item_stack; } } impl EntityBase for LingeringPotionEntity { - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let stack = self.item_stack.read().await; + fn init_data_tracker(&self) { + let entity = self.get_entity(); + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // Sync the item stack so the client renders the correct potion type - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::lingering_potion::ITEM_STACK, - &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( - stack.clone(), - ), - )], - None, - ); - }) + // Sync the item stack so the client renders the correct potion type + entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::lingering_potion::ITEM_STACK, + &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( + stack.clone(), + ), + )], + None, + ); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -99,54 +98,39 @@ impl EntityBase for LingeringPotionEntity { self } - fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); - let hit_pos = hit.hit_pos(); + fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) { + let world = self.get_entity().world.load(); + let hit_pos = hit.hit_pos(); - // Only extinguish fire for plain water potions - let stack = self.item_stack.read().await.clone(); - extinguish_fire_if_water_potion(&world, hit_pos, &stack).await; + // Read stored item stack and compute potion effects + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); - // Play impact particles - world.send_entity_status( - self.get_entity(), - EntityStatus::Death, - Some(ActorEventID::Death), - ); + // Play impact particles + world.send_entity_status( + self.get_entity(), + EntityStatus::Death, + Some(ActorEventID::Death), + ); - // Read stored item stack and compute potion effects - let stack = self.item_stack.read().await.clone(); - let effects = crate::item::potion::PotionContents::read_potion_effects(&stack); + let effects = crate::item::potion::PotionContents::read_potion_effects(&stack); - // If no effects, just splash (like water bottles) - if effects.is_empty() { - return; - } + // If no effects, just splash (like water bottles) + if effects.is_empty() { + extinguish_fire_if_water_potion(&world, hit_pos, &stack); + return; + } - // Play splash/break particles & sound - let mut color = 0x385dc6; // default water-like color - if let Some(pc) = - stack.get_data_component::() - { - if let Some(c) = pc.custom_color { - color = c; - } else if !effects.is_empty() { - let mut r_sum = 0.0; - let mut g_sum = 0.0; - let mut b_sum = 0.0; - let count = effects.len() as f32; - for (eff, _, _, _, _, _) in &effects { - let c = eff.color; - r_sum += ((c >> 16) & 0xFF) as f32; - g_sum += ((c >> 8) & 0xFF) as f32; - b_sum += (c & 0xFF) as f32; - } - let r = (r_sum / count) as i32; - let g = (g_sum / count) as i32; - let b = (b_sum / count) as i32; - color = (r << 16) | (g << 8) | b; - } + // Play splash/break particles & sound + let mut color = 0x385dc6; // default water-like color + if let Some(pc) = + stack.get_data_component::() + { + if let Some(c) = pc.custom_color { + color = c; } else if !effects.is_empty() { let mut r_sum = 0.0; let mut g_sum = 0.0; @@ -163,43 +147,60 @@ impl EntityBase for LingeringPotionEntity { let b = (b_sum / count) as i32; color = (r << 16) | (g << 8) | b; } + } else if !effects.is_empty() { + let mut r_sum = 0.0; + let mut g_sum = 0.0; + let mut b_sum = 0.0; + let count = effects.len() as f32; + for (eff, _, _, _, _, _) in &effects { + let c = eff.color; + r_sum += ((c >> 16) & 0xFF) as f32; + g_sum += ((c >> 8) & 0xFF) as f32; + b_sum += (c & 0xFF) as f32; + } + let r = (r_sum / count) as i32; + let g = (g_sum / count) as i32; + let b = (b_sum / count) as i32; + color = (r << 16) | (g << 8) | b; + } - let has_instant = effects.iter().any(|(e, _, _, _, _, _)| { - e.id == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id - || e.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id - }); - let event_id = if has_instant { 2007 } else { 2002 }; - let block_pos = BlockPos(Vector3::new( - hit_pos.x.floor() as i32, - hit_pos.y.floor() as i32, - hit_pos.z.floor() as i32, - )); - let chunk_pos = to_chunk_pos(&Vector2::new(block_pos.0.x, block_pos.0.z)); - world.broadcast_to_chunk( - chunk_pos, - &CWorldEvent::new(event_id, block_pos, color, false), - ); + let has_instant = effects.iter().any(|(e, _, _, _, _, _)| { + e.id == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id + || e.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id + }); + let event_id = if has_instant { 2007 } else { 2002 }; + let block_pos = BlockPos(Vector3::new( + hit_pos.x.floor() as i32, + hit_pos.y.floor() as i32, + hit_pos.z.floor() as i32, + )); + let chunk_pos = to_chunk_pos(&Vector2::new(block_pos.0.x, block_pos.0.z)); + world.broadcast_to_chunk( + chunk_pos, + &CWorldEvent::new(event_id, block_pos, color, false), + ); - // Spawn and configure an `AreaEffectCloud` entity - let cloud_entity = crate::entity::Entity::from_uuid( - Uuid::new_v4(), - world.clone(), - hit_pos, - &pumpkin_data::entity::EntityType::AREA_EFFECT_CLOUD, - ); - let cloud = crate::entity::area_effect_cloud::AreaEffectCloudEntity::create( - cloud_entity, - stack.clone(), - effects.clone(), - 600, - 3.0, - 20, - 20, - -0.5, - -100, - ); + // Spawn and configure an `AreaEffectCloud` entity + extinguish_fire_if_water_potion(&world, hit_pos, &stack); - world.spawn_entity(cloud).await; - }) + let cloud_entity = crate::entity::Entity::from_uuid( + Uuid::new_v4(), + world.clone(), + hit_pos, + &pumpkin_data::entity::EntityType::AREA_EFFECT_CLOUD, + ); + let cloud = crate::entity::area_effect_cloud::AreaEffectCloudEntity::create( + cloud_entity, + stack, + effects, + 600, + 3.0, + 20, + 20, + -0.5, + -100, + ); + + world.spawn_entity(cloud); } } diff --git a/crates/pumpkin/src/entity/projectile/llama_spit.rs b/crates/pumpkin/src/entity/projectile/llama_spit.rs index d84d07b80..c78e88d9a 100644 --- a/crates/pumpkin/src/entity/projectile/llama_spit.rs +++ b/crates/pumpkin/src/entity/projectile/llama_spit.rs @@ -6,7 +6,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, living::LivingEntity, projectile::{ProjectileHit, ThrownItemEntity}, }, @@ -57,18 +57,12 @@ impl LlamaSpitEntity { } impl EntityBase for LlamaSpitEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.get_entity().touching_water.load(Ordering::Relaxed) { - self.get_entity().remove().await; - return; - } - self.thrown.process_tick(caller, server).await; - }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + if self.get_entity().touching_water.load(Ordering::Relaxed) { + self.get_entity().remove(); + return; + } + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -83,32 +77,25 @@ impl EntityBase for LlamaSpitEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - if let ProjectileHit::Entity { - ref entity, - hit_pos, - .. - } = hit - { - let entity_clone = entity.clone(); - let world = self.get_entity().world.load(); - let owner_id = self.thrown.owner_id; - let owner = owner_id.and_then(|id| world.get_entity_by_id(id)); + fn on_hit(&self, hit: ProjectileHit) { + if let ProjectileHit::Entity { + ref entity, + hit_pos, + .. + } = hit + { + let world = self.get_entity().world.load(); + let owner_id = self.thrown.owner_id; + let owner = owner_id.and_then(|id| world.get_entity_by_id(id)); - tokio::spawn(async move { - let _ = entity_clone - .damage_with_context( - entity_clone.as_ref(), - 1.0, - DamageType::SPIT, - Some(hit_pos), - None, - owner.as_deref(), - ) - .await; - }); - } - }) + let _ = entity.damage_with_context( + entity.as_ref(), + 1.0, + DamageType::SPIT, + Some(hit_pos), + None, + owner.as_deref(), + ); + } } } diff --git a/crates/pumpkin/src/entity/projectile/mod.rs b/crates/pumpkin/src/entity/projectile/mod.rs index 8e41cadfe..34d0364ae 100644 --- a/crates/pumpkin/src/entity/projectile/mod.rs +++ b/crates/pumpkin/src/entity/projectile/mod.rs @@ -118,7 +118,7 @@ impl ThrownItemEntity { impl ThrownItemEntity { /// Process a tick for projectile movement and collisions - pub async fn process_tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { + pub fn process_tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { let entity = self.get_entity(); let world = entity.world.load(); @@ -169,9 +169,7 @@ impl ThrownItemEntity { let mut hit = None; // Block collisions - let (block_cols, block_positions) = world - .get_block_collisions(search_box, caller.as_ref()) - .await; + let (block_cols, block_positions) = world.get_block_collisions(search_box, caller.as_ref()); for (idx, bb) in block_cols.iter().enumerate() { if let Some(t) = calculate_ray_intersection(&start_pos, &delta, bb) && t < closest_t @@ -224,8 +222,8 @@ impl ThrownItemEntity { } // Just trigger hit effects and remove - caller.on_hit(h).await; - entity.remove().await; + caller.on_hit(h); + entity.remove(); } } diff --git a/crates/pumpkin/src/entity/projectile/shulker_bullet.rs b/crates/pumpkin/src/entity/projectile/shulker_bullet.rs index 2254f6e6d..5fc5d289b 100644 --- a/crates/pumpkin/src/entity/projectile/shulker_bullet.rs +++ b/crates/pumpkin/src/entity/projectile/shulker_bullet.rs @@ -15,7 +15,7 @@ use rand::RngExt; use uuid::Uuid; use crate::entity::mob::shulker::Axis; -use crate::entity::{Entity, EntityBase, EntityBaseFuture}; +use crate::entity::{Entity, EntityBase}; use crate::server::Server; // Direction ordinal constants @@ -284,250 +284,59 @@ impl EntityBase for ShulkerBulletEntity { } /// Any hit destroys the bullet (melee, arrow, etc.). - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - // Guard against double-hit - if self.has_hit.swap(true, Ordering::SeqCst) { - return false; - } - let entity = &self.entity; - let world = entity.world.load(); - let pos = entity.pos.load(); - world.play_sound_fine( - Sound::EntityShulkerBulletHit, - SoundCategory::Hostile, - &pos, - 1.0, - 1.0, - ); - world.spawn_particle( - pos, - Vector3::new(0.2, 0.2, 0.2), - 0.0, - 2, - Particle::Explosion, - ); - entity.remove().await; - true - }) + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + // Guard against double-hit + if self.has_hit.swap(true, Ordering::SeqCst) { + return false; + } + let entity = &self.entity; + let world = entity.world.load(); + let pos = entity.pos.load(); + world.play_sound_fine( + Sound::EntityShulkerBulletHit, + SoundCategory::Hostile, + &pos, + 1.0, + 1.0, + ); + world.spawn_particle( + pos, + Vector3::new(0.2, 0.2, 0.2), + 0.0, + 2, + Particle::Explosion, + ); + entity.remove(); + true } #[allow(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.has_hit.load(Ordering::Relaxed) { - return; - } - - // Discard bullet after 150 ticks if it never hits anything - let age = self.age.fetch_add(1, Ordering::Relaxed) + 1; - if age > 150 { - if !self.has_hit.swap(true, Ordering::SeqCst) { - let entity = &self.entity; - let world = entity.world.load(); - let pos = entity.pos.load(); - world.play_sound_fine( - Sound::EntityShulkerBulletHit, - SoundCategory::Hostile, - &pos, - 1.0, - 1.0, - ); - world.spawn_particle( - pos, - Vector3::new(0.2, 0.2, 0.2), - 0.0, - 2, - Particle::Explosion, - ); - entity.remove().await; - } - return; - } - - let entity = &self.entity; - let world = entity.world.load(); - - let target_id = self.target_id.load(Ordering::Relaxed); - let target_opt = if target_id >= 0 { - world.get_entity_by_id(target_id) - } else { - None - }; - - // Apply gravity only if target is null, dead, or a spectator. - let target_alive = target_opt - .as_ref() - .is_some_and(|t| t.get_entity().is_alive()); - - if !target_alive && target_id >= 0 { - // Target ID is set but entity was not found (dead/left the world). - // Only permanently clear the target when we're sure it's gone. - if let Some(t) = &target_opt { - if !t.get_entity().is_alive() { - self.target_id.store(-1, Ordering::Relaxed); - } - } else { - // Not found -> clear target - self.target_id.store(-1, Ordering::Relaxed); - } - } - - if target_alive { - // Accelerate target deltas x 1.025, clamped - let mut tdx = (self.target_delta_x.load() * 1.025).clamp(-1.0, 1.0); - let mut tdy = (self.target_delta_y.load() * 1.025).clamp(-1.0, 1.0); - let mut tdz = (self.target_delta_z.load() * 1.025).clamp(-1.0, 1.0); - self.target_delta_x.store(tdx); - self.target_delta_y.store(tdy); - self.target_delta_z.store(tdz); - - // Clamp initialised sentinel values - if tdx.abs() < 1e-10 && tdy.abs() < 1e-10 && tdz.abs() < 1e-10 { - tdx = 0.0; - tdy = 0.0; - tdz = 0.0; - } - - // Lerp actual velocity toward steering delta - let mut vel = entity.velocity.load(); - vel.x += (tdx - vel.x) * 0.2; - vel.y += (tdy - vel.y) * 0.2; - vel.z += (tdz - vel.z) * 0.2; - entity.velocity.store(vel); - } else { - // No live target – apply gravity and drift - let mut vel = entity.velocity.load(); - vel.y -= 0.04; - entity.velocity.store(vel); - } - - let vel = entity.velocity.load(); - let old_pos = entity.pos.load(); - let new_pos = old_pos.add(&vel); - entity.set_pos(new_pos); - - // Broadcast position and velocity - let chunk_pos = entity.chunk_pos.load(); - world.broadcast_to_chunk( - chunk_pos, - &CEntityPositionSync::new( - entity.entity_id.into(), - new_pos, - vel, - entity.yaw.load(), - entity.pitch.load(), - false, - ), - ); - world.broadcast_to_chunk( - chunk_pos, - &CEntityVelocity::new(entity.entity_id.into(), vel), - ); - - // Check for block collisions - let new_bp = entity.block_pos.load(); - let state = world.get_block_state(&new_bp); - if !state.is_air() && state.is_solid() { - if !self.has_hit.swap(true, Ordering::SeqCst) { - let pos = entity.pos.load(); - world.play_sound_fine( - Sound::EntityShulkerBulletHit, - SoundCategory::Hostile, - &pos, - 1.0, - 1.0, - ); - world.spawn_particle( - pos, - Vector3::new(0.2, 0.2, 0.2), - 0.0, - 2, - Particle::Explosion, - ); - entity.remove().await; - } - return; - } - - // Check for entity collisions - let bullet_bb = entity.bounding_box.load().expand(0.1, 0.1, 0.1); - let nearby_entities = world.get_entities_at_box(&bullet_bb); - let nearby_players = world.get_players_at_box(&bullet_bb); - let nearby: Vec> = nearby_entities - .into_iter() - .chain( - nearby_players - .into_iter() - .map(|p| p as Arc), - ) - .collect(); - for hit_entity in nearby { - let he = hit_entity.get_entity(); - // Skip self - if he.entity_id == entity.entity_id { - continue; - } - // Never hit the owner shulker - if he.entity_id == self.owner_id { - continue; - } - // Must be alive - if !he.is_alive() { - continue; - } - // Must be a living entity - let Some(living) = hit_entity.get_living_entity() else { - continue; - }; - if !living.entity.is_alive() { - continue; - } - - if self.has_hit.swap(true, Ordering::SeqCst) { - break; - } - - // Deal 4 (MOB_PROJECTILE) damage - let owner_arc = world.get_entity_by_id(self.owner_id); - let damaged = hit_entity - .damage_with_context( - hit_entity.as_ref(), - 4.0, - DamageType::MOB_PROJECTILE, - None, - owner_arc.as_deref(), - Some(caller.as_ref()), - ) - .await; - - if damaged { - // Apply levitation for 200 ticks - living - .add_effect(Effect { - effect_type: &StatusEffect::LEVITATION, - duration: 200, - amplifier: 0, - ambient: false, - show_particles: true, - show_icon: true, - blend: false, - }) - .await; - } + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { + if self.has_hit.load(Ordering::Relaxed) { + return; + } + // Discard bullet after 150 ticks if it never hits anything + let age = self.age.fetch_add(1, Ordering::Relaxed) + 1; + if age > 150 { + if !self.has_hit.swap(true, Ordering::SeqCst) { + let entity = &self.entity; + let world = entity.world.load(); let pos = entity.pos.load(); + world.play_sound_fine( + Sound::EntityShulkerBulletHit, + SoundCategory::Hostile, + &pos, + 1.0, + 1.0, + ); world.spawn_particle( pos, Vector3::new(0.2, 0.2, 0.2), @@ -535,52 +344,231 @@ impl EntityBase for ShulkerBulletEntity { 2, Particle::Explosion, ); - entity.remove().await; + entity.remove(); + } + return; + } + + let entity = &self.entity; + let world = entity.world.load(); + + let target_id = self.target_id.load(Ordering::Relaxed); + let target_opt = if target_id >= 0 { + world.get_entity_by_id(target_id) + } else { + None + }; + + // Apply gravity only if target is null, dead, or a spectator. + let target_alive = target_opt + .as_ref() + .is_some_and(|t| t.get_entity().is_alive()); + + if !target_alive && target_id >= 0 { + // Target ID is set but entity was not found (dead/left the world). + // Only permanently clear the target when we're sure it's gone. + if let Some(t) = &target_opt { + if !t.get_entity().is_alive() { + self.target_id.store(-1, Ordering::Relaxed); + } + } else { + // Not found -> clear target + self.target_id.store(-1, Ordering::Relaxed); + } + } + + if target_alive { + // Accelerate target deltas x 1.025, clamped + let mut tdx = (self.target_delta_x.load() * 1.025).clamp(-1.0, 1.0); + let mut tdy = (self.target_delta_y.load() * 1.025).clamp(-1.0, 1.0); + let mut tdz = (self.target_delta_z.load() * 1.025).clamp(-1.0, 1.0); + self.target_delta_x.store(tdx); + self.target_delta_y.store(tdy); + self.target_delta_z.store(tdz); + + // Clamp initialised sentinel values + if tdx.abs() < 1e-10 && tdy.abs() < 1e-10 && tdz.abs() < 1e-10 { + tdx = 0.0; + tdy = 0.0; + tdz = 0.0; + } + + // Lerp actual velocity toward steering delta + let mut vel = entity.velocity.load(); + vel.x += (tdx - vel.x) * 0.2; + vel.y += (tdy - vel.y) * 0.2; + vel.z += (tdz - vel.z) * 0.2; + entity.velocity.store(vel); + } else { + // No live target – apply gravity and drift + let mut vel = entity.velocity.load(); + vel.y -= 0.04; + entity.velocity.store(vel); + } + + let vel = entity.velocity.load(); + let old_pos = entity.pos.load(); + let new_pos = old_pos.add(&vel); + entity.set_pos(new_pos); + + // Broadcast position and velocity + let chunk_pos = entity.chunk_pos.load(); + world.broadcast_to_chunk( + chunk_pos, + &CEntityPositionSync::new( + entity.entity_id.into(), + new_pos, + vel, + entity.yaw.load(), + entity.pitch.load(), + false, + ), + ); + world.broadcast_to_chunk( + chunk_pos, + &CEntityVelocity::new(entity.entity_id.into(), vel), + ); + + // Check for block collisions + let new_bp = entity.block_pos.load(); + let state = world.get_block_state(&new_bp); + if !state.is_air() && state.is_solid() { + if !self.has_hit.swap(true, Ordering::SeqCst) { + let pos = entity.pos.load(); + world.play_sound_fine( + Sound::EntityShulkerBulletHit, + SoundCategory::Hostile, + &pos, + 1.0, + 1.0, + ); + world.spawn_particle( + pos, + Vector3::new(0.2, 0.2, 0.2), + 0.0, + 2, + Particle::Explosion, + ); + entity.remove(); + } + return; + } + + // Check for entity collisions + let bullet_bb = entity.bounding_box.load().expand(0.1, 0.1, 0.1); + let nearby_entities = world.get_entities_at_box(&bullet_bb); + let nearby_players = world.get_players_at_box(&bullet_bb); + let nearby: Vec> = nearby_entities + .into_iter() + .chain( + nearby_players + .into_iter() + .map(|p| p as Arc), + ) + .collect(); + for hit_entity in nearby { + let he = hit_entity.get_entity(); + // Skip self + if he.entity_id == entity.entity_id { + continue; + } + // Never hit the owner shulker + if he.entity_id == self.owner_id { + continue; + } + // Must be alive + if !he.is_alive() { + continue; + } + // Must be a living entity + let Some(living) = hit_entity.get_living_entity() else { + continue; + }; + if !living.entity.is_alive() { + continue; + } + + if self.has_hit.swap(true, Ordering::SeqCst) { break; } - if !target_alive || self.has_hit.load(Ordering::Relaxed) { - return; + // Deal 4 (MOB_PROJECTILE) damage + let owner_arc = world.get_entity_by_id(self.owner_id); + let damaged = hit_entity.damage_with_context( + hit_entity.as_ref(), + 4.0, + DamageType::MOB_PROJECTILE, + None, + owner_arc.as_deref(), + None, + ); + + if damaged && let Some(living) = hit_entity.get_living_entity() { + // Apply levitation for 200 ticks + living.add_effect(Effect { + effect_type: &StatusEffect::LEVITATION, + duration: 200, + amplifier: 0, + ambient: false, + show_particles: true, + show_icon: true, + blend: false, + }); } - let target_pos = target_opt.as_ref().map(|t| t.get_entity().pos.load()); + let pos = entity.pos.load(); + world.spawn_particle( + pos, + Vector3::new(0.2, 0.2, 0.2), + 0.0, + 2, + Particle::Explosion, + ); + entity.remove(); + break; + } - let raw_dir = self.current_dir.load(Ordering::Relaxed); - let avoid_axis = dir_axis(raw_dir); + if !target_alive || self.has_hit.load(Ordering::Relaxed) { + return; + } - // Decrement flight-step counter; re-select direction when it hits 0 - let steps = self.flight_steps.fetch_sub(1, Ordering::Relaxed) - 1; - if steps <= 0 { - self.select_next_dir(avoid_axis, target_pos); - } + let target_pos = target_opt.as_ref().map(|t| t.get_entity().pos.load()); - // Check the block immediately ahead. - // If it is solid we must re-select; if we've aligned axes with the target we also re-select. - let dir = self.current_dir.load(Ordering::Relaxed); - if dir != DIR_NONE { - let cur_bp = entity.block_pos.load(); - if !is_empty_block(&world, &cur_bp, dir) { - // Solid obstacle -> navigate around it - self.select_next_dir(dir_axis(dir), target_pos); - } else if let Some(tp) = target_pos { - let axis = dir_axis(dir); - let tbp = BlockPos::new( - tp.x.floor() as i32, - tp.y.floor() as i32, - tp.z.floor() as i32, - ); - let cur_bp2 = entity.block_pos.load(); - let reached = match axis { - 0 => cur_bp2.0.x == tbp.0.x, - 1 => cur_bp2.0.y == tbp.0.y, - _ => cur_bp2.0.z == tbp.0.z, - }; - if reached { - // Aligned on this axis -> switch to next best axis - self.select_next_dir(axis, Some(tp)); - } + let raw_dir = self.current_dir.load(Ordering::Relaxed); + let avoid_axis = dir_axis(raw_dir); + + // Decrement flight-step counter; re-select direction when it hits 0 + let steps = self.flight_steps.fetch_sub(1, Ordering::Relaxed) - 1; + if steps <= 0 { + self.select_next_dir(avoid_axis, target_pos); + } + + // Check the block immediately ahead. + // If it is solid we must re-select; if we've aligned axes with the target we also re-select. + let dir = self.current_dir.load(Ordering::Relaxed); + if dir != DIR_NONE { + let cur_bp = entity.block_pos.load(); + if !is_empty_block(&world, &cur_bp, dir) { + // Solid obstacle -> navigate around it + self.select_next_dir(dir_axis(dir), target_pos); + } else if let Some(tp) = target_pos { + let axis = dir_axis(dir); + let tbp = BlockPos::new( + tp.x.floor() as i32, + tp.y.floor() as i32, + tp.z.floor() as i32, + ); + let cur_bp2 = entity.block_pos.load(); + let reached = match axis { + 0 => cur_bp2.0.x == tbp.0.x, + 1 => cur_bp2.0.y == tbp.0.y, + _ => cur_bp2.0.z == tbp.0.z, + }; + if reached { + // Aligned on this axis -> switch to next best axis + self.select_next_dir(axis, Some(tp)); } } - }) + } } } diff --git a/crates/pumpkin/src/entity/projectile/small_fireball.rs b/crates/pumpkin/src/entity/projectile/small_fireball.rs index 97f43bf45..e73ab6fb8 100644 --- a/crates/pumpkin/src/entity/projectile/small_fireball.rs +++ b/crates/pumpkin/src/entity/projectile/small_fireball.rs @@ -3,7 +3,7 @@ use std::sync::atomic::AtomicBool; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, projectile::{ProjectileHit, ThrownItemEntity}, }, server::Server, @@ -37,12 +37,8 @@ impl SmallFireballEntity { } impl EntityBase for SmallFireballEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -56,44 +52,34 @@ impl EntityBase for SmallFireballEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - match hit { - ProjectileHit::Entity { ref entity, .. } => { - let entity_clone = entity.clone(); - - tokio::spawn(async move { - entity_clone.get_entity().set_on_fire_for(5.0); - let _ = entity_clone - .damage( - entity_clone.as_ref(), - 5.0, - pumpkin_data::damage::DamageType::FIREBALL, - ) - .await; - }); - } - ProjectileHit::Block { pos, face, .. } => { - // Try to place fire - let block_to_place = match face { - pumpkin_data::BlockDirection::Up => pos.up(), - pumpkin_data::BlockDirection::Down => pos.down(), - pumpkin_data::BlockDirection::North => pos.north(), - pumpkin_data::BlockDirection::South => pos.south(), - pumpkin_data::BlockDirection::West => pos.west(), - pumpkin_data::BlockDirection::East => pos.east(), - }; - let world = self.get_entity().world.load(); - let fire_state = pumpkin_data::Block::FIRE.default_state.id; - world - .set_block_state( - &block_to_place, - fire_state, - pumpkin_world::world::BlockFlags::NOTIFY_ALL, - ) - .await; - } + fn on_hit(&self, hit: ProjectileHit) { + match hit { + ProjectileHit::Entity { ref entity, .. } => { + entity.get_entity().set_on_fire_for(5.0); + let _ = entity.damage( + entity.as_ref(), + 5.0, + pumpkin_data::damage::DamageType::FIREBALL, + ); } - }) + ProjectileHit::Block { pos, face, .. } => { + // Try to place fire + let block_to_place = match face { + pumpkin_data::BlockDirection::Up => pos.up(), + pumpkin_data::BlockDirection::Down => pos.down(), + pumpkin_data::BlockDirection::North => pos.north(), + pumpkin_data::BlockDirection::South => pos.south(), + pumpkin_data::BlockDirection::West => pos.west(), + pumpkin_data::BlockDirection::East => pos.east(), + }; + let world = self.get_entity().world.load(); + let fire_state = pumpkin_data::Block::FIRE.default_state.id; + world.set_block_state( + &block_to_place, + fire_state, + pumpkin_world::world::BlockFlags::NOTIFY_ALL, + ); + } + } } } diff --git a/crates/pumpkin/src/entity/projectile/snowball.rs b/crates/pumpkin/src/entity/projectile/snowball.rs index ecc3da48f..da67c594c 100644 --- a/crates/pumpkin/src/entity/projectile/snowball.rs +++ b/crates/pumpkin/src/entity/projectile/snowball.rs @@ -3,7 +3,7 @@ use std::sync::atomic::AtomicBool; use crate::entity::projectile::ProjectileHit; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, projectile::ThrownItemEntity}, + entity::{Entity, EntityBase, projectile::ThrownItemEntity}, server::Server, }; use pumpkin_data::damage::DamageType; @@ -42,12 +42,8 @@ impl SnowballEntity { } impl EntityBase for SnowballEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -61,30 +57,22 @@ impl EntityBase for SnowballEntity { self } - fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); + fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) { + let world = self.get_entity().world.load(); - // Always send particle status regardless of what was hit - world.send_entity_status( - self.get_entity(), - EntityStatus::Death, - Some(ActorEventID::Death), - ); + // Always send particle status regardless of what was hit + world.send_entity_status( + self.get_entity(), + EntityStatus::Death, + Some(ActorEventID::Death), + ); - // Handle entity-specific damage - if let ProjectileHit::Entity { ref entity, .. } = hit { - let entity_clone = entity.clone(); + // Handle entity-specific damage + if let ProjectileHit::Entity { ref entity, .. } = hit { + let is_blaze = entity.get_entity().entity_type.id == EntityType::BLAZE.id; + let damage = if is_blaze { 3.0 } else { 0.0 }; // Only damage blazes - tokio::spawn(async move { - let is_blaze = entity_clone.get_entity().entity_type.id == EntityType::BLAZE.id; - let damage = if is_blaze { 3.0 } else { 0.0 }; // Only damage blazes - - entity_clone - .damage(entity_clone.as_ref(), damage, DamageType::THROWN) - .await; - }); - } - }) + entity.damage(entity.as_ref(), damage, DamageType::THROWN); + } } } diff --git a/crates/pumpkin/src/entity/projectile/splash_potion.rs b/crates/pumpkin/src/entity/projectile/splash_potion.rs index ef9b37f1c..d09085ad7 100644 --- a/crates/pumpkin/src/entity/projectile/splash_potion.rs +++ b/crates/pumpkin/src/entity/projectile/splash_potion.rs @@ -1,8 +1,8 @@ -use std::sync::Arc; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, RwLock}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, projectile::ThrownItemEntity}, + entity::{Entity, EntityBase, projectile::ThrownItemEntity}, server::Server, }; use pumpkin_data::item_stack::ItemStack; @@ -12,7 +12,6 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::{boundingbox::BoundingBox, vector2::Vector2}; use pumpkin_util::math::{position::BlockPos, vector2::to_chunk_pos}; use pumpkin_world::world::BlockFlags; -use tokio::sync::RwLock; const GRAVITY: f64 = 0.05; @@ -47,8 +46,11 @@ impl SplashPotionEntity { } } - pub async fn set_item_stack(&self, item_stack: ItemStack) { - let mut write = self.item_stack.write().await; + pub fn set_item_stack(&self, item_stack: ItemStack) { + let mut write = self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); *write = item_stack; } } @@ -61,7 +63,7 @@ fn is_water_potion(stack: &ItemStack) -> bool { } /// Extinguishes fire (including soul fire) at the hit position and its four horizontal neighbors. -async fn extinguish_fire(world: &Arc, hit_pos: Vector3) { +fn extinguish_fire(world: &Arc, hit_pos: Vector3) { let air_state_id = Block::AIR.default_state.id; let neighbors = [ @@ -81,48 +83,43 @@ async fn extinguish_fire(world: &Arc, hit_pos: Vector3 let state_id = world.get_block_state_id(&pos); let raw_block_id = state_id.to_block_id(); if raw_block_id == BlockId::FIRE || raw_block_id == BlockId::SOUL_FIRE { - world - .set_block_state(&pos, air_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, air_state_id, BlockFlags::NOTIFY_ALL); } } } -pub(crate) async fn extinguish_fire_if_water_potion( +pub(crate) fn extinguish_fire_if_water_potion( world: &Arc, hit_pos: Vector3, stack: &ItemStack, ) { if is_water_potion(stack) { - extinguish_fire(world, hit_pos).await; + extinguish_fire(world, hit_pos); } } impl EntityBase for SplashPotionEntity { - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let stack = self.item_stack.read().await; + fn init_data_tracker(&self) { + let entity = self.get_entity(); + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // Sync the item stack - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::splash_potion::ITEM_STACK, - &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( - stack.clone(), - ), - )], - None, - ); - }) + // Sync the item stack + entity.send_meta_data( + &[pumpkin_protocol::java::client::play::Metadata::new( + pumpkin_data::tracked_data::splash_potion::ITEM_STACK, + &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( + stack.clone(), + ), + )], + None, + ); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { self.thrown.process_tick(caller, server).await }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -136,122 +133,123 @@ impl EntityBase for SplashPotionEntity { self } - fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); - let hit_pos = hit.hit_pos(); + fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) { + let world = self.get_entity().world.load(); + let hit_pos = hit.hit_pos(); - // Only extinguish fire for plain water potions - let stack = self.item_stack.read().await.clone(); - extinguish_fire_if_water_potion(&world, hit_pos, &stack).await; + // Extinguish fire if it's a water potion + let stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + extinguish_fire_if_water_potion(&world, hit_pos, &stack); - let effects = crate::item::potion::PotionContents::read_potion_effects(&stack); + // Send impact entity status (plays break particles) + world.send_entity_status( + self.get_entity(), + pumpkin_data::entity::EntityStatus::Death, + Some(pumpkin_protocol::bedrock::server::actor_event::ActorEventID::Death), + ); - let mut color = 0x385dc6; // Default to water color if no effects/color found - if let Some(pc) = - stack.get_data_component::() - { - if let Some(c) = pc.custom_color { - color = c; - } else if !effects.is_empty() { - let mut r_sum = 0.0; - let mut g_sum = 0.0; - let mut b_sum = 0.0; - let count = effects.len() as f32; - for (eff, _, _, _, _, _) in &effects { - let c = eff.color; - r_sum += ((c >> 16) & 0xFF) as f32; - g_sum += ((c >> 8) & 0xFF) as f32; - b_sum += (c & 0xFF) as f32; - } - let r = (r_sum / count) as i32; - let g = (g_sum / count) as i32; - let b = (b_sum / count) as i32; - color = (r << 16) | (g << 8) | b; + let effects = crate::item::potion::PotionContents::read_potion_effects(&stack); + + // Calculate color: custom_color if present, else blend of effects, else default water color + let mut color = 0x385dc6; // default water-like color + if let Some(pc) = + stack.get_data_component::() + { + if let Some(c) = pc.custom_color { + color = c; + } else if !effects.is_empty() { + let mut r_sum = 0.0; + let mut g_sum = 0.0; + let mut b_sum = 0.0; + let count = effects.len() as f32; + for (eff, _, _, _, _, _) in &effects { + let c = eff.color; + r_sum += ((c >> 16) & 0xFF) as f32; + g_sum += ((c >> 8) & 0xFF) as f32; + b_sum += (c & 0xFF) as f32; } - } else { - // Try to guess from effects directly if potion contents missing but effects present - if !effects.is_empty() { - let mut r_sum = 0.0; - let mut g_sum = 0.0; - let mut b_sum = 0.0; - let count = effects.len() as f32; - for (eff, _, _, _, _, _) in &effects { - let c = eff.color; - r_sum += ((c >> 16) & 0xFF) as f32; - g_sum += ((c >> 8) & 0xFF) as f32; - b_sum += (c & 0xFF) as f32; - } - let r = (r_sum / count) as i32; - let g = (g_sum / count) as i32; - let b = (b_sum / count) as i32; - color = (r << 16) | (g << 8) | b; + let r = (r_sum / count) as i32; + let g = (g_sum / count) as i32; + let b = (b_sum / count) as i32; + color = (r << 16) | (g << 8) | b; + } + } else if !effects.is_empty() { + let mut r_sum = 0.0; + let mut g_sum = 0.0; + let mut b_sum = 0.0; + let count = effects.len() as f32; + for (eff, _, _, _, _, _) in &effects { + let c = eff.color; + r_sum += ((c >> 16) & 0xFF) as f32; + g_sum += ((c >> 8) & 0xFF) as f32; + b_sum += (c & 0xFF) as f32; + } + let r = (r_sum / count) as i32; + let g = (g_sum / count) as i32; + let b = (b_sum / count) as i32; + color = (r << 16) | (g << 8) | b; + } + + // Play splash particles + let has_instant = effects.iter().any(|(e, _, _, _, _, _)| { + e.id == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id + || e.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id + }); + let event_id = if has_instant { 2007 } else { 2002 }; + + // Convert hit_pos to BlockPos + let block_pos = BlockPos(Vector3::new( + hit_pos.x.floor() as i32, + hit_pos.y.floor() as i32, + hit_pos.z.floor() as i32, + )); + world.broadcast_to_chunk( + to_chunk_pos(&Vector2::new(block_pos.0.x, block_pos.0.z)), + &CWorldEvent::new(event_id, block_pos, color, false), + ); + + // If no effects, just splash (like water bottles) + if effects.is_empty() { + return; + } + + let radius = 4.0f64; + let min = Vector3::new(hit_pos.x - radius, hit_pos.y - radius, hit_pos.z - radius); + let max = Vector3::new(hit_pos.x + radius, hit_pos.y + radius, hit_pos.z + radius); + let aabb = BoundingBox::new(min, max); + + // Gather entity and player candidates + let mut candidates = world.get_entities_at_box(&aabb); + let players = world.get_players_at_box(&aabb); + for p in players { + candidates.push(p.clone() as Arc); + } + + for cand in candidates { + if let Some(living) = cand.get_living_entity() { + let pos = cand.get_entity().pos.load(); + let dx = pos.x - hit_pos.x; + let dy = pos.y - hit_pos.y; + let dz = pos.z - hit_pos.z; + let dist = (dx * dx + dy * dy + dz * dz).sqrt(); + if dist > radius { + continue; } + + // Distance scaling + let scale = (1.0f32 - (dist as f32 / radius as f32)).max(0.0); + + crate::item::potion::PotionContents::apply_effects_to( + living, + effects.clone(), + scale, + crate::item::potion::PotionApplicationSource::Normal, + ); } - - // Play splash particles - let has_instant = effects.iter().any(|(e, _, _, _, _, _)| { - e.id == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id - || e.id == pumpkin_data::effect::StatusEffect::INSTANT_HEALTH.id - }); - let event_id = if has_instant { 2007 } else { 2002 }; - - // Convert hit_pos to BlockPos - let block_pos = BlockPos(Vector3::new( - hit_pos.x.floor() as i32, - hit_pos.y.floor() as i32, - hit_pos.z.floor() as i32, - )); - world.broadcast_to_chunk( - to_chunk_pos(&Vector2::new(block_pos.0.x, block_pos.0.z)), - &CWorldEvent::new(event_id, block_pos, color, false), - ); - - // If no effects, just splash (like water bottles) - if effects.is_empty() { - return; - } - - let radius = 4.0f64; - let min = Vector3::new(hit_pos.x - radius, hit_pos.y - radius, hit_pos.z - radius); - let max = Vector3::new(hit_pos.x + radius, hit_pos.y + radius, hit_pos.z + radius); - let aabb = BoundingBox::new(min, max); - - // Gather entity and player candidates - let mut candidates = world.get_entities_at_box(&aabb); - let players = world.get_players_at_box(&aabb); - for p in players { - candidates.push(p.clone() as Arc); - } - - for cand in candidates { - let cand_clone = cand.clone(); - let effs_clone: Vec<_> = effects.clone(); - let hit_pos_clone = hit_pos; - tokio::spawn(async move { - if let Some(living) = cand_clone.get_living_entity() { - let pos = cand_clone.get_entity().pos.load(); - let dx = pos.x - hit_pos_clone.x; - let dy = pos.y - hit_pos_clone.y; - let dz = pos.z - hit_pos_clone.z; - let dist = (dx * dx + dy * dy + dz * dz).sqrt(); - if dist > radius { - return; - } - - // Distance scaling - let scale = (1.0f32 - (dist as f32 / radius as f32)).max(0.0); - - crate::item::potion::PotionContents::apply_effects_to( - living, - effs_clone, - scale, - crate::item::potion::PotionApplicationSource::Normal, - ) - .await; - } - }); - } - }) + } } } diff --git a/crates/pumpkin/src/entity/projectile/trident.rs b/crates/pumpkin/src/entity/projectile/trident.rs index 0c2aaad43..3c1d46319 100644 --- a/crates/pumpkin/src/entity/projectile/trident.rs +++ b/crates/pumpkin/src/entity/projectile/trident.rs @@ -1,9 +1,8 @@ -use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering}; -use tokio::sync::Mutex; +use std::sync::{Arc, Mutex}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity, player::Player}, + entity::{Entity, EntityBase, living::LivingEntity, player::Player}, server::Server, }; use pumpkin_data::damage::DamageType; @@ -153,139 +152,132 @@ impl TridentEntity { } impl EntityBase for TridentEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let world = entity.world.load(); + fn tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { + let entity = self.get_entity(); + let world = entity.world.load(); - // Handle shake time - let shake = self.shake_time.load(Ordering::Relaxed); - if shake > 0 { - self.shake_time.store(shake - 1, Ordering::Relaxed); + // Handle shake time + let shake = self.shake_time.load(Ordering::Relaxed); + if shake > 0 { + self.shake_time.store(shake - 1, Ordering::Relaxed); + } + + if self.in_ground.load(Ordering::Relaxed) { + let _in_ground_time = self.in_ground_time.fetch_add(1, Ordering::Relaxed); + let life = self.life.fetch_add(1, Ordering::Relaxed); + + // Despawn after enough time + if life >= Self::DESPAWN_TIME { + entity.remove(); } + return; + } - if self.in_ground.load(Ordering::Relaxed) { - let _in_ground_time = self.in_ground_time.fetch_add(1, Ordering::Relaxed); - let life = self.life.fetch_add(1, Ordering::Relaxed); + // Trident is flying + let start_pos = entity.pos.load(); + let mut velocity = entity.velocity.load(); - // Despawn after enough time - if life >= Self::DESPAWN_TIME { - entity.remove().await; - } - return; - } + // Apply gravity + velocity.y -= Self::GRAVITY; - // Trident is flying - let start_pos = entity.pos.load(); - let mut velocity = entity.velocity.load(); + // Apply inertia (air resistance or water drag) + let inertia = if entity.touching_water.load(Ordering::Relaxed) { + Self::WATER_INERTIA + } else { + Self::AIR_INERTIA + }; + velocity = velocity.multiply(inertia, inertia, inertia); - // Apply gravity - velocity.y -= Self::GRAVITY; + entity.velocity.store(velocity); - // Apply inertia (air resistance or water drag) - let inertia = if entity.touching_water.load(Ordering::Relaxed) { - Self::WATER_INERTIA - } else { - Self::AIR_INERTIA - }; - velocity = velocity.multiply(inertia, inertia, inertia); + // Update rotation based on velocity + let len = velocity.horizontal_length(); + entity.set_rotation( + velocity.x.atan2(velocity.z) as f32 * 57.295_776, + velocity.y.atan2(len) as f32 * 57.295_776, + ); - entity.velocity.store(velocity); + // Move trident + let new_pos = start_pos.add(&velocity); + entity.set_pos(new_pos); - // Update rotation based on velocity - let len = velocity.horizontal_length(); - entity.set_rotation( - velocity.x.atan2(velocity.z) as f32 * 57.295_776, - velocity.y.atan2(len) as f32 * 57.295_776, - ); + // Broadcast velocity update + let packet = CEntityVelocity::new(entity.entity_id.into(), velocity); + let chunk_pos = entity.chunk_pos.load(); + world.broadcast_to_chunk(chunk_pos, &packet); - // Move trident - let new_pos = start_pos.add(&velocity); - entity.set_pos(new_pos); + // Check for collisions using raycasting + let search_box = BoundingBox::new( + Vector3::new( + start_pos.x.min(new_pos.x), + start_pos.y.min(new_pos.y), + start_pos.z.min(new_pos.z), + ), + Vector3::new( + start_pos.x.max(new_pos.x), + start_pos.y.max(new_pos.y), + start_pos.z.max(new_pos.z), + ), + ) + .expand(0.3, 0.3, 0.3); - // Broadcast velocity update - let packet = CEntityVelocity::new(entity.entity_id.into(), velocity); - let chunk_pos = entity.chunk_pos.load(); - world.broadcast_to_chunk(chunk_pos, &packet); + let mut closest_t = 1.0f64; + let mut hit = None; - // Check for collisions using raycasting - let search_box = BoundingBox::new( - Vector3::new( - start_pos.x.min(new_pos.x), - start_pos.y.min(new_pos.y), - start_pos.z.min(new_pos.z), - ), - Vector3::new( - start_pos.x.max(new_pos.x), - start_pos.y.max(new_pos.y), - start_pos.z.max(new_pos.z), - ), - ) - .expand(0.3, 0.3, 0.3); + // Block collisions + let (block_cols, block_positions) = + world.get_block_collisions(search_box, self.get_entity()); + for (idx, bb) in block_cols.iter().enumerate() { + if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, bb) + && t < closest_t + { + closest_t = t; - let mut closest_t = 1.0f64; - let mut hit = None; - - // Block collisions - let (block_cols, block_positions) = world - .get_block_collisions(search_box, self.get_entity()) - .await; - for (idx, bb) in block_cols.iter().enumerate() { - if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, bb) - && t < closest_t - { - closest_t = t; - - // Map back to block pos - let mut curr = 0; - for (len, pos) in &block_positions { - curr += len; - if idx < curr { - let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); - hit = Some(ProjectileHit::Block { - pos: *pos, - face: get_hit_face(hit_pos, *pos), - hit_pos, - normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), - }); - break; - } + // Map back to block pos + let mut curr = 0; + for (len, pos) in &block_positions { + curr += len; + if idx < curr { + let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); + hit = Some(ProjectileHit::Block { + pos: *pos, + face: get_hit_face(hit_pos, *pos), + hit_pos, + normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), + }); + break; } } } + } - // Entity collisions - let candidates = world.get_entities_at_box(&search_box); - for cand in candidates { - if self.should_skip_collision(entity, &cand) { - continue; - } - - let ebb = cand.get_entity().bounding_box.load().expand(0.3, 0.3, 0.3); - if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, &ebb) - && t < closest_t - { - closest_t = t; - let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); - hit = Some(ProjectileHit::Entity { - entity: cand.clone(), - hit_pos, - normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), - }); - } + // Entity collisions + let candidates = world.get_entities_at_box(&search_box); + for cand in candidates { + if self.should_skip_collision(entity, &cand) { + continue; } - // Handle hit - if let Some(h) = hit - && !self.has_hit.swap(true, Ordering::SeqCst) + let ebb = cand.get_entity().bounding_box.load().expand(0.3, 0.3, 0.3); + if let Some(t) = calculate_ray_intersection(&start_pos, &velocity, &ebb) + && t < closest_t { - caller.on_hit(h).await; + closest_t = t; + let hit_pos = start_pos.add(&velocity.multiply(t, t, t)); + hit = Some(ProjectileHit::Entity { + entity: cand.clone(), + hit_pos, + normal: velocity.normalize().multiply(-1.0, -1.0, -1.0), + }); } - }) + } + + // Handle hit + if let Some(h) = hit + && !self.has_hit.swap(true, Ordering::SeqCst) + { + caller.on_hit(h); + } } fn get_entity(&self) -> &Entity { @@ -299,107 +291,106 @@ impl EntityBase for TridentEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - let world = entity.world.load(); + fn on_hit(&self, hit: ProjectileHit) { + let entity = self.get_entity(); + let world = entity.world.load(); - match hit { - ProjectileHit::Block { pos, hit_pos, .. } => { - self.in_ground.store(true, Ordering::Relaxed); - self.shake_time.store(7, Ordering::Relaxed); - *self - .last_block_pos - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(pos); + match hit { + ProjectileHit::Block { pos, hit_pos, .. } => { + self.in_ground.store(true, Ordering::Relaxed); + self.shake_time.store(7, Ordering::Relaxed); + *self + .last_block_pos + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(pos); - // Stop the trident - entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); - entity.set_pos(hit_pos); + // Stop the trident + entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); + entity.set_pos(hit_pos); - // Play sound - let sound_packet = CSoundEffect::new( - IdOr::Id(Sound::ItemTridentHitGround as u16), - SoundCategory::Neutral, - &hit_pos, - 1.0, - 1.0, - 0.0, - ); - let chunk_pos = entity.chunk_pos.load(); - world.broadcast_to_chunk(chunk_pos, &sound_packet); - } - ProjectileHit::Entity { - entity: target, - hit_pos, - .. - } => { - let mut damage = Self::BASE_DAMAGE; + // Play sound + let sound_packet = CSoundEffect::new( + IdOr::Id(Sound::ItemTridentHitGround as u16), + SoundCategory::Neutral, + &hit_pos, + 1.0, + 1.0, + 0.0, + ); + let chunk_pos = entity.chunk_pos.load(); + world.broadcast_to_chunk(chunk_pos, &sound_packet); + } + ProjectileHit::Entity { + entity: target, + hit_pos, + .. + } => { + let mut damage = Self::BASE_DAMAGE; - // Apply Impaling enchantment extra damage - if let Some(enchantments) = self - .item_stack - .lock() - .await - .get_data_component::( - ) { - for (enchantment, level) in enchantments.enchantment.iter() { - if **enchantment == pumpkin_data::Enchantment::IMPALING { - let in_water = - target.get_entity().touching_water.load(Ordering::Relaxed); - if in_water { - damage += 1.25 * f64::from(*level); - } + // Apply Impaling enchantment extra damage + if let Some(enchantments) = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get_data_component::() + { + for (enchantment, level) in enchantments.enchantment.iter() { + if **enchantment == pumpkin_data::Enchantment::IMPALING { + let in_water = + target.get_entity().touching_water.load(Ordering::Relaxed); + if in_water { + damage += 1.25 * f64::from(*level); } } } - - target - .damage(&*target, damage as f32, DamageType::TRIDENT) - .await; - - // Play hit sound - let sound_packet = CSoundEffect::new( - IdOr::Id(Sound::ItemTridentHit as u16), - SoundCategory::Neutral, - &hit_pos, - 1.0, - 1.0, - 0.0, - ); - world.broadcast_packet_all(&sound_packet); - - // Standard bounce/fall-back behavior - entity.velocity.store(Vector3::new(0.0, -0.1, 0.0)); - self.has_hit.store(false, Ordering::Relaxed); // Let it hit the ground } + + let damage_val = damage as f32; + target.damage(&*target, damage_val, DamageType::TRIDENT); + + // Play hit sound + let sound_packet = CSoundEffect::new( + IdOr::Id(Sound::ItemTridentHit as u16), + SoundCategory::Neutral, + &hit_pos, + 1.0, + 1.0, + 0.0, + ); + world.broadcast_packet_all(&sound_packet); + + // Standard bounce/fall-back behavior + entity.velocity.store(Vector3::new(0.0, -0.1, 0.0)); + self.has_hit.store(false, Ordering::Relaxed); // Let it hit the ground } - }) + } } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Can only pick up when on the ground - if !self.in_ground.load(Ordering::Relaxed) { - return; - } + fn on_player_collision(&self, player: &Arc) { + // Can only pick up when on the ground + if !self.in_ground.load(Ordering::Relaxed) { + return; + } - if player.living_entity.health.load() <= 0.0 { - return; - } + if player.living_entity.health.load() <= 0.0 { + return; + } - match self.pickup { - ArrowPickup::Disallowed => return, - ArrowPickup::CreativeOnly if !player.is_creative() => return, - _ => {} - } + match self.pickup { + ArrowPickup::Disallowed => return, + ArrowPickup::CreativeOnly if !player.is_creative() => return, + _ => {} + } - let mut stack = self.item_stack.lock().await.clone(); - if player.is_creative() || player.inventory.insert_stack_anywhere(&mut stack).await { - player.living_entity.pickup(&self.entity, 1); - self.get_entity().remove().await; - } - }) + let mut stack = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + if player.is_creative() || player.inventory.insert_stack_anywhere(&mut stack) { + player.living_entity.pickup(&self.entity, 1); + self.get_entity().remove(); + } } } diff --git a/crates/pumpkin/src/entity/projectile/wind_charge.rs b/crates/pumpkin/src/entity/projectile/wind_charge.rs index 6b2086671..e2dc020d0 100644 --- a/crates/pumpkin/src/entity/projectile/wind_charge.rs +++ b/crates/pumpkin/src/entity/projectile/wind_charge.rs @@ -9,7 +9,7 @@ use std::{ use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, living::LivingEntity, projectile::ThrownItemEntity, + Entity, EntityBase, living::LivingEntity, projectile::ThrownItemEntity, projectile_deflection::ProjectileDeflectionType, }, server::Server, @@ -130,21 +130,15 @@ impl WindChargeEntity { } impl EntityBase for WindChargeEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.thrown_item_entity.process_tick(caller, server).await; + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown_item_entity.process_tick(caller, server); - if let Some(cooldown) = self.deflect_cooldown() { - let cooldown_ticks = cooldown.load(Ordering::Relaxed); - if cooldown_ticks > 0 { - cooldown.store(cooldown_ticks - 1, Ordering::Relaxed); - } + if let Some(cooldown) = self.deflect_cooldown() { + let cooldown_ticks = cooldown.load(Ordering::Relaxed); + if cooldown_ticks > 0 { + cooldown.store(cooldown_ticks - 1, Ordering::Relaxed); } - }) + } } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/wither_skull.rs b/crates/pumpkin/src/entity/projectile/wither_skull.rs index 80655345b..303a3a536 100644 --- a/crates/pumpkin/src/entity/projectile/wither_skull.rs +++ b/crates/pumpkin/src/entity/projectile/wither_skull.rs @@ -8,7 +8,7 @@ use pumpkin_util::{Difficulty, math::vector3::Vector3}; use crate::{ entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, NbtFuture, projectile::{ProjectileHit, ThrownItemEntity}, }, server::Server, @@ -95,27 +95,19 @@ impl EntityBase for WitherSkullEntity { }) } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - tracked_data::wither_skull::DATA_DANGEROUS, - self.is_dangerous(), - )], - None, - ); - }) + fn init_data_tracker(&self) { + let entity = self.get_entity(); + entity.send_meta_data( + &[Metadata::new( + tracked_data::wither_skull::DATA_DANGEROUS, + self.is_dangerous(), + )], + None, + ); } - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.thrown.process_tick(caller, server).await; - }) + fn tick<'a>(&'a self, caller: &'a Arc, server: &'a Server) { + self.thrown.process_tick(caller, server); } fn get_entity(&self) -> &Entity { @@ -130,47 +122,43 @@ impl EntityBase for WitherSkullEntity { self } - fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let world = self.get_entity().world.load(); + fn on_hit(&self, hit: ProjectileHit) { + let world = self.get_entity().world.load(); - if let ProjectileHit::Entity { ref entity, .. } = hit { - let entity_clone = entity.clone(); - let difficulty = world.level_info.load().difficulty; + if let ProjectileHit::Entity { ref entity, .. } = hit { + let difficulty = world.level_info.load().difficulty; - tokio::spawn(async move { - let _ = entity_clone - .damage(entity_clone.as_ref(), 8.0, DamageType::WITHER_SKULL) - .await; + let _ = entity.damage(entity.as_ref(), 8.0, DamageType::WITHER_SKULL); - if let Some(living) = entity_clone.get_living_entity() { - let duration = match difficulty { - Difficulty::Hard => 800, // 40 seconds - Difficulty::Normal => 200, // 10 seconds - Difficulty::Easy | Difficulty::Peaceful => 0, - }; + if let Some(living) = entity.get_living_entity() { + let duration = match difficulty { + Difficulty::Hard => 800, // 40 seconds + Difficulty::Normal => 200, // 10 seconds + Difficulty::Easy | Difficulty::Peaceful => 0, + }; - if duration > 0 { - let effect = Effect { - effect_type: &StatusEffect::WITHER, - duration, - amplifier: 1, - ambient: false, - show_particles: true, - show_icon: true, - blend: true, - }; - if let Some(player) = entity_clone.get_player() { - player.send_effect(effect.clone()).await; - } - living.add_effect(effect).await; - } + if duration > 0 { + let effect = Effect { + effect_type: &StatusEffect::WITHER, + duration, + amplifier: 1, + ambient: false, + show_particles: true, + show_icon: true, + blend: true, + }; + if let Some(player) = entity.get_player() { + player.add_effect(effect); + } else { + living.add_effect(effect); } - }); + } } + } - let hit_pos = hit.hit_pos(); + let hit_pos = hit.hit_pos(); + tokio::spawn(async move { world.explode(hit_pos, 1.0, ExplosionInteraction::Mob).await; - }) + }); } } diff --git a/crates/pumpkin/src/entity/tnt.rs b/crates/pumpkin/src/entity/tnt.rs index 840caaf68..274d87d33 100644 --- a/crates/pumpkin/src/entity/tnt.rs +++ b/crates/pumpkin/src/entity/tnt.rs @@ -1,5 +1,5 @@ use super::{Entity, EntityBase, living::LivingEntity}; -use crate::{entity::EntityBaseFuture, server::Server}; +use crate::server::Server; use core::f32; use pumpkin_data::Block; use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; @@ -32,80 +32,72 @@ impl TNTEntity { } impl EntityBase for TNTEntity { - fn tick<'a>( - &'a self, - caller: &'a Arc, - server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let entity = &self.entity; + fn tick(&self, caller: &Arc, server: &Server) { + let entity = &self.entity; - let mut velo = entity.velocity.load(); - velo.y -= self.get_gravity(); + let mut velo = entity.velocity.load(); + velo.y -= self.get_gravity(); - entity.move_entity(caller, velo).await; - entity.tick_block_collisions(caller, server).await; + entity.move_entity(caller, velo); + entity.tick_block_collisions(caller, server); - // Read back what actually happened instead of reusing the pre-move - // value: `move_entity` clamps on collision, and an explosion may have - // pushed us while we were awaiting above - let velo = entity.velocity.load(); - if entity.on_ground.load(Ordering::Relaxed) { - entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); - } else { - entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); - } + // Read back what actually happened instead of reusing the pre-move + // value: `move_entity` clamps on collision, and an explosion may have + // pushed us while we were moving above + let velo = entity.velocity.load(); + if entity.on_ground.load(Ordering::Relaxed) { + entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); + } else { + entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); + } - if entity.velocity_dirty.swap(false, Ordering::SeqCst) { - entity.send_pos_rot(); - entity.send_velocity(); - } + if entity.velocity_dirty.swap(false, Ordering::SeqCst) { + entity.send_pos_rot(); + entity.send_velocity(); + } - // FIX: Prevent fuse underflow (vanilla parity) - let fuse = self.fuse.load(Relaxed); + // FIX: Prevent fuse underflow (vanilla parity) + let fuse = self.fuse.load(Relaxed); - if fuse <= 1 { - // TNT explodes now - self.entity.remove().await; - let world = self.entity.world.load(); + if fuse <= 1 { + // TNT explodes now + self.entity.remove(); + let world = self.entity.world.load_full(); + let pos = self.entity.pos.load(); + let power = self.power; + tokio::spawn(async move { if world.level_info.load().game_rules.tnt_explodes { world - .explode( - self.entity.pos.load(), - self.power, - crate::world::ExplosionInteraction::Tnt, - ) + .explode(pos, power, crate::world::ExplosionInteraction::Tnt) .await; } - } else { - // Safe decrement - self.fuse.store(fuse - 1, Relaxed); - entity.update_fluid_state(caller).await; - } - }) + }); + } else { + // Safe decrement + self.fuse.store(fuse - 1, Relaxed); + entity.update_fluid_state(caller); + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async { - let pos: f64 = rand::random::() * TAU; + fn init_data_tracker(&self) { + let pos: f64 = rand::random::() * TAU; - self.entity - .set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02)); + self.entity + .set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02)); - self.entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::tnt::FUSE_ID, - VarInt(self.fuse.load(Relaxed) as i32), - ), - Metadata::new( - pumpkin_data::tracked_data::tnt::BLOCK_STATE_ID, - VarInt(i32::from(Block::TNT.default_state.id.as_u16())), - ), - ], - None, - ); - }) + self.entity.send_meta_data( + &[ + Metadata::new( + pumpkin_data::tracked_data::tnt::FUSE_ID, + VarInt(self.fuse.load(Relaxed) as i32), + ), + Metadata::new( + pumpkin_data::tracked_data::tnt::BLOCK_STATE_ID, + VarInt(i32::from(Block::TNT.default_state.id.as_u16())), + ), + ], + None, + ); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/type.rs b/crates/pumpkin/src/entity/type.rs index 25186ab95..1aa7fc983 100644 --- a/crates/pumpkin/src/entity/type.rs +++ b/crates/pumpkin/src/entity/type.rs @@ -261,7 +261,7 @@ pub fn from_type( } id if id == EntityType::EXPERIENCE_ORB.id => Arc::new(ExperienceOrbEntity::new(entity, 1)), id if id == EntityType::TNT.id => Arc::new(TNTEntity::new(entity, 4.0, 80)), - id if id == EntityType::ITEM.id => Arc::new(ItemEntity::new_for_restore(entity)), + id if id == EntityType::ITEM.id => Arc::new(ItemEntity::new_empty(entity)), id if id == EntityType::ARROW.id => Arc::new(ArrowEntity::new(entity, None)), id if id == EntityType::SPECTRAL_ARROW.id => Arc::new(ArrowEntity::new(entity, None)), id if id == EntityType::TRIDENT.id => Arc::new(TridentEntity::new(entity, None)), diff --git a/crates/pumpkin/src/entity/vehicle/boat.rs b/crates/pumpkin/src/entity/vehicle/boat.rs index d073f0145..20d07bc4c 100644 --- a/crates/pumpkin/src/entity/vehicle/boat.rs +++ b/crates/pumpkin/src/entity/vehicle/boat.rs @@ -60,27 +60,19 @@ impl EntityBase for BoatEntity { None } - fn tick<'a>( - &'a self, - _caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.vehicle.tick(); + fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { + self.vehicle.tick(); - let underwater = self.ticks_underwater.load(); - if self.vehicle.entity.touching_water.load(Ordering::Relaxed) { - self.ticks_underwater.store((underwater + 1.0).min(60.0)); - } else if underwater > 0.0 { - self.ticks_underwater.store((underwater - 1.0).max(0.0)); - } - }) + let underwater = self.ticks_underwater.load(); + if self.vehicle.entity.touching_water.load(Ordering::Relaxed) { + self.ticks_underwater.store((underwater + 1.0).min(60.0)); + } else if underwater > 0.0 { + self.ticks_underwater.store((underwater - 1.0).max(0.0)); + } } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.send_wobble_metadata(); - }) + fn init_data_tracker(&self) { + self.send_wobble_metadata(); } fn can_hit(&self) -> bool { @@ -91,16 +83,16 @@ impl EntityBase for BoatEntity { true } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, amount: f32, _damage_type: DamageType, _position: Option>, - source: Option<&'a dyn EntityBase>, - _cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { self.vehicle.damage_with_context(amount, source).await }) + source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + self.vehicle.damage_with_context(amount, source) } fn interact<'a>( @@ -121,7 +113,7 @@ impl EntityBase for BoatEntity { return false; } - if player.get_entity().has_vehicle().await { + if player.get_entity().has_vehicle() { return false; } @@ -143,10 +135,8 @@ impl EntityBase for BoatEntity { }) } - fn set_paddle_state(&self, left: bool, right: bool) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.set_paddles(left, right); - }) + fn set_paddle_state(&self, left: bool, right: bool) { + self.set_paddles(left, right); } fn cast_any(&self) -> &dyn std::any::Any { self diff --git a/crates/pumpkin/src/entity/vehicle/minecart.rs b/crates/pumpkin/src/entity/vehicle/minecart.rs index d91597bc8..6ba70b426 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart.rs @@ -138,366 +138,362 @@ impl EntityBase for MinecartEntity { } #[allow(clippy::too_many_lines)] - fn tick<'a>( - &'a self, - caller: &'a Arc, - _server: &'a Server, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.vehicle.tick(); - if let MinecartKind::Furnace(minecart) = &self.kind { - minecart.tick(&self.vehicle.entity); + fn tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { + self.vehicle.tick(); + if let MinecartKind::Furnace(minecart) = &self.kind { + minecart.tick(&self.vehicle.entity); + } + + let world = self.vehicle.entity.world.load(); + let pos = self.vehicle.entity.pos.load(); + let mut block_pos = BlockPos(Vector3::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + )); + + let (mut block, mut state_id) = world.get_block_and_state_id(&block_pos); + + let mut is_powered_rail = block.id == Block::POWERED_RAIL.id; + let mut is_activator_rail = block.id == Block::ACTIVATOR_RAIL.id; + let mut is_on_rails = is_powered_rail + || is_activator_rail + || block.id == Block::RAIL.id + || block.id == Block::DETECTOR_RAIL.id; + + // If not on rails at current Y level, check the block directly below + if !is_on_rails { + let below_block_pos = BlockPos(Vector3::new( + block_pos.0.x, + block_pos.0.y - 1, + block_pos.0.z, + )); + let (below_block, below_state_id) = world.get_block_and_state_id(&below_block_pos); + if below_block.id == Block::RAIL.id + || below_block.id == Block::POWERED_RAIL.id + || below_block.id == Block::DETECTOR_RAIL.id + || below_block.id == Block::ACTIVATOR_RAIL.id + { + block_pos = below_block_pos; + block = below_block; + state_id = below_state_id; + is_powered_rail = block.id == Block::POWERED_RAIL.id; + is_activator_rail = block.id == Block::ACTIVATOR_RAIL.id; + is_on_rails = true; + } + } + + if is_powered_rail || is_activator_rail { + let props = PoweredRailLikeProperties::from_state_id(state_id, block); + let powered = props.powered; + + if is_activator_rail && let MinecartKind::Hopper(minecart) = &self.kind { + minecart.set_enabled(!powered); } - let world = self.vehicle.entity.world.load(); + if powered { + if is_powered_rail { + let mut velocity = self.vehicle.entity.velocity.load(); + let speed = velocity.length(); + if speed > 0.01 { + let new_speed = (speed + 0.06).min(0.4); + velocity = velocity + .normalize() + .multiply(new_speed, new_speed, new_speed); + self.vehicle.entity.velocity.store(velocity); + } else { + let yaw = self.vehicle.entity.yaw.load(); + let push_dir = Vector3::new( + -f64::from((yaw.to_radians()).sin()), + 0.0, + f64::from((yaw.to_radians()).cos()), + ); + self.vehicle + .entity + .velocity + .store(push_dir.multiply(0.1, 0.1, 0.1)); + } + self.vehicle.entity.send_velocity(); + } else if is_activator_rail { + match &self.kind { + MinecartKind::Tnt(minecart) => { + minecart.prime(&self.vehicle.entity, 80); + } + MinecartKind::Rideable(_) => { + if let Ok(passengers) = self.vehicle.entity.passengers.try_lock() { + let p_ids: Vec = passengers + .iter() + .map(|p| p.get_entity().entity_id) + .collect(); + if !p_ids.is_empty() { + let world = self.vehicle.entity.world.load(); + let vid = self.vehicle.entity.entity_id; + tokio::spawn(async move { + if let Some(v) = world.get_entity_by_id(vid) { + for pid in p_ids { + v.get_entity().remove_passenger(pid).await; + } + } + }); + } + } + if self.vehicle.get_hurt_time() == 0 { + self.vehicle.set_hurt_dir(-self.vehicle.get_hurt_dir()); + self.vehicle.set_hurt_time(10); + self.vehicle.set_damage(50.0); + self.vehicle.send_wobble_metadata(); + } + } + _ => {} + } + } + } else if is_powered_rail { + let mut velocity = self.vehicle.entity.velocity.load(); + velocity = velocity.multiply(0.5, 0.5, 0.5); + if velocity.length() < 0.01 { + velocity = Vector3::new(0.0, 0.0, 0.0); + } + self.vehicle.entity.velocity.store(velocity); + self.vehicle.entity.send_velocity(); + } + } + + if let MinecartKind::Tnt(minecart) = &self.kind + && minecart.tick(&self.vehicle.entity) + { + return; + } + + let mut velocity = self.vehicle.entity.velocity.load(); + + let mut has_driver = false; + let mut driver_input = 0; + let mut driver_yaw = 0.0f32; + + if let Ok(passengers) = self.vehicle.entity.passengers.try_lock() + && let Some(passenger) = passengers.first() + && let Some(player) = passenger.get_player() + { + driver_input = player.last_input.load(Ordering::Relaxed); + driver_yaw = player.get_entity().yaw.load(); + has_driver = true; + } + + if has_driver && is_on_rails { + let forward = driver_input & SPlayerInput::FORWARD != 0; + let backward = driver_input & SPlayerInput::BACKWARD != 0; + + let mut force_dir = Vector3::new(0.0, 0.0, 0.0); + if forward { + let yaw_rad = f64::from(driver_yaw).to_radians(); + force_dir.x = -yaw_rad.sin(); + force_dir.z = yaw_rad.cos(); + } else if backward { + let yaw_rad = f64::from(driver_yaw).to_radians(); + force_dir.x = yaw_rad.sin(); + force_dir.z = -yaw_rad.cos(); + } + + if forward || backward { + velocity.x += force_dir.x * 0.02; + velocity.z += force_dir.z * 0.02; + + let speed = velocity.x.hypot(velocity.z); + if speed > 0.15 { + #[allow(clippy::suboptimal_flops)] + let old_speed = self + .vehicle + .entity + .velocity + .load() + .x + .hypot(self.vehicle.entity.velocity.load().z); + + let max_speed = old_speed.clamp(0.15, 0.4); + if speed > max_speed { + velocity.x = (velocity.x / speed) * max_speed; + velocity.z = (velocity.z / speed) * max_speed; + } + } + self.vehicle.entity.velocity.store(velocity); + self.vehicle.entity.send_velocity(); + } + } + + let mut velocity = self.vehicle.entity.velocity.load(); + + if is_on_rails { + use pumpkin_data::block_properties::RailLikeProperties; + use pumpkin_data::block_properties::{RailShape, RailShapeStraight}; + + let shape = if block.id == Block::RAIL.id { + let props = RailLikeProperties::from_state_id(state_id, block); + props.shape + } else { + let props = PoweredRailLikeProperties::from_state_id(state_id, block); + match props.shape { + RailShapeStraight::NorthSouth => RailShape::NorthSouth, + RailShapeStraight::EastWest => RailShape::EastWest, + RailShapeStraight::AscendingEast => RailShape::AscendingEast, + RailShapeStraight::AscendingWest => RailShape::AscendingWest, + RailShapeStraight::AscendingNorth => RailShape::AscendingNorth, + RailShapeStraight::AscendingSouth => RailShape::AscendingSouth, + } + }; + let pos = self.vehicle.entity.pos.load(); - let mut block_pos = BlockPos(Vector3::new( - pos.x.floor() as i32, - pos.y.floor() as i32, - pos.z.floor() as i32, - )); + let block_center_bottom = Vector3::new( + f64::from(block_pos.0.x) + 0.5, + f64::from(block_pos.0.y), + f64::from(block_pos.0.z) + 0.5, + ); - let mut block = world.get_block(&block_pos); - let mut state_id = world.get_block_state_id(&block_pos); + let (exit0, exit1) = get_exits(shape); + let exit0 = exit0.multiply(0.5, 0.5, 0.5); + let exit1 = exit1.multiply(0.5, 0.5, 0.5); - let mut is_powered_rail = block.id == Block::POWERED_RAIL.id; - let mut is_activator_rail = block.id == Block::ACTIVATOR_RAIL.id; - let mut is_on_rails = is_powered_rail - || is_activator_rail - || block.id == Block::RAIL.id - || block.id == Block::DETECTOR_RAIL.id; + let in_corner = exit0.x != exit1.x && exit0.z != exit1.z; + let mut target_position = pos; - // If not on rails at current Y level, check the block directly below - if !is_on_rails { + if in_corner { + let from0to1 = exit1 - exit0; + let from0topos = pos - block_center_bottom - exit0; + let dot_num = from0to1.dot(&from0topos); + let dot_den = from0to1.dot(&from0to1); + if dot_den != 0.0 { + let travel_vector_from0 = + from0to1.multiply(dot_num / dot_den, dot_num / dot_den, dot_num / dot_den); + target_position = block_center_bottom.add(&exit0).add(&travel_vector_from0); + } + } else { + let z_snap = (exit0.x - exit1.x).abs() > 1e-5; + let x_snap = (exit0.z - exit1.z).abs() > 1e-5; + if x_snap { + target_position.x = block_center_bottom.x; + } + if z_snap { + target_position.z = block_center_bottom.z; + } + } + + target_position.y = match shape { + RailShape::AscendingEast + | RailShape::AscendingWest + | RailShape::AscendingNorth + | RailShape::AscendingSouth => pos.y, + _ => f64::from(block_pos.0.y) + RAIL_HEIGHT_OFFSET, + }; + self.vehicle.entity.pos.store(target_position); + + let horizontal_in_direction = Vector3::new(exit1.x, 0.0, exit1.z); + let mut horizontal_out_direction = Vector3::new(exit0.x, 0.0, exit0.z); + + if velocity.dot(&horizontal_out_direction) < velocity.dot(&horizontal_in_direction) { + horizontal_out_direction = horizontal_in_direction; + } + + let out_position = block_center_bottom.add(&horizontal_out_direction).add( + &horizontal_out_direction + .normalize() + .multiply(1e-5, 1e-5, 1e-5), + ); + + let mut towards_out = out_position - target_position; + towards_out.y = 0.0; + let towards_length = towards_out.length(); + if towards_length > 1e-5 { + towards_out = towards_out.normalize(); + let speed = velocity.length(); + velocity = towards_out.multiply(speed, speed, speed); + } + + velocity.y = 0.0; + self.vehicle.entity.velocity.store(velocity); + } else if !self.vehicle.entity.on_ground.load(Ordering::Relaxed) { + velocity.y -= GRAVITY; + self.vehicle.entity.velocity.store(velocity); + } + + if velocity.length() > 0.001 { + self.move_entity(caller, velocity); + + if let MinecartKind::Tnt(minecart) = &self.kind + && self + .vehicle + .entity + .horizontal_collision + .load(Ordering::Relaxed) + && velocity.x.mul_add(velocity.x, velocity.z * velocity.z) >= 0.01 + { + minecart.explode( + &self.vehicle.entity, + velocity.x.mul_add(velocity.x, velocity.z * velocity.z), + ); + return; + } + + let new_pos = self.vehicle.entity.pos.load(); + + if let Ok(passengers) = self.vehicle.entity.passengers.try_lock() { + for passenger in passengers.iter() { + passenger.get_entity().set_pos(new_pos); + } + } + + self.vehicle.entity.send_pos_rot(); + + #[allow(clippy::useless_let_if_seq)] + let mut friction = 0.95; // Vanilla minecart air drag + + if is_on_rails { + let has_passengers = self + .vehicle + .entity + .passengers + .try_lock() + .is_ok_and(|p| !p.is_empty()); + friction = if has_passengers { 0.99 } else { 0.96 }; + } else { let below_block_pos = BlockPos(Vector3::new( block_pos.0.x, block_pos.0.y - 1, block_pos.0.z, )); let below_block = world.get_block(&below_block_pos); - if below_block.id == Block::RAIL.id - || below_block.id == Block::POWERED_RAIL.id - || below_block.id == Block::DETECTOR_RAIL.id - || below_block.id == Block::ACTIVATOR_RAIL.id - { - block_pos = below_block_pos; - block = below_block; - state_id = world.get_block_state_id(&block_pos); - is_powered_rail = block.id == Block::POWERED_RAIL.id; - is_activator_rail = block.id == Block::ACTIVATOR_RAIL.id; - is_on_rails = true; + + let is_on_ground = self.vehicle.entity.on_ground.load(Ordering::Relaxed) + || (below_block.id != Block::AIR.id + && below_block.id != Block::WATER.id + && below_block.id != Block::LAVA.id); + let is_in_water = self.vehicle.entity.touching_water.load(Ordering::Relaxed) + || below_block.id == Block::WATER.id; + + if is_on_ground { + friction = 0.5; + } else if is_in_water { + friction = 0.95; } } - if is_powered_rail || is_activator_rail { - let props = PoweredRailLikeProperties::from_state_id(state_id, block); - let powered = props.powered; - - if is_activator_rail && let MinecartKind::Hopper(minecart) = &self.kind { - minecart.set_enabled(!powered); - } - - if powered { - if is_powered_rail { - let mut velocity = self.vehicle.entity.velocity.load(); - let speed = velocity.length(); - if speed > 0.01 { - let new_speed = (speed + 0.06).min(0.4); - velocity = velocity - .normalize() - .multiply(new_speed, new_speed, new_speed); - self.vehicle.entity.velocity.store(velocity); - } else { - let yaw = self.vehicle.entity.yaw.load(); - let push_dir = Vector3::new( - -f64::from((yaw.to_radians()).sin()), - 0.0, - f64::from((yaw.to_radians()).cos()), - ); - self.vehicle - .entity - .velocity - .store(push_dir.multiply(0.1, 0.1, 0.1)); - } - self.vehicle.entity.send_velocity(); - } else if is_activator_rail { - match &self.kind { - MinecartKind::Tnt(minecart) => { - minecart.prime(&self.vehicle.entity, 80); - } - MinecartKind::Rideable(_) => { - let passengers = - self.vehicle.entity.passengers.lock().await.clone(); - for passenger in passengers { - self.vehicle - .entity - .remove_passenger(passenger.get_entity().entity_id) - .await; - } - if self.vehicle.get_hurt_time() == 0 { - self.vehicle.set_hurt_dir(-self.vehicle.get_hurt_dir()); - self.vehicle.set_hurt_time(10); - self.vehicle.set_damage(50.0); - self.vehicle.send_wobble_metadata(); - } - } - _ => {} - } - } - } else if is_powered_rail { - let mut velocity = self.vehicle.entity.velocity.load(); - velocity = velocity.multiply(0.5, 0.5, 0.5); - if velocity.length() < 0.01 { - velocity = Vector3::new(0.0, 0.0, 0.0); - } - self.vehicle.entity.velocity.store(velocity); - self.vehicle.entity.send_velocity(); - } + let mut next_vel = if is_on_rails && let MinecartKind::Furnace(minecart) = &self.kind { + minecart.velocity(&self.vehicle.entity, velocity) + } else if is_on_rails && let Some(inventory) = self.container() { + container::velocity(&self.vehicle.entity, inventory, velocity) + } else { + velocity.multiply(friction, friction, friction) + }; + if next_vel.length() < 0.005 { + next_vel = Vector3::new(0.0, 0.0, 0.0); } - - if let MinecartKind::Tnt(minecart) = &self.kind - && minecart.tick(&self.vehicle.entity).await - { - return; + self.vehicle.entity.velocity.store(next_vel); + if next_vel.length_squared() == 0.0 { + self.vehicle.entity.send_velocity(); } + } - let mut velocity = self.vehicle.entity.velocity.load(); - - let mut has_driver = false; - let mut driver_input = 0; - let mut driver_yaw = 0.0f32; - - { - let passengers = self.vehicle.entity.passengers.lock().await; - if let Some(passenger) = passengers.first() - && let Some(player) = passenger.get_player() - { - driver_input = player.last_input.load(Ordering::Relaxed); - driver_yaw = player.get_entity().yaw.load(); - has_driver = true; - } - } - - if has_driver && is_on_rails { - let forward = driver_input & SPlayerInput::FORWARD != 0; - let backward = driver_input & SPlayerInput::BACKWARD != 0; - - let mut force_dir = Vector3::new(0.0, 0.0, 0.0); - if forward { - let yaw_rad = f64::from(driver_yaw).to_radians(); - force_dir.x = -yaw_rad.sin(); - force_dir.z = yaw_rad.cos(); - } else if backward { - let yaw_rad = f64::from(driver_yaw).to_radians(); - force_dir.x = yaw_rad.sin(); - force_dir.z = -yaw_rad.cos(); - } - - if forward || backward { - velocity.x += force_dir.x * 0.02; - velocity.z += force_dir.z * 0.02; - - let speed = velocity.x.hypot(velocity.z); - if speed > 0.15 { - #[allow(clippy::suboptimal_flops)] - let old_speed = self - .vehicle - .entity - .velocity - .load() - .x - .hypot(self.vehicle.entity.velocity.load().z); - - let max_speed = old_speed.clamp(0.15, 0.4); - if speed > max_speed { - velocity.x = (velocity.x / speed) * max_speed; - velocity.z = (velocity.z / speed) * max_speed; - } - } - self.vehicle.entity.velocity.store(velocity); - self.vehicle.entity.send_velocity(); - } - } - - let mut velocity = self.vehicle.entity.velocity.load(); - - if is_on_rails { - use pumpkin_data::block_properties::RailLikeProperties; - use pumpkin_data::block_properties::{RailShape, RailShapeStraight}; - - let shape = if block.id == Block::RAIL.id { - let props = RailLikeProperties::from_state_id(state_id, block); - props.shape - } else { - let props = PoweredRailLikeProperties::from_state_id(state_id, block); - match props.shape { - RailShapeStraight::NorthSouth => RailShape::NorthSouth, - RailShapeStraight::EastWest => RailShape::EastWest, - RailShapeStraight::AscendingEast => RailShape::AscendingEast, - RailShapeStraight::AscendingWest => RailShape::AscendingWest, - RailShapeStraight::AscendingNorth => RailShape::AscendingNorth, - RailShapeStraight::AscendingSouth => RailShape::AscendingSouth, - } - }; - - let pos = self.vehicle.entity.pos.load(); - let block_center_bottom = Vector3::new( - f64::from(block_pos.0.x) + 0.5, - f64::from(block_pos.0.y), - f64::from(block_pos.0.z) + 0.5, - ); - - let (exit0, exit1) = get_exits(shape); - let exit0 = exit0.multiply(0.5, 0.5, 0.5); - let exit1 = exit1.multiply(0.5, 0.5, 0.5); - - let in_corner = exit0.x != exit1.x && exit0.z != exit1.z; - let mut target_position = pos; - - if in_corner { - let from0to1 = exit1 - exit0; - let from0topos = pos - block_center_bottom - exit0; - let dot_num = from0to1.dot(&from0topos); - let dot_den = from0to1.dot(&from0to1); - if dot_den != 0.0 { - let travel_vector_from0 = from0to1.multiply( - dot_num / dot_den, - dot_num / dot_den, - dot_num / dot_den, - ); - target_position = block_center_bottom.add(&exit0).add(&travel_vector_from0); - } - } else { - let z_snap = (exit0.x - exit1.x).abs() > 1e-5; - let x_snap = (exit0.z - exit1.z).abs() > 1e-5; - if x_snap { - target_position.x = block_center_bottom.x; - } - if z_snap { - target_position.z = block_center_bottom.z; - } - } - - target_position.y = match shape { - RailShape::AscendingEast - | RailShape::AscendingWest - | RailShape::AscendingNorth - | RailShape::AscendingSouth => pos.y, - _ => f64::from(block_pos.0.y) + RAIL_HEIGHT_OFFSET, - }; - self.vehicle.entity.pos.store(target_position); - - let horizontal_in_direction = Vector3::new(exit1.x, 0.0, exit1.z); - let mut horizontal_out_direction = Vector3::new(exit0.x, 0.0, exit0.z); - - if velocity.dot(&horizontal_out_direction) < velocity.dot(&horizontal_in_direction) - { - horizontal_out_direction = horizontal_in_direction; - } - - let out_position = block_center_bottom.add(&horizontal_out_direction).add( - &horizontal_out_direction - .normalize() - .multiply(1e-5, 1e-5, 1e-5), - ); - - let mut towards_out = out_position - target_position; - towards_out.y = 0.0; - let towards_length = towards_out.length(); - if towards_length > 1e-5 { - towards_out = towards_out.normalize(); - let speed = velocity.length(); - velocity = towards_out.multiply(speed, speed, speed); - } - - velocity.y = 0.0; - self.vehicle.entity.velocity.store(velocity); - } else if !self.vehicle.entity.on_ground.load(Ordering::Relaxed) { - velocity.y -= GRAVITY; - self.vehicle.entity.velocity.store(velocity); - } - - if velocity.length() > 0.001 { - self.move_entity(caller, velocity).await; - - if let MinecartKind::Tnt(minecart) = &self.kind - && self - .vehicle - .entity - .horizontal_collision - .load(Ordering::Relaxed) - && velocity.x.mul_add(velocity.x, velocity.z * velocity.z) >= 0.01 - { - minecart - .explode( - &self.vehicle.entity, - velocity.x.mul_add(velocity.x, velocity.z * velocity.z), - ) - .await; - return; - } - - let new_pos = self.vehicle.entity.pos.load(); - - let passengers = self.vehicle.entity.passengers.lock().await; - for passenger in passengers.iter() { - passenger.get_entity().set_pos(new_pos); - } - drop(passengers); - - self.vehicle.entity.send_pos_rot(); - - #[allow(clippy::useless_let_if_seq)] - let mut friction = 0.95; // Vanilla minecart air drag - - if is_on_rails { - let passengers = self.vehicle.entity.passengers.lock().await; - let has_passengers = !passengers.is_empty(); - drop(passengers); - friction = if has_passengers { 0.99 } else { 0.96 }; - } else { - let below_block_pos = BlockPos(Vector3::new( - block_pos.0.x, - block_pos.0.y - 1, - block_pos.0.z, - )); - let below_block = world.get_block(&below_block_pos); - - let is_on_ground = self.vehicle.entity.on_ground.load(Ordering::Relaxed) - || (below_block.id != Block::AIR.id - && below_block.id != Block::WATER.id - && below_block.id != Block::LAVA.id); - let is_in_water = self.vehicle.entity.touching_water.load(Ordering::Relaxed) - || below_block.id == Block::WATER.id; - - if is_on_ground { - friction = 0.5; - } else if is_in_water { - friction = 0.95; - } - } - - let mut next_vel = - if is_on_rails && let MinecartKind::Furnace(minecart) = &self.kind { - minecart.velocity(&self.vehicle.entity, velocity) - } else if is_on_rails && let Some(inventory) = self.container() { - container::velocity(&self.vehicle.entity, inventory, velocity).await - } else { - velocity.multiply(friction, friction, friction) - }; - if next_vel.length() < 0.005 { - next_vel = Vector3::new(0.0, 0.0, 0.0); - } - self.vehicle.entity.velocity.store(next_vel); - if next_vel.length_squared() == 0.0 { - self.vehicle.entity.send_velocity(); - } - } - - if let MinecartKind::Hopper(minecart) = &self.kind { - minecart.tick(&self.vehicle.entity).await; - } - }) + if let MinecartKind::Hopper(minecart) = &self.kind { + minecart.tick(&self.vehicle.entity); + } } fn get_entity(&self) -> &Entity { @@ -518,8 +514,8 @@ impl EntityBase for MinecartEntity { let self_entity = self.get_entity(); let other_entity = entity.get_entity(); - if self_entity.no_clip.load(Ordering::Relaxed) - || other_entity.no_clip.load(Ordering::Relaxed) + if self_entity.no_physics.load(Ordering::Relaxed) + || other_entity.no_physics.load(Ordering::Relaxed) { return; } @@ -633,7 +629,7 @@ impl EntityBase for MinecartEntity { } } } else { - if !self_entity.has_passengers().await && self.is_pushable() { + if !self_entity.has_passengers() && self.is_pushable() { let mut vel = self_entity.velocity.load(); vel.x -= xa; vel.z -= za; @@ -641,7 +637,7 @@ impl EntityBase for MinecartEntity { self_entity.send_velocity(); } - if !other_entity.has_passengers().await && entity.is_pushable() { + if !other_entity.has_passengers() && entity.is_pushable() { let mut vel = other_entity.velocity.load(); vel.x += xa / 4.0; vel.z += za / 4.0; @@ -657,102 +653,100 @@ impl EntityBase for MinecartEntity { true } - fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - self.vehicle.send_wobble_metadata(); - if let MinecartKind::Furnace(minecart) = &self.kind { - minecart.init_data_tracker(&self.vehicle.entity); - } - }) + fn init_data_tracker(&self) { + self.vehicle.send_wobble_metadata(); + if let MinecartKind::Furnace(minecart) = &self.kind { + minecart.init_data_tracker(&self.vehicle.entity); + } } fn can_hit(&self) -> bool { self.vehicle.entity.is_alive() } - fn damage_with_context<'a>( - &'a self, - _caller: &'a dyn EntityBase, + fn damage_with_context( + &self, + _caller: &dyn EntityBase, amount: f32, damage_type: DamageType, _position: Option>, - source: Option<&'a dyn EntityBase>, - cause: Option<&'a dyn EntityBase>, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let creative = source - .and_then(EntityBase::get_player) - .is_some_and(|player| player.gamemode.load() == GameMode::Creative); + source: Option<&dyn EntityBase>, + cause: Option<&dyn EntityBase>, + ) -> bool { + let creative = source + .and_then(EntityBase::get_player) + .is_some_and(|player| player.gamemode.load() == GameMode::Creative); - if let MinecartKind::Tnt(minecart) = &self.kind - && damage_type == DamageType::ARROW - && self.vehicle.entity.fire_ticks.load(Ordering::Relaxed) > 0 - { - let projectile_speed_squared = cause - .map(|entity| entity.get_entity().velocity.load().length_squared()) - .unwrap_or_default(); - minecart - .explode(&self.vehicle.entity, projectile_speed_squared) - .await; - if self.vehicle.entity.is_removed() { - return true; + if let MinecartKind::Tnt(minecart) = &self.kind + && damage_type == DamageType::ARROW + && self.vehicle.entity.fire_ticks.load(Ordering::Relaxed) > 0 + { + let projectile_speed_squared = cause + .map(|entity| entity.get_entity().velocity.load().length_squared()) + .unwrap_or_default(); + minecart.explode(&self.vehicle.entity, projectile_speed_squared); + if self.vehicle.entity.is_removed() { + return true; + } + } + + let will_break = self.vehicle.entity.is_alive() + && (creative || self.vehicle.get_damage() + amount * 10.0 > 40.0); + + if let MinecartKind::Tnt(minecart) = &self.kind + && will_break + && !creative + { + let velocity = self.vehicle.entity.velocity.load(); + let speed_squared = velocity.x.mul_add(velocity.x, velocity.z * velocity.z); + let ignites = damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_FIRE) + || damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) + || self.vehicle.entity.fire_ticks.load(Ordering::Relaxed) > 0; + if ignites || speed_squared >= 0.01 { + self.vehicle.apply_damage_wobble(amount); + let fuse = rand::rng().random_range(0..20) + rand::rng().random_range(0..20); + if self + .vehicle + .entity + .world + .load() + .level_info + .load() + .game_rules + .tnt_explodes + { + minecart.prime(&self.vehicle.entity, fuse); + } else { + minecart.set_fuse(fuse); + } + return true; + } + } + + let damaged = self.vehicle.damage_with_context(amount, source); + + if will_break && !creative && self.vehicle.entity.is_removed() { + let world = self.vehicle.entity.world.load(); + if world.level_info.load().game_rules.entity_drops { + let position = self.vehicle.entity.block_pos.load(); + if let Some(container) = self.container() + && container.claim_drops() + { + let container_clone = container.clone(); + let world_clone = self.vehicle.entity.world.load_full(); + tokio::spawn(async move { + container_clone.unpack_loot().await; + let inventory: Arc = container_clone; + world_clone.scatter_inventory(&position, &inventory).await; + }); + } + if let Some(item) = self.drop_item() { + world.drop_stack(&position, ItemStack::new(1, item)); } } + } - let will_break = self.vehicle.entity.is_alive() - && (creative || self.vehicle.get_damage() + amount * 10.0 > 40.0); - - if let MinecartKind::Tnt(minecart) = &self.kind - && will_break - && !creative - { - let velocity = self.vehicle.entity.velocity.load(); - let speed_squared = velocity.x.mul_add(velocity.x, velocity.z * velocity.z); - let ignites = damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_FIRE) - || damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) - || self.vehicle.entity.fire_ticks.load(Ordering::Relaxed) > 0; - if ignites || speed_squared >= 0.01 { - self.vehicle.apply_damage_wobble(amount); - let fuse = rand::rng().random_range(0..20) + rand::rng().random_range(0..20); - if self - .vehicle - .entity - .world - .load() - .level_info - .load() - .game_rules - .tnt_explodes - { - minecart.prime(&self.vehicle.entity, fuse); - } else { - minecart.set_fuse(fuse); - } - return true; - } - } - - let damaged = self.vehicle.damage_with_context(amount, source).await; - - if will_break && !creative && self.vehicle.entity.is_removed() { - let world = self.vehicle.entity.world.load(); - if world.level_info.load().game_rules.entity_drops { - let position = self.vehicle.entity.block_pos.load(); - if let Some(container) = self.container() - && container.claim_drops() - { - container.unpack_loot().await; - let inventory: Arc = container.clone(); - world.scatter_inventory(&position, &inventory).await; - } - if let Some(item) = self.drop_item() { - world.drop_stack(&position, ItemStack::new(1, item)).await; - } - } - } - - damaged - }) + damaged } fn interact<'a>( @@ -779,68 +773,61 @@ impl EntityBase for MinecartEntity { }) } - fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self - .vehicle - .entity - .passengers - .lock() - .await - .iter() - .any(|passenger| passenger.get_entity().entity_id == player.entity_id()) - { - return; + fn on_player_collision(&self, player: &Arc) { + if self.vehicle.entity.has_passenger(player.entity_id()) { + return; + } + + if player.is_spectator() { + return; + } + + let player_pos = player.get_entity().pos.load(); + let minecart_pos = self.vehicle.entity.pos.load(); + + let mut diff_x = minecart_pos.x - player_pos.x; + let mut diff_z = minecart_pos.z - player_pos.z; + + let dist_sq = diff_x * diff_x + diff_z * diff_z; + if dist_sq > 0.0001 { + let dist = dist_sq.sqrt(); + diff_x /= dist; + diff_z /= dist; + + let push_force = 0.1; + let mut vel = self.vehicle.entity.velocity.load(); + vel.x += diff_x * push_force; + vel.z += diff_z * push_force; + + let horizontal_speed = vel.x.hypot(vel.z); + if horizontal_speed > 0.4 { + vel.x = (vel.x / horizontal_speed) * 0.4; + vel.z = (vel.z / horizontal_speed) * 0.4; } - if player.is_spectator() { - return; - } - - let player_pos = player.get_entity().pos.load(); - let minecart_pos = self.vehicle.entity.pos.load(); - - let mut diff_x = minecart_pos.x - player_pos.x; - let mut diff_z = minecart_pos.z - player_pos.z; - - let dist_sq = diff_x * diff_x + diff_z * diff_z; - if dist_sq > 0.0001 { - let dist = dist_sq.sqrt(); - diff_x /= dist; - diff_z /= dist; - - let push_force = 0.1; - let mut vel = self.vehicle.entity.velocity.load(); - vel.x += diff_x * push_force; - vel.z += diff_z * push_force; - - let horizontal_speed = vel.x.hypot(vel.z); - if horizontal_speed > 0.4 { - vel.x = (vel.x / horizontal_speed) * 0.4; - vel.z = (vel.z / horizontal_speed) * 0.4; - } - - self.vehicle.entity.velocity.store(vel); - self.vehicle.entity.send_velocity(); - } - }) + self.vehicle.entity.velocity.store(vel); + self.vehicle.entity.send_velocity(); + } } - fn move_entity<'a>( - &'a self, - caller: &'a Arc, - motion: Vector3, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let to_position = self.vehicle.entity.pos.load().add(&motion); - self.vehicle.entity.move_entity(caller, motion).await; - let should_continue = self.push_entities(caller).await; - if should_continue { - let current_pos = self.vehicle.entity.pos.load(); - let back_motion = to_position.sub(¤t_pos); - self.vehicle.entity.move_entity(caller, back_motion).await; + fn move_entity(&self, caller: &Arc, motion: Vector3) { + let to_position = self.vehicle.entity.pos.load().add(&motion); + self.vehicle.entity.move_entity(caller, motion); + let caller_clone = caller.clone(); + let entity_id = self.vehicle.entity.entity_id; + let world = self.vehicle.entity.world.load().clone(); + tokio::spawn(async move { + if let Some(dyn_self) = world.get_entity_by_id(entity_id) { + let should_continue = dyn_self.push_entities(&caller_clone).await; + if should_continue { + let current_pos = dyn_self.get_entity().pos.load(); + let back_motion = to_position.sub(¤t_pos); + dyn_self + .get_entity() + .move_entity(&caller_clone, back_motion); + } } - }) + }); } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/vehicle/minecart/container.rs b/crates/pumpkin/src/entity/vehicle/minecart/container.rs index 69192383f..4662fc9c9 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/container.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/container.rs @@ -15,7 +15,7 @@ use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; use tokio::sync::{Mutex, RwLock}; -use crate::entity::{Entity, EntityBase, player::Player}; +use crate::entity::{Entity, player::Player}; use crate::world::loot::fill_chest_inventory; use pumpkin_data::chest_loot_table::get_chest_loot_table; @@ -200,13 +200,37 @@ pub(super) async fn open( .is_some() } -pub(super) async fn velocity( +pub(super) fn velocity( entity: &Entity, inventory: &MinecartInventory, velocity: Vector3, ) -> Vector3 { - let signal = crate::block::calculate_comparator_output(inventory).await; - let mut friction = if inventory.has_loot_table().await { + let has_loot = inventory + .loot_table + .try_lock() + .is_ok_and(|guard| guard.is_some()); + let signal = if has_loot { + 0 + } else if let Ok(items) = inventory.items.try_read() { + let mut total_fill = 0.0; + let mut has_items = false; + for stack in items.iter() { + if !stack.is_empty() { + let max_count = stack.get_max_stack_size(); + total_fill += f64::from(stack.item_count) / f64::from(max_count); + has_items = true; + } + } + if has_items { + let factor = total_fill / inventory.size as f64; + (factor * 14.0).floor() as u8 + 1 + } else { + 0 + } + } else { + 0 + }; + let mut friction = if has_loot { 0.98 } else { 0.98 + f64::from(15 - signal) * 0.001 diff --git a/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs b/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs index 564524303..ac8901068 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs @@ -33,7 +33,7 @@ impl HopperMinecart { self.enabled.store(enabled, Ordering::Relaxed); } - pub(super) async fn tick(&self, entity: &Entity) { + pub(super) fn tick(&self, entity: &Entity) { if !self.enabled.load(Ordering::Relaxed) { return; } @@ -41,63 +41,79 @@ impl HopperMinecart { let world = entity.world.load(); let pos = entity.pos.load(); let source_pos = BlockPos::floored(pos.x, pos.y + 1.5, pos.z); - if let Some(block_entity) = world.get_block_entity(&source_pos) - && let Some(source) = block_entity.get_inventory() - { - for slot in 0..source.size() { - let stack = source.get_stack(slot).await; - if stack.is_empty() - || !source.can_transfer_to(self.inventory.as_ref(), slot, &stack) - { - continue; - } - let backup = stack.clone(); - let one = source.remove_stack_specific(slot, 1).await; - if HopperBlockEntity::add_one_item(source.as_ref(), self.inventory.as_ref(), one) - .await - { - return; - } - source.set_stack(slot, backup).await; - } - return; - } - - let suction_box = BoundingBox::new( - Vector3::new(pos.x - 0.5, pos.y + 0.6875, pos.z - 0.5), - Vector3::new(pos.x + 0.5, pos.y + 2.0, pos.z + 0.5), - ); - if self.pick_up_item(entity, &suction_box).await { - return; - } + let inventory = self.inventory.clone(); let cart_box = entity.bounding_box.load().expand(0.25, 0.0, 0.25); - self.pick_up_item(entity, &cart_box).await; + let world_clone = world.clone(); + + tokio::spawn(async move { + if let Some(block_entity) = world_clone.get_block_entity(&source_pos) + && let Some(source) = block_entity.get_inventory() + { + for slot in 0..source.size() { + let stack = source.get_stack(slot).await; + if stack.is_empty() || !source.can_transfer_to(inventory.as_ref(), slot, &stack) + { + continue; + } + let backup = stack.clone(); + let one = source.remove_stack_specific(slot, 1).await; + if HopperBlockEntity::add_one_item(source.as_ref(), inventory.as_ref(), one) + .await + { + return; + } + source.set_stack(slot, backup).await; + } + return; + } + + let suction_box = BoundingBox::new( + Vector3::new(pos.x - 0.5, pos.y + 0.6875, pos.z - 0.5), + Vector3::new(pos.x + 0.5, pos.y + 2.0, pos.z + 0.5), + ); + if Self::pick_up_item_internal(&world_clone, &inventory, &suction_box).await { + return; + } + Self::pick_up_item_internal(&world_clone, &inventory, &cart_box).await; + }); } - async fn pick_up_item(&self, entity: &Entity, search_box: &BoundingBox) -> bool { - let world = entity.world.load(); + async fn pick_up_item_internal( + world: &Arc, + inventory: &Arc, + search_box: &BoundingBox, + ) -> bool { for entity in world.get_entities_at_box(search_box) { let Some(item) = entity.get_item_entity() else { continue; }; - let mut stack = item.get_item_stack().lock().await; - if stack.is_empty() { - continue; - } - let backup = stack.clone(); - let one = stack.split(1); - if HopperBlockEntity::add_one_item( - self.inventory.as_ref(), - self.inventory.as_ref(), - one, - ) - .await - { + let (backup, one) = { + let mut stack = item + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if stack.is_empty() { - item.get_entity().remove().await; + continue; + } + (stack.clone(), stack.split(1)) + }; + if HopperBlockEntity::add_one_item(inventory.as_ref(), inventory.as_ref(), one).await { + let is_empty = { + let stack = item + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stack.is_empty() + }; + if is_empty { + item.get_entity().remove(); } return true; } + let mut stack = item + .get_item_stack() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); *stack = backup; } false diff --git a/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs b/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs index d1a78a544..222b0530e 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs @@ -8,7 +8,7 @@ impl RideableMinecart { pub(super) async fn interact(&self, entity: &Entity, player: &Arc) -> bool { if player.get_entity().is_sneaking() || !entity.passengers.lock().await.is_empty() - || player.get_entity().has_vehicle().await + || player.get_entity().has_vehicle() { return false; } diff --git a/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs b/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs index c65c44476..014539522 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs @@ -53,7 +53,7 @@ impl TntMinecart { ); } - pub(super) async fn tick(&self, entity: &Entity) -> bool { + pub(super) fn tick(&self, entity: &Entity) -> bool { let fuse = self.fuse.load(Ordering::Relaxed); if fuse > 0 { self.fuse.store(fuse - 1, Ordering::Relaxed); @@ -71,18 +71,17 @@ impl TntMinecart { self.explode( entity, velocity.x.mul_add(velocity.x, velocity.z * velocity.z), - ) - .await; + ); return true; } false } - pub(super) async fn explode(&self, entity: &Entity, horizontal_speed_squared: f64) { + pub(super) fn explode(&self, entity: &Entity, horizontal_speed_squared: f64) { let world = entity.world.load(); if !world.level_info.load().game_rules.tnt_explodes { if self.fuse.load(Ordering::Relaxed) > -1 { - entity.remove().await; + entity.remove(); } return; } @@ -95,14 +94,17 @@ impl TntMinecart { ); let pos = entity.pos.load(); let primed = self.fuse.load(Ordering::Relaxed) > -1; - entity.remove().await; - if primed { - world.explode_tnt_minecart(pos, power).await; - } else { - world - .explode(pos, power, crate::world::ExplosionInteraction::Tnt) - .await; - } + entity.remove(); + let world_clone = world.clone(); + tokio::spawn(async move { + if primed { + world_clone.explode_tnt_minecart(pos, power).await; + } else { + world_clone + .explode(pos, power, crate::world::ExplosionInteraction::Tnt) + .await; + } + }); } pub(super) fn set_fuse(&self, fuse: i32) { diff --git a/crates/pumpkin/src/entity/vehicle/vehicle.rs b/crates/pumpkin/src/entity/vehicle/vehicle.rs index 0c716656a..d23c2258d 100644 --- a/crates/pumpkin/src/entity/vehicle/vehicle.rs +++ b/crates/pumpkin/src/entity/vehicle/vehicle.rs @@ -42,10 +42,8 @@ impl VehicleEntity { self.entity.entity_id, ); if let Some(server) = self.entity.world.load().server.upgrade() { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - server.plugin_manager.fire(&server, &mut update_event).await; - }); + tokio::spawn(async move { + server.plugin_manager.fire(&server, &mut update_event).await; }); } } @@ -159,14 +157,14 @@ impl VehicleEntity { ); } - pub async fn kill_and_drop_self(&self) { + pub fn kill_and_drop_self(&self) { let world = self.entity.world.load(); let entity_drops = world.level_info.load().game_rules.entity_drops; if entity_drops && let Some(loot_table) = &self.entity.entity_type.loot_table { let pos = self.entity.block_pos.load(); - let is_raining = world.is_raining().await; - let is_thundering = world.is_thundering().await; + let is_raining = world.is_raining(); + let is_thundering = world.is_thundering(); let params = LootContextParameters { is_raining: Some(is_raining), is_thundering: Some(is_thundering), @@ -174,14 +172,14 @@ impl VehicleEntity { ..Default::default() }; for stack in loot_table.get_loot(params) { - world.drop_stack(&pos, stack).await; + world.drop_stack(&pos, stack); } } - self.entity.remove().await; + self.entity.remove(); } - pub async fn damage_with_context(&self, amount: f32, source: Option<&dyn EntityBase>) -> bool { + pub fn damage_with_context(&self, amount: f32, source: Option<&dyn EntityBase>) -> bool { if !self.entity.is_alive() { return true; } @@ -194,7 +192,9 @@ impl VehicleEntity { attacker_id, ); if let Some(server) = self.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut damage_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut damage_event); } if damage_event.cancelled { return false; @@ -215,17 +215,16 @@ impl VehicleEntity { if let Some(server) = self.entity.world.load().server.upgrade() { server .plugin_manager - .fire(&server, &mut destroy_event) - .await; + .fire_blocking(&server, &mut destroy_event); } if destroy_event.cancelled { return false; } if is_creative { - self.entity.remove().await; + self.entity.remove(); } else { - self.kill_and_drop_self().await; + self.kill_and_drop_self(); } } diff --git a/crates/pumpkin/src/item/items/armor_stand.rs b/crates/pumpkin/src/item/items/armor_stand.rs index 8a73b30d3..54b8046a3 100644 --- a/crates/pumpkin/src/item/items/armor_stand.rs +++ b/crates/pumpkin/src/item/items/armor_stand.rs @@ -89,7 +89,7 @@ impl ItemBehaviour for ArmorStandItem { let armor_stand = ArmorStandEntity::new(entity); - world.spawn_entity(Arc::new(armor_stand)).await; + world.spawn_entity(Arc::new(armor_stand)); item.decrement_unless_creative(player.gamemode.load(), 1); } }) diff --git a/crates/pumpkin/src/item/items/axe.rs b/crates/pumpkin/src/item/items/axe.rs index 1d92ec263..eb3a45677 100644 --- a/crates/pumpkin/src/item/items/axe.rs +++ b/crates/pumpkin/src/item/items/axe.rs @@ -42,7 +42,7 @@ impl ItemBehaviour for AxeItem { // First we try to strip the block. by getting his equivalent and applying it the axis. // If there is a strip equivalent. - let changed = if let Some(replacement) = replacement_block { + let changed = replacement_block.is_some_and(|replacement| { let new_block = replacement.to_block(); // Bamboo blocks are pillars too, but they are not part of the logs tag. let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_LOGS) @@ -81,13 +81,11 @@ impl ItemBehaviour for AxeItem { other_state_id, new_block, ); - world - .set_block_state( - &other_half_pos, - other_new_state_id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &other_half_pos, + other_new_state_id, + BlockFlags::NOTIFY_ALL, + ); } crate::block::blocks::weathering_copper::with_properties_of( block, @@ -101,13 +99,9 @@ impl ItemBehaviour for AxeItem { new_block, ) }; - world - .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL); true - } else { - false - }; + }); if changed && player.gamemode.load() != GameMode::Creative { // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. diff --git a/crates/pumpkin/src/item/items/boat.rs b/crates/pumpkin/src/item/items/boat.rs index fab84c201..19e0437d8 100644 --- a/crates/pumpkin/src/item/items/boat.rs +++ b/crates/pumpkin/src/item/items/boat.rs @@ -99,7 +99,7 @@ impl ItemBehaviour for BoatItem { let (start_pos, end_pos) = self.get_start_and_end_pos(player); // Vanilla: raycast with FluidHandling.ANY - stops on water/lava surface or solid blocks - let checker = async |pos: &BlockPos, world_inner: &Arc| { + let checker = |pos: &BlockPos, world_inner: &Arc| { let state_id = world_inner.get_block_state_id(pos); // Air doesn't stop the raycast @@ -116,8 +116,7 @@ impl ItemBehaviour for BoatItem { true }; - let Some((hit_pos, _direction)) = world.raycast(start_pos, end_pos, checker).await - else { + let Some((hit_pos, _direction)) = world.raycast(start_pos, end_pos, checker) else { return; }; @@ -179,12 +178,12 @@ impl ItemBehaviour for BoatItem { entity.set_rotation(player_yaw, 0.0); let boat_entity = Arc::new(BoatEntity::new(entity)); - world.spawn_entity(boat_entity).await; + world.spawn_entity(boat_entity); // Decrement item unless in creative mode - let mut stack = player.inventory.held_item().await; + let mut stack = player.inventory.held_item(); stack.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(stack).await; + player.inventory.set_held_item(stack); // TODO: world.emitGameEvent(user, GameEvent.ENTITY_PLACE, hitResult.getPos()) // TODO: user.incrementStat(Stats.USED.getOrCreateStat(this)) diff --git a/crates/pumpkin/src/item/items/bone_meal.rs b/crates/pumpkin/src/item/items/bone_meal.rs index e5cfb5c07..83acafb36 100644 --- a/crates/pumpkin/src/item/items/bone_meal.rs +++ b/crates/pumpkin/src/item/items/bone_meal.rs @@ -38,7 +38,6 @@ impl ItemBehaviour for BoneMealItem { if server .block_registry .bone_meal(block, &world, &location, state_id) - .await { world.sync_world_event(WorldEvent::ParticlesAndSoundPlantGrowth, location, 15); item.decrement_unless_creative(player.gamemode.load(), 1); diff --git a/crates/pumpkin/src/item/items/bow.rs b/crates/pumpkin/src/item/items/bow.rs index 32d87b3e9..784b7d1d3 100644 --- a/crates/pumpkin/src/item/items/bow.rs +++ b/crates/pumpkin/src/item/items/bow.rs @@ -41,13 +41,14 @@ impl ItemBehaviour for BowItem { // Get the held item stack let inventory = player.inventory(); - let stack = inventory.held_item().await; + let stack = inventory.held_item(); // Start the bow drawing animation - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } @@ -108,7 +109,7 @@ impl BowItem { // Check for Infinity enchantment let mut has_infinity = false; - let held = player.inventory().held_item().await; + let held = player.inventory().held_item(); if let Some(enchantments) = held.get_data_component::() { @@ -118,7 +119,7 @@ impl BowItem { .any(|(e, _)| **e == pumpkin_data::Enchantment::INFINITY); } - Self::fire_arrow(player, power, projectile).await; + Self::fire_arrow(player, power, &projectile); // Consume arrow (if not creative and no Infinity) if let Some(slot) = arrow_slot @@ -149,7 +150,7 @@ impl BowItem { } /// Fire an arrow from the bow - pub async fn fire_arrow(player: &Player, power: f32, projectile: ItemStack) { + pub fn fire_arrow(player: &Player, power: f32, projectile: &ItemStack) { if power < 0.1 { return; // Not enough charge } @@ -173,10 +174,10 @@ impl BowItem { }; let mut arrow = - ArrowEntity::new_shot(arrow_entity, player.get_entity(), &projectile, pickup); + ArrowEntity::new_shot(arrow_entity, player.get_entity(), projectile, pickup); // Read enchantments of the held item (bow) - let stack = player.inventory().held_item().await; + let stack = player.inventory().held_item(); if let Some(enchantments) = stack.get_data_component::() { @@ -204,7 +205,7 @@ impl BowItem { // Spawn the arrow entity in the world let arrow_arc: Arc = Arc::new(arrow); - world.spawn_entity(arrow_arc).await; + world.spawn_entity(arrow_arc); // Play bow shoot sound let sound_pitch = 1.0 / (rand::random::() * 0.4 + 1.2) + power * 0.5; diff --git a/crates/pumpkin/src/item/items/brush.rs b/crates/pumpkin/src/item/items/brush.rs index 9e1d2980c..e8d6d913d 100644 --- a/crates/pumpkin/src/item/items/brush.rs +++ b/crates/pumpkin/src/item/items/brush.rs @@ -87,11 +87,12 @@ impl ItemBehaviour for BrushItem { SoundCategory::Players, &player.position(), ); - let stack = player.inventory().held_item().await; - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + let stack = player.inventory().held_item(); + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } @@ -122,9 +123,7 @@ impl ItemBehaviour for BrushItem { if current_stage < 3 { let next_stage_id = set_dusted_stage(block, current_state_id, current_stage + 1); - world - .set_block_state(&location, next_stage_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&location, next_stage_id, BlockFlags::NOTIFY_ALL); world.play_sound( if is_sand { @@ -142,9 +141,7 @@ impl ItemBehaviour for BrushItem { Block::GRAVEL.default_state.id }; - world - .set_block_state(&location, replacement_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&location, replacement_state_id, BlockFlags::NOTIFY_ALL); world.play_sound( if is_sand { @@ -166,7 +163,7 @@ impl ItemBehaviour for BrushItem { Entity::new(world.clone(), spawn_pos, &EntityType::ITEM), ItemStack::new(1, loot_item), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); } player.damage_held_item(1).await; @@ -178,11 +175,12 @@ impl ItemBehaviour for BrushItem { ); } - let stack = player.inventory().held_item().await; - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + let stack = player.inventory().held_item(); + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } @@ -206,7 +204,7 @@ impl ItemBehaviour for BrushItem { Entity::new(world.clone(), ent.pos.load(), &EntityType::ITEM), ItemStack::new(1, &Item::ARMADILLO_SCUTE), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); player.damage_held_item(16).await; } else { @@ -218,11 +216,12 @@ impl ItemBehaviour for BrushItem { ); } - let stack = player.inventory().held_item().await; - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + let stack = player.inventory().held_item(); + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } diff --git a/crates/pumpkin/src/item/items/bucket.rs b/crates/pumpkin/src/item/items/bucket.rs index e1bc3d44e..053b12ffc 100644 --- a/crates/pumpkin/src/item/items/bucket.rs +++ b/crates/pumpkin/src/item/items/bucket.rs @@ -104,27 +104,28 @@ fn set_waterlogged(block: &Block, state: BlockStateId, waterlogged: bool) -> Blo async fn give_player_bucket_item(player: &Player, item: &'static Item) { if player.gamemode.load() == GameMode::Creative { - let inv = player.inventory.main_inventory.read().await; - for stack in inv.iter() { - if stack.item.id == item.id { - return; - } + let has_item = { + let inv = player + .inventory + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + inv.iter().any(|stack| stack.item.id == item.id) + }; + if has_item { + return; } - drop(inv); let mut item_stack = ItemStack::new(1, item); - player - .inventory - .insert_stack_anywhere(&mut item_stack) - .await; + player.inventory.insert_stack_anywhere(&mut item_stack); } else { let item_stack = ItemStack::new(1, item); - let mut held_stack = player.inventory.held_item().await; + let mut held_stack = player.inventory.held_item(); if held_stack.item_count == 1 { - player.inventory.set_held_item(item_stack).await; + player.inventory.set_held_item(item_stack); } else { held_stack.decrement(1); - player.inventory.set_held_item(held_stack).await; + player.inventory.set_held_item(held_stack); player .inventory .offer_or_drop_stack(item_stack, player) @@ -142,36 +143,28 @@ pub(crate) async fn try_pickup_fluid_at( let (block, state) = world.get_block_and_state_id(&block_pos); if block == &Block::POWDER_SNOW { - world - .break_block( - &block_pos, - None, - BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_DROPS, - ) - .await; + world.break_block( + &block_pos, + None, + BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_DROPS, + ); return Some(&Item::POWDER_SNOW_BUCKET); } if is_waterlogged(block, state) { let state_id = set_waterlogged(block, state, false); - world - .set_block_state(&block_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + world.set_block_state(&block_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS); world.schedule_fluid_tick(&Fluid::WATER, block_pos, 5, TickPriority::Normal); return Some(&Item::WATER_BUCKET); } if state == Block::LAVA.default_state.id || state == Block::WATER.default_state.id { - world - .break_block(&block_pos, None, BlockFlags::NOTIFY_NEIGHBORS) - .await; - world - .set_block_state( - &block_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.break_block(&block_pos, None, BlockFlags::NOTIFY_NEIGHBORS); + world.set_block_state( + &block_pos, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); return Some(if state == Block::LAVA.default_state.id { &Item::LAVA_BUCKET } else { @@ -195,9 +188,7 @@ async fn try_pickup_bucket_item( let (block, state) = world.get_block_and_state_id(&target_pos); if waterlogged_check(block, state).is_some() { let state_id = set_waterlogged(block, state, false); - world - .set_block_state(&target_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + world.set_block_state(&target_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS); world.schedule_fluid_tick(&Fluid::WATER, target_pos, 5, TickPriority::Normal); return Some(&Item::WATER_BUCKET); } @@ -221,11 +212,7 @@ pub(crate) fn play_bucket_evaporation(world: &Arc, position: &Vector3, - pos: BlockPos, - direction: BlockDirection, -) -> bool { +fn try_place_powder_snow(world: &Arc, pos: BlockPos, direction: BlockDirection) -> bool { let state = world.get_block_state(&pos); let target_pos = if state.replaceable() { pos @@ -236,13 +223,11 @@ async fn try_place_powder_snow( if !target_state.is_air() && !target_state.is_liquid() && !target_state.replaceable() { return false; } - world - .set_block_state( - &target_pos, - Block::POWDER_SNOW.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + &target_pos, + Block::POWDER_SNOW.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ); true } @@ -254,14 +239,12 @@ pub(crate) async fn try_place_filled_bucket( ) -> bool { let (block, state) = world.get_block_and_state(&pos); if item.id == Item::POWDER_SNOW_BUCKET.id { - return try_place_powder_snow(world, pos, direction).await; + return try_place_powder_snow(world, pos, direction); } if is_waterlogged(block, state.id) && item.id == Item::WATER_BUCKET.id { let state_id = set_waterlogged(block, state.id, true); - world - .set_block_state(&pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + world.set_block_state(&pos, state_id, BlockFlags::NOTIFY_NEIGHBORS); world.schedule_fluid_tick(&Fluid::WATER, pos, 5, TickPriority::Normal); return true; } @@ -274,25 +257,21 @@ pub(crate) async fn try_place_filled_bucket( return false; } let state_id = set_waterlogged(block, state.id, true); - world - .set_block_state(&target_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; + world.set_block_state(&target_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS); world.schedule_fluid_tick(&Fluid::WATER, target_pos, 5, TickPriority::Normal); return true; } if state.id == Block::AIR.default_state.id || state.is_liquid() { - world - .set_block_state( - &target_pos, - if item.id == Item::LAVA_BUCKET.id { - Block::LAVA.default_state.id - } else { - Block::WATER.default_state.id - }, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + world.set_block_state( + &target_pos, + if item.id == Item::LAVA_BUCKET.id { + Block::LAVA.default_state.id + } else { + Block::WATER.default_state.id + }, + BlockFlags::NOTIFY_NEIGHBORS, + ); return true; } @@ -309,7 +288,7 @@ impl ItemBehaviour for EmptyBucketItem { let world = player.world(); let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, world_inner: &Arc| { + let checker = |pos: &BlockPos, world_inner: &Arc| { let state_id = world_inner.get_block_state_id(pos); let block = Block::from_state_id(state_id); @@ -323,8 +302,7 @@ impl ItemBehaviour for EmptyBucketItem { || (block.id == Block::LAVA.id && state_id == Block::LAVA.default_state.id)) }; - let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker).await - else { + let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker) else { return; }; @@ -365,7 +343,7 @@ impl ItemBehaviour for FilledBucketItem { Box::pin(async move { let world = player.world(); let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, world_inner: &Arc| { + let checker = |pos: &BlockPos, world_inner: &Arc| { let state_id = world_inner.get_block_state_id(pos); if Fluid::from_state_id(state_id).is_some() { return false; @@ -373,7 +351,7 @@ impl ItemBehaviour for FilledBucketItem { state_id != Block::AIR.default_state.id }; - let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker).await else { + let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker) else { return; }; @@ -420,11 +398,10 @@ impl ItemBehaviour for MilkBucketItem { player: &'a Player, ) -> Pin + Send + 'a>> { Box::pin(async move { - let stack = player.inventory().held_item().await; + let stack = player.inventory().held_item(); player .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 32) - .await; + .set_active_hand(pumpkin_util::Hand::Right, stack, 32); }) } @@ -434,7 +411,7 @@ impl ItemBehaviour for MilkBucketItem { player: &'a Player, ) -> Pin + Send + 'a>> { Box::pin(async move { - player.living_entity.reset_effects_and_attributes().await; + player.living_entity.reset_effects_and_attributes(); give_player_bucket_item(player, &Item::BUCKET).await; }) } diff --git a/crates/pumpkin/src/item/items/bundle.rs b/crates/pumpkin/src/item/items/bundle.rs index 987177b3c..7c68c84bf 100644 --- a/crates/pumpkin/src/item/items/bundle.rs +++ b/crates/pumpkin/src/item/items/bundle.rs @@ -22,7 +22,7 @@ impl ItemBehaviour for BundleItem { player: &'a Player, ) -> Pin + Send + 'a>> { Box::pin(async move { - let mut held_item = player.inventory.held_item().await; + let mut held_item = player.inventory.held_item(); let mut matched = false; let mut used_slot_index = player.inventory.get_selected_slot() as usize; @@ -40,13 +40,13 @@ impl ItemBehaviour for BundleItem { ); let updated_bundle = held_item.clone(); - player.drop_item(extracted_stack).await; + player.drop_item(extracted_stack); player.sync_hand_slot(used_slot_index, updated_bundle).await; } } if !matched { - let mut off_hand_item = player.inventory.off_hand_item().await; + let mut off_hand_item = player.inventory.off_hand_item(); if !off_hand_item.is_empty() && Self::ids().contains(&off_hand_item.item.id) { used_slot_index = 40; // OFF_HAND_SLOT if let Some(bundle_contents) = @@ -61,7 +61,7 @@ impl ItemBehaviour for BundleItem { ); let updated_bundle = off_hand_item.clone(); - player.drop_item(extracted_stack).await; + player.drop_item(extracted_stack); player.sync_hand_slot(used_slot_index, updated_bundle).await; } } diff --git a/crates/pumpkin/src/item/items/crossbow.rs b/crates/pumpkin/src/item/items/crossbow.rs index bae7fd987..68d3e44af 100644 --- a/crates/pumpkin/src/item/items/crossbow.rs +++ b/crates/pumpkin/src/item/items/crossbow.rs @@ -32,7 +32,7 @@ impl ItemBehaviour for CrossbowItem { ) -> Pin + Send + 'a>> { Box::pin(async move { let inventory = player.inventory(); - let stack = inventory.held_item().await; + let stack = inventory.held_item(); // Every crossbow carries a ChargedProjectiles component by default, so its mere // presence does not mean the crossbow is loaded. Vanilla checks the list is also @@ -52,8 +52,7 @@ impl ItemBehaviour for CrossbowItem { player .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 72000) - .await; + .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); }) } @@ -67,7 +66,7 @@ impl ItemBehaviour for CrossbowItem { let use_ticks = 72000 - use_ticks; let mut charge_time = 25; - let mut stack = player.inventory().held_item().await; + let mut stack = player.inventory().held_item(); if let Some(enchantments) = stack.get_data_component::() { for (enchantment, level) in enchantments.enchantment.iter() { @@ -107,7 +106,7 @@ impl ItemBehaviour for CrossbowItem { projectiles: vec![arrow_nbt], })), )); - player.inventory().set_held_item(stack).await; + player.inventory().set_held_item(stack); if player.gamemode.load() != GameMode::Creative { player.consume_arrow(slot).await; @@ -120,7 +119,7 @@ impl ItemBehaviour for CrossbowItem { ); } } - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); }) } @@ -135,7 +134,7 @@ impl ItemBehaviour for CrossbowItem { impl CrossbowItem { async fn fire_projectiles(player: &Player) { - let mut held = player.inventory().held_item().await; + let mut held = player.inventory().held_item(); let projectiles = held.get_data_component::().cloned(); let has_multishot = held.get_data_component::() @@ -186,13 +185,13 @@ impl CrossbowItem { ); arrow.set_velocity_from_rotation(pitch, t_yaw, 0.0, 3.15, 1.0); let arrow_arc: Arc = Arc::new(arrow); - world.spawn_entity(arrow_arc).await; + world.spawn_entity(arrow_arc); } } held.patch .retain(|(id, _)| *id != DataComponent::ChargedProjectiles); - player.inventory().set_held_item(held).await; + player.inventory().set_held_item(held); player.damage_held_item(1).await; } } diff --git a/crates/pumpkin/src/item/items/egg.rs b/crates/pumpkin/src/item/items/egg.rs index e89e51d87..cce289dcb 100644 --- a/crates/pumpkin/src/item/items/egg.rs +++ b/crates/pumpkin/src/item/items/egg.rs @@ -37,37 +37,36 @@ impl ItemBehaviour for EggItem { ); // Capture the held item stack and pass it to the thrown egg entity - let item_stack: ItemStack = player.inventory.held_item().await; + let item_stack: ItemStack = player.inventory.held_item(); let entity = Entity::new(world.clone(), position, &EntityType::EGG); let egg = EggEntity::new_shot(entity, player.get_entity()); // Propagate the item stack so clients show correct variant - egg.set_item_stack(item_stack.clone()).await; + egg.set_item_stack(item_stack); let (yaw, pitch) = player.rotation(); egg.thrown .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(egg)).await; + world.spawn_entity(Arc::new(egg)); // Consume item - let mut main_hand = player.inventory.held_item().await; + let mut main_hand = player.inventory.held_item(); let consumed = if !main_hand.is_empty() && Self::ids().contains(&main_hand.item.id) { main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand).await; + player.inventory.set_held_item(main_hand); true } else { false }; if !consumed { - let mut off_hand = player.inventory.off_hand_item().await; + let mut off_hand = player.inventory.off_hand_item(); if !off_hand.is_empty() && Self::ids().contains(&off_hand.item.id) { off_hand.decrement_unless_creative(player.gamemode.load(), 1); player .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand) - .await; + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } } }) diff --git a/crates/pumpkin/src/item/items/end_crystal.rs b/crates/pumpkin/src/item/items/end_crystal.rs index d4ba129ef..8d93b27db 100644 --- a/crates/pumpkin/src/item/items/end_crystal.rs +++ b/crates/pumpkin/src/item/items/end_crystal.rs @@ -56,7 +56,7 @@ impl ItemBehaviour for EndCrystalItem { let entity = Entity::new(world.clone(), location.to_f64(), &EntityType::END_CRYSTAL); let end_crystal = Arc::new(EndCrystalEntity::new(entity)); - world.spawn_entity(end_crystal.clone()).await; + world.spawn_entity(end_crystal.clone()); end_crystal.set_show_bottom(false); item.decrement_unless_creative(player.gamemode.load(), 1); }) diff --git a/crates/pumpkin/src/item/items/ender_eye.rs b/crates/pumpkin/src/item/items/ender_eye.rs index f3eb72015..210dd624a 100644 --- a/crates/pumpkin/src/item/items/ender_eye.rs +++ b/crates/pumpkin/src/item/items/ender_eye.rs @@ -68,15 +68,13 @@ impl ItemBehaviour for EnderEyeItem { block.from_properties(&props).to_state_id(block) }; - world - .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_LISTENERS) - .await; + world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_LISTENERS); // Consume one item. item.decrement_unless_creative(player.gamemode.load(), 1); world.sync_world_event(WorldEvent::EndPortalFrameFill, location, 0); // Try to complete the portal. - EndPortal::get_new_portal(&world, location).await; + EndPortal::get_new_portal(&world, location); }) } @@ -89,10 +87,10 @@ impl ItemBehaviour for EnderEyeItem { let world = player.world(); let (start_pos, end_pos) = self.get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, w: &Arc| { + let checker = |pos: &BlockPos, w: &Arc| { w.get_block_state_id(pos) != Block::AIR.default_state.id }; - if let Some((hit_pos, _)) = world.raycast(start_pos, end_pos, checker).await + if let Some((hit_pos, _)) = world.raycast(start_pos, end_pos, checker) && world.get_block(&hit_pos) == &Block::END_PORTAL_FRAME { return; @@ -120,9 +118,9 @@ impl ItemBehaviour for EnderEyeItem { f64::from(target.0.y), f64::from(target.0.z), ); - eye.signal_to(target_vec).await; + eye.signal_to(target_vec); - world.spawn_entity(eye).await; + world.spawn_entity(eye); let pitch = 0.33f32 + rand::random::() * (0.5 - 0.33); world.play_sound_fine( @@ -133,10 +131,12 @@ impl ItemBehaviour for EnderEyeItem { pitch, ); - player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::LaunchedEyeOfEnder).await; - let mut stack = player.inventory.held_item().await; + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::LaunchedEyeOfEnder, + ); + let mut stack = player.inventory.held_item(); stack.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(stack).await; + player.inventory.set_held_item(stack); }) } diff --git a/crates/pumpkin/src/item/items/ender_pearl.rs b/crates/pumpkin/src/item/items/ender_pearl.rs index 269adc9c1..b9e51350c 100644 --- a/crates/pumpkin/src/item/items/ender_pearl.rs +++ b/crates/pumpkin/src/item/items/ender_pearl.rs @@ -53,26 +53,25 @@ impl ItemBehaviour for EnderPearlItem { POWER, DIVERGENCE, ); - world.spawn_entity(Arc::new(pearl)).await; + world.spawn_entity(Arc::new(pearl)); // Consume item - let mut main_hand = player.inventory.held_item().await; + let mut main_hand = player.inventory.held_item(); let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::ENDER_PEARL.id { main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand).await; + player.inventory.set_held_item(main_hand); true } else { false }; if !consumed { - let mut off_hand = player.inventory.off_hand_item().await; + let mut off_hand = player.inventory.off_hand_item(); if !off_hand.is_empty() && off_hand.item.id == Item::ENDER_PEARL.id { off_hand.decrement_unless_creative(player.gamemode.load(), 1); player .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand) - .await; + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } } }) diff --git a/crates/pumpkin/src/item/items/experience_bottle.rs b/crates/pumpkin/src/item/items/experience_bottle.rs index cb7c2126f..a64ed8052 100644 --- a/crates/pumpkin/src/item/items/experience_bottle.rs +++ b/crates/pumpkin/src/item/items/experience_bottle.rs @@ -32,11 +32,11 @@ impl ItemBehaviour for ExperienceBottleItem { ); let amount = (rand::random::() % 9 + 3) as u32; // 3..=11 exp - ExperienceOrbEntity::spawn(&world, pos, amount).await; + ExperienceOrbEntity::spawn(&world, pos, amount); - let mut held = player.inventory().held_item().await; + let mut held = player.inventory().held_item(); held.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory().set_held_item(held).await; + player.inventory().set_held_item(held); }) } diff --git a/crates/pumpkin/src/item/items/firework_rocket.rs b/crates/pumpkin/src/item/items/firework_rocket.rs index e8f88ffca..a8802d4b5 100644 --- a/crates/pumpkin/src/item/items/firework_rocket.rs +++ b/crates/pumpkin/src/item/items/firework_rocket.rs @@ -45,7 +45,7 @@ impl ItemBehaviour for FireworkRocketItem { &EntityType::FIREWORK_ROCKET, ); let entity = FireworkRocketEntity::new(entity); - world.spawn_entity(Arc::new(entity)).await; + world.spawn_entity(Arc::new(entity)); }) } @@ -63,7 +63,7 @@ impl ItemBehaviour for FireworkRocketItem { &EntityType::FIREWORK_ROCKET, ); let entity = FireworkRocketEntity::new_shot(entity, player.get_entity()); - world.spawn_entity(Arc::new(entity)).await; + world.spawn_entity(Arc::new(entity)); } }) } diff --git a/crates/pumpkin/src/item/items/fishing_rod.rs b/crates/pumpkin/src/item/items/fishing_rod.rs index 9c709eae4..4ff3bfab7 100644 --- a/crates/pumpkin/src/item/items/fishing_rod.rs +++ b/crates/pumpkin/src/item/items/fishing_rod.rs @@ -56,17 +56,17 @@ impl ItemBehaviour for FishingRodItem { .store(bobber.entity.entity_id, Ordering::Relaxed); let bobber_arc: Arc = Arc::new(bobber); - world.spawn_entity(bobber_arc).await; + world.spawn_entity(bobber_arc); } else { // Reel in if let Some(bobber_base) = world.get_entity_by_id(bobber_id) { if let Some(bobber) = bobber_base.cast_any().downcast_ref::() { - let _result = bobber.reel_in(player).await; + let _result = bobber.reel_in(player); // TODO: give items } - bobber_base.get_entity().remove().await; + bobber_base.get_entity().remove(); } player.fishing_bobber.store(-1, Ordering::Relaxed); diff --git a/crates/pumpkin/src/item/items/glass_bottle.rs b/crates/pumpkin/src/item/items/glass_bottle.rs index c6ce8e2f0..d19a9fd04 100644 --- a/crates/pumpkin/src/item/items/glass_bottle.rs +++ b/crates/pumpkin/src/item/items/glass_bottle.rs @@ -80,9 +80,7 @@ impl ItemBehaviour for GlassBottleItem { .flatten(); if let Some(new_state_id) = cauldron_action { - world - .set_block_state(&check_pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&check_pos, new_state_id, BlockFlags::NOTIFY_ALL); } world.play_sound( diff --git a/crates/pumpkin/src/item/items/goat_horn.rs b/crates/pumpkin/src/item/items/goat_horn.rs index ca3e73516..cd073a22d 100644 --- a/crates/pumpkin/src/item/items/goat_horn.rs +++ b/crates/pumpkin/src/item/items/goat_horn.rs @@ -27,11 +27,12 @@ impl ItemBehaviour for GoatHornItem { SoundCategory::Players, &player.position(), ); - let stack = player.inventory().held_item().await; - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + let stack = player.inventory().held_item(); + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } diff --git a/crates/pumpkin/src/item/items/hoe.rs b/crates/pumpkin/src/item/items/hoe.rs index dfab05729..164f2b5ba 100644 --- a/crates/pumpkin/src/item/items/hoe.rs +++ b/crates/pumpkin/src/item/items/hoe.rs @@ -72,13 +72,11 @@ impl ItemBehaviour for HoeItem { // Vanilla returns PASS without touching the block when nothing is tilled, // otherwise the rewrite would reset properties such as `snowy` on grass blocks. if changed { - world - .set_block_state( - &location, - future_block.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &location, + future_block.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } //Also rooted_dirt drop a hanging_root @@ -97,7 +95,7 @@ impl ItemBehaviour for HoeItem { entity, ItemStack::new(1, &Item::HANGING_ROOTS), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); } if changed && player.gamemode.load() != GameMode::Creative { diff --git a/crates/pumpkin/src/item/items/honeycomb.rs b/crates/pumpkin/src/item/items/honeycomb.rs index 906fa3576..4c35ca140 100644 --- a/crates/pumpkin/src/item/items/honeycomb.rs +++ b/crates/pumpkin/src/item/items/honeycomb.rs @@ -76,9 +76,7 @@ pub(crate) async fn try_wax_block(world: &Arc, location: BlockPos, block: new_block.default_state.id }; - world - .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL); world.sync_world_event(WorldEvent::ParticlesAndSoundWaxOn, location, 0); true } diff --git a/crates/pumpkin/src/item/items/ignite/fire_charge.rs b/crates/pumpkin/src/item/items/ignite/fire_charge.rs index 424ddbc2b..bdfadc41a 100644 --- a/crates/pumpkin/src/item/items/ignite/fire_charge.rs +++ b/crates/pumpkin/src/item/items/ignite/fire_charge.rs @@ -40,9 +40,7 @@ impl ItemBehaviour for FireChargeItem { let world = player.world(); Ignition::ignite_block( |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { - world - .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); world.play_block_sound(Sound::ItemFirechargeUse, SoundCategory::Blocks, pos); }, diff --git a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs index 8d4cb4b73..48766c492 100644 --- a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs +++ b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs @@ -63,9 +63,7 @@ impl ItemBehaviour for FlintAndSteelItem { let ignited = Ignition::ignite_block( |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { - world - .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); }, &world, location, diff --git a/crates/pumpkin/src/item/items/lead.rs b/crates/pumpkin/src/item/items/lead.rs index e07da57f8..d0907ce36 100644 --- a/crates/pumpkin/src/item/items/lead.rs +++ b/crates/pumpkin/src/item/items/lead.rs @@ -76,10 +76,10 @@ impl ItemBehaviour for LeadItem { if is_leashed_to_player { if knot.is_none() { - knot = Some(LeashKnotEntity::get_or_create(&world, location).await); + knot = Some(LeashKnotEntity::get_or_create(&world, location)); } if let Some(k) = &knot { - ent.leash_to(k.clone() as Arc).await; + ent.leash_to(k.clone() as Arc); any_leashed = true; } } diff --git a/crates/pumpkin/src/item/items/map.rs b/crates/pumpkin/src/item/items/map.rs index 39237d275..c565db7bc 100644 --- a/crates/pumpkin/src/item/items/map.rs +++ b/crates/pumpkin/src/item/items/map.rs @@ -31,12 +31,12 @@ impl ItemBehaviour for MapItem { }; let inventory = player.inventory(); - let held_stack = inventory.held_item().await; + let held_stack = inventory.held_item(); let (found, mut hand_stack, hand) = if !held_stack.is_empty() && held_stack.item.id == Item::MAP.id { (true, held_stack, pumpkin_util::Hand::Right) } else { - let off_hand = inventory.off_hand_item().await; + let off_hand = inventory.off_hand_item(); if !off_hand.is_empty() && off_hand.item.id == Item::MAP.id { (true, off_hand, pumpkin_util::Hand::Left) } else { @@ -62,10 +62,10 @@ impl ItemBehaviour for MapItem { let gamemode = player.gamemode.load(); if hand_stack.item_count == 1 && gamemode != GameMode::Creative { - inventory.set_stack_in_hand(hand, filled_map).await; + inventory.set_stack_in_hand(hand, filled_map); } else { hand_stack.decrement_unless_creative(gamemode, 1); - inventory.set_stack_in_hand(hand, hand_stack).await; + inventory.set_stack_in_hand(hand, hand_stack); inventory.offer_or_drop_stack(filled_map, player).await; } } diff --git a/crates/pumpkin/src/item/items/minecart.rs b/crates/pumpkin/src/item/items/minecart.rs index c1b12df89..678e99ae7 100644 --- a/crates/pumpkin/src/item/items/minecart.rs +++ b/crates/pumpkin/src/item/items/minecart.rs @@ -87,7 +87,7 @@ impl ItemBehaviour for MinecartItem { entity_type, ); let minecart_entity = Arc::new(MinecartEntity::new(entity)); - world.spawn_entity(minecart_entity).await; + world.spawn_entity(minecart_entity); item.decrement_unless_creative(player.gamemode.load(), 1); }) } diff --git a/crates/pumpkin/src/item/items/potions.rs b/crates/pumpkin/src/item/items/potions.rs index 7512b4cee..44105e1dc 100644 --- a/crates/pumpkin/src/item/items/potions.rs +++ b/crates/pumpkin/src/item/items/potions.rs @@ -70,13 +70,13 @@ impl ItemBehaviour for SplashPotionItem { let splash = SplashPotionEntity::new_shot(entity, player.get_entity()); // Copy the held item stack data into the projectile - let main_s = player.inventory.held_item().await; + let main_s = player.inventory.held_item(); let mut used_main = true; let mut stack = (!main_s.is_empty() && main_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id) .then_some(main_s); if stack.is_none() { - let off_s = player.inventory.off_hand_item().await; + let off_s = player.inventory.off_hand_item(); if !off_s.is_empty() && off_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id { stack = Some(off_s); @@ -84,27 +84,26 @@ impl ItemBehaviour for SplashPotionItem { } } let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); - splash.set_item_stack(stack).await; + splash.set_item_stack(stack); let (yaw, pitch) = player.rotation(); splash .thrown .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(splash)).await; + world.spawn_entity(Arc::new(splash)); // Decrement the used stack (clear) if used_main { - let mut s = player.inventory.held_item().await; + let mut s = player.inventory.held_item(); s.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(s).await; + player.inventory.set_held_item(s); } else { - let mut s = player.inventory.off_hand_item().await; + let mut s = player.inventory.off_hand_item(); s.decrement_unless_creative(player.gamemode.load(), 1); player .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, s) - .await; + .set_stack_in_hand(pumpkin_util::Hand::Left, s); } }) } @@ -132,13 +131,13 @@ impl ItemBehaviour for LingeringPotionItem { let ling = LingeringPotionEntity::new_shot(entity, player.get_entity()); // Copy the held item stack data into the projectile - let main_s = player.inventory.held_item().await; + let main_s = player.inventory.held_item(); let mut used_main = true; let mut stack = (!main_s.is_empty() && main_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id) .then_some(main_s); if stack.is_none() { - let off_s = player.inventory.off_hand_item().await; + let off_s = player.inventory.off_hand_item(); if !off_s.is_empty() && off_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id { @@ -147,26 +146,25 @@ impl ItemBehaviour for LingeringPotionItem { } } let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); - ling.set_item_stack(stack).await; + ling.set_item_stack(stack); let (yaw, pitch) = player.rotation(); ling.thrown .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(ling)).await; + world.spawn_entity(Arc::new(ling)); // Decrement the used stack (clear) if used_main { - let mut s = player.inventory.held_item().await; + let mut s = player.inventory.held_item(); s.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(s).await; + player.inventory.set_held_item(s); } else { - let mut s = player.inventory.off_hand_item().await; + let mut s = player.inventory.off_hand_item(); s.decrement_unless_creative(player.gamemode.load(), 1); player .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, s) - .await; + .set_stack_in_hand(pumpkin_util::Hand::Left, s); } }) } diff --git a/crates/pumpkin/src/item/items/shears.rs b/crates/pumpkin/src/item/items/shears.rs index b0cd2b1c7..a5773b153 100644 --- a/crates/pumpkin/src/item/items/shears.rs +++ b/crates/pumpkin/src/item/items/shears.rs @@ -100,7 +100,7 @@ impl ItemBehaviour for ShearsItem { Entity::new(world.clone(), pos, &EntityType::ITEM), ItemStack::new(wool_count, wool_item), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); player.damage_held_item(1).await; } }) @@ -145,9 +145,7 @@ async fn handle_growing_plant( }); if let Some(new_state_id) = action { - world - .set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL); world.play_sound( Sound::BlockGrowingPlantCrop, SoundCategory::Blocks, @@ -192,9 +190,7 @@ async fn handle_beehive( }); if let Some(new_state_id) = action { - world - .set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL); world.play_sound( Sound::BlockBeehiveShear, SoundCategory::Blocks, @@ -210,7 +206,7 @@ async fn handle_beehive( Entity::new(world.clone(), drop_pos, &EntityType::ITEM), ItemStack::new(3, &Item::HONEYCOMB), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); player.damage_held_item(1).await; return true; } @@ -222,9 +218,7 @@ async fn handle_pumpkin(player: &Player, location: &BlockPos, block: &Block) { if block.id == Block::PUMPKIN.id { let world = player.world(); let carved_state = Block::CARVED_PUMPKIN.default_state.id; - world - .set_block_state(location, carved_state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(location, carved_state, BlockFlags::NOTIFY_ALL); world.play_sound( Sound::BlockPumpkinCarve, SoundCategory::Blocks, @@ -240,7 +234,7 @@ async fn handle_pumpkin(player: &Player, location: &BlockPos, block: &Block) { Entity::new(world.clone(), drop_pos, &EntityType::ITEM), ItemStack::new(4, &Item::PUMPKIN_SEEDS), )); - world.spawn_entity(item_entity).await; + world.spawn_entity(item_entity); player.damage_held_item(1).await; } } diff --git a/crates/pumpkin/src/item/items/shovel.rs b/crates/pumpkin/src/item/items/shovel.rs index 316d11c28..452f40159 100644 --- a/crates/pumpkin/src/item/items/shovel.rs +++ b/crates/pumpkin/src/item/items/shovel.rs @@ -46,13 +46,11 @@ impl ItemBehaviour for ShovelItem { && face != BlockDirection::Down && world.get_block_state(&location.up()).is_air() { - world - .set_block_state( - &location, - Block::DIRT_PATH.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &location, + Block::DIRT_PATH.default_state.id, + BlockFlags::NOTIFY_ALL, + ); true } else { false @@ -66,13 +64,11 @@ impl ItemBehaviour for ShovelItem { world.sync_world_event(WorldEvent::SoundExtinguishFire, location, 0); campfire_props.lit = false; - world - .set_block_state( - &location, - campfire_props.to_state_id(block), - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &location, + campfire_props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ); let seed = rng().random::(); player .play_sound( diff --git a/crates/pumpkin/src/item/items/snowball.rs b/crates/pumpkin/src/item/items/snowball.rs index f3d0d0c4b..43b8f49e3 100644 --- a/crates/pumpkin/src/item/items/snowball.rs +++ b/crates/pumpkin/src/item/items/snowball.rs @@ -40,26 +40,25 @@ impl ItemBehaviour for SnowBallItem { snowball .thrown .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(snowball)).await; + world.spawn_entity(Arc::new(snowball)); // Consume item - let mut main_hand = player.inventory.held_item().await; + let mut main_hand = player.inventory.held_item(); let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::SNOWBALL.id { main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand).await; + player.inventory.set_held_item(main_hand); true } else { false }; if !consumed { - let mut off_hand = player.inventory.off_hand_item().await; + let mut off_hand = player.inventory.off_hand_item(); if !off_hand.is_empty() && off_hand.item.id == Item::SNOWBALL.id { off_hand.decrement_unless_creative(player.gamemode.load(), 1); player .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand) - .await; + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } } }) diff --git a/crates/pumpkin/src/item/items/spawn_egg.rs b/crates/pumpkin/src/item/items/spawn_egg.rs index 4c09e84bc..73b9166f5 100644 --- a/crates/pumpkin/src/item/items/spawn_egg.rs +++ b/crates/pumpkin/src/item/items/spawn_egg.rs @@ -103,7 +103,7 @@ impl ItemBehaviour for SpawnEggItem { apply_entity_variant(item, mob.as_ref()); // Broadcast the new mob to all players - world.spawn_entity(mob).await; + world.spawn_entity(mob); item.decrement_unless_creative(player.gamemode.load(), 1); } }) diff --git a/crates/pumpkin/src/item/items/spyglass.rs b/crates/pumpkin/src/item/items/spyglass.rs index 9139d9316..76da0a08e 100644 --- a/crates/pumpkin/src/item/items/spyglass.rs +++ b/crates/pumpkin/src/item/items/spyglass.rs @@ -27,11 +27,12 @@ impl ItemBehaviour for SpyglassItem { SoundCategory::Players, &player.position(), ); - let stack = player.inventory().held_item().await; - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION) - .await; + let stack = player.inventory().held_item(); + player.living_entity.set_active_hand( + pumpkin_util::Hand::Right, + stack, + Self::USE_DURATION, + ); }) } diff --git a/crates/pumpkin/src/item/items/trident.rs b/crates/pumpkin/src/item/items/trident.rs index 067db6a0a..fd243b3d7 100644 --- a/crates/pumpkin/src/item/items/trident.rs +++ b/crates/pumpkin/src/item/items/trident.rs @@ -32,12 +32,11 @@ impl ItemBehaviour for TridentItem { ) -> Pin + Send + 'a>> { Box::pin(async move { let inventory = player.inventory(); - let stack = inventory.held_item().await; + let stack = inventory.held_item(); player .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 72000) - .await; + .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); }) } @@ -58,7 +57,7 @@ impl ItemBehaviour for TridentItem { } let world = player.world(); - let stack_guard = player.inventory().held_item().await; + let stack_guard = player.inventory().held_item(); // Check Riptide level let mut riptide_level = 0u32; @@ -76,7 +75,7 @@ impl ItemBehaviour for TridentItem { let in_water = world.get_block_state(&player.position().to_block_pos()).id == pumpkin_data::Block::WATER.default_state.id; if !in_water { - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); return; } @@ -100,7 +99,7 @@ impl ItemBehaviour for TridentItem { } player.damage_held_item(1).await; - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); return; } @@ -114,7 +113,7 @@ impl ItemBehaviour for TridentItem { ArrowPickup::Allowed, ); trident.set_velocity_from_rotation(pitch, yaw, 0.0, 2.5, 1.0); - world.spawn_entity(Arc::new(trident)).await; + world.spawn_entity(Arc::new(trident)); world.play_sound( Sound::ItemTridentThrow, @@ -149,7 +148,7 @@ impl ItemBehaviour for TridentItem { } } - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); }) } diff --git a/crates/pumpkin/src/item/items/wind_charge.rs b/crates/pumpkin/src/item/items/wind_charge.rs index 5c9f967d6..56dd6ca0a 100644 --- a/crates/pumpkin/src/item/items/wind_charge.rs +++ b/crates/pumpkin/src/item/items/wind_charge.rs @@ -48,9 +48,7 @@ impl ItemBehaviour for WindChargeItem { // TODO: player.incrementStat(Stats.USED) // TODO: Implement that the projectile will explode on impact - world - .spawn_entity(Arc::new(WindChargeEntity::new_normal(wind_charge))) - .await; + world.spawn_entity(Arc::new(WindChargeEntity::new_normal(wind_charge))); }) } diff --git a/crates/pumpkin/src/item/potion.rs b/crates/pumpkin/src/item/potion.rs index bc717b772..049d52b57 100644 --- a/crates/pumpkin/src/item/potion.rs +++ b/crates/pumpkin/src/item/potion.rs @@ -132,7 +132,7 @@ impl PotionContents { } /// Apply instant or duration effects to a target living entity. - pub async fn apply_effects_to( + pub fn apply_effects_to( target: &LivingEntity, effects: Vec<(&'static StatusEffect, i32, u8, bool, bool, bool)>, scale: f32, @@ -155,13 +155,11 @@ impl PotionContents { } else if effect_type.id == pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE.id { let amount = (6 * ((amplifier as i32) + 1)) as f32 * instant_scale; - target - .damage( - target.get_entity(), - amount, - pumpkin_data::damage::DamageType::MAGIC, - ) - .await; + let _ = target.damage( + target.get_entity(), + amount, + pumpkin_data::damage::DamageType::MAGIC, + ); } // For instant effects, still add a short visual effect entry as before @@ -174,7 +172,7 @@ impl PotionContents { show_icon, blend: false, }; - target.add_effect(eff).await; + target.add_effect(eff); } else { // Duration scaling let duration_scale = source.duration_scale(scale); @@ -189,7 +187,7 @@ impl PotionContents { show_icon, blend: false, }; - target.add_effect(eff).await; + target.add_effect(eff); } } } diff --git a/crates/pumpkin/src/lib.rs b/crates/pumpkin/src/lib.rs index f6f4f6f83..b6b06724c 100644 --- a/crates/pumpkin/src/lib.rs +++ b/crates/pumpkin/src/lib.rs @@ -306,9 +306,15 @@ impl PumpkinServer { // Ticker { let ticker_server = server.clone(); - server.spawn_task(async move { - Ticker::run(&ticker_server).await; - }); + if let Err(err) = std::thread::Builder::new() + .name("Server-Ticker".into()) + .spawn(move || { + Ticker::run(&ticker_server); + }) + { + error!("Failed to spawn Server-Ticker thread: {err}"); + std::process::exit(1); + } }; let (bedrock_status, ice_socket) = Self::bind_bedrock_status(&server).await; diff --git a/crates/pumpkin/src/net/bedrock/mod.rs b/crates/pumpkin/src/net/bedrock/mod.rs index 4ef104738..5e72233e9 100644 --- a/crates/pumpkin/src/net/bedrock/mod.rs +++ b/crates/pumpkin/src/net/bedrock/mod.rs @@ -722,13 +722,13 @@ impl BedrockClient { self.handle_respawn(player, SRespawn::read(reader)?).await; } SAnimate::PACKET_ID => { - self.handle_animate(player, server, &SAnimate::read(reader)?).await; + self.handle_animate(player, server, &SAnimate::read(reader)?); } SActorEvent::PACKET_ID => { - self.handle_actor_event(player, SActorEvent::read(reader)?).await; + self.handle_actor_event(player, &SActorEvent::read(reader)?); } SEmote::PACKET_ID => { - self.handle_emote(player, server, SEmote::read_slice(reader)?).await; + self.handle_emote(player, server, SEmote::read_slice(reader)?); } SEmoteList::PACKET_ID => { self.handle_emote_list(player, server, &SEmoteList::read(reader)?); diff --git a/crates/pumpkin/src/net/bedrock/play/actor_event.rs b/crates/pumpkin/src/net/bedrock/play/actor_event.rs index 117e87f44..0e493d5a4 100644 --- a/crates/pumpkin/src/net/bedrock/play/actor_event.rs +++ b/crates/pumpkin/src/net/bedrock/play/actor_event.rs @@ -2,13 +2,13 @@ use super::*; impl BedrockClient { - pub async fn handle_actor_event(&self, player: &Player, packet: SActorEvent) { + pub fn handle_actor_event(&self, player: &Player, packet: &SActorEvent) { if packet.event_id != ActorEventID::Feed || !player .living_entity .item_in_use .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .as_ref() .and_then(|item| item.get_data_component::()) .is_some_and(|consumable| consumable.animation == ConsumeAnimation::Eat) diff --git a/crates/pumpkin/src/net/bedrock/play/animate.rs b/crates/pumpkin/src/net/bedrock/play/animate.rs index 34b554697..52c8df198 100644 --- a/crates/pumpkin/src/net/bedrock/play/animate.rs +++ b/crates/pumpkin/src/net/bedrock/play/animate.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub async fn handle_animate(&self, player: &Arc, _server: &Server, packet: &SAnimate) { + pub fn handle_animate(&self, player: &Arc, _server: &Server, packet: &SAnimate) { if !player.has_client_loaded() { return; } @@ -26,7 +26,7 @@ impl BedrockClient { data: 0.0, swing_source: None, }; - world.broadcast_editioned(&je_packet, &be_packet).await; + world.broadcast_editioned(&je_packet, &be_packet); } } } diff --git a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs index ba546188d..223574401 100644 --- a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs @@ -25,7 +25,7 @@ impl BedrockClient { return; } - let slot_with_stack = player.inventory().get_slot_with_stack(&stack).await; + let slot_with_stack = player.inventory().get_slot_with_stack(&stack); if slot_with_stack != -1 { if pumpkin_inventory::player::player_inventory::PlayerInventory::is_valid_hotbar_index( @@ -85,21 +85,22 @@ impl BedrockClient { .await; // Sync main hand equipment to other players - let stack_in_hand = player.inventory().held_item().await; + let stack_in_hand = player.inventory().held_item(); let equipment = &[(EquipmentSlot::MAIN_HAND, stack_in_hand)]; player.living_entity.send_equipment_changes(equipment); // Sync bedrock inventory updates + let slots = player + .inventory() + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .map(NetworkItemStackDescriptor::from) + .collect(); self.enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), - slots: player - .inventory() - .main_inventory - .read() - .await - .iter() - .map(NetworkItemStackDescriptor::from) - .collect(), + slots, full_container_name: FullContainerName { container_name: ContainerName::Inventory, dynamic_id: None, diff --git a/crates/pumpkin/src/net/bedrock/play/chat_message.rs b/crates/pumpkin/src/net/bedrock/play/chat_message.rs index d710b2d4c..5bf07e640 100644 --- a/crates/pumpkin/src/net/bedrock/play/chat_message.rs +++ b/crates/pumpkin/src/net/bedrock/play/chat_message.rs @@ -51,7 +51,7 @@ impl BedrockClient { packet.filtered_message.map(std::borrow::Cow::into_owned), ); - entity.world.load().broadcast_editioned(&je_packet, &be_packet).await; + entity.world.load().broadcast_editioned(&je_packet, &be_packet); } } }} diff --git a/crates/pumpkin/src/net/bedrock/play/container_close.rs b/crates/pumpkin/src/net/bedrock/play/container_close.rs index d23efcd4f..6ec4cba80 100644 --- a/crates/pumpkin/src/net/bedrock/play/container_close.rs +++ b/crates/pumpkin/src/net/bedrock/play/container_close.rs @@ -28,16 +28,17 @@ impl BedrockClient { .await; // Sync the inventory content to Bedrock client + let slots = player + .inventory() + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .map(NetworkItemStackDescriptor::from) + .collect(); self.enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), // player inventory - slots: player - .inventory() - .main_inventory - .read() - .await - .iter() - .map(NetworkItemStackDescriptor::from) - .collect(), + slots, full_container_name: FullContainerName { container_name: ContainerName::Inventory, dynamic_id: None, diff --git a/crates/pumpkin/src/net/bedrock/play/emote.rs b/crates/pumpkin/src/net/bedrock/play/emote.rs index 8256c6865..ebe4f7f40 100644 --- a/crates/pumpkin/src/net/bedrock/play/emote.rs +++ b/crates/pumpkin/src/net/bedrock/play/emote.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub async fn handle_emote(&self, player: &Arc, _server: &Server, packet: SEmote<'_>) { + pub fn handle_emote(&self, player: &Arc, _server: &Server, packet: SEmote<'_>) { if !player.has_client_loaded() { return; } @@ -20,15 +20,13 @@ impl BedrockClient { broadcast_packet.actor_runtime_id = VarULong(entity.entity_id as u64); broadcast_packet.flags |= pumpkin_protocol::bedrock::server::emote::EMOTE_FLAG_SERVER_SIDE; - world - .broadcast_packet_except_editioned( - &[player.gameprofile.id], - &CEntityAnimation::new( - VarInt(entity.entity_id), - Animation::SwingMainArm, // Fallback for Java? Or just ignore - ), - &broadcast_packet, - ) - .await; + world.broadcast_packet_except_editioned( + &[player.gameprofile.id], + &CEntityAnimation::new( + VarInt(entity.entity_id), + Animation::SwingMainArm, // Fallback for Java? Or just ignore + ), + &broadcast_packet, + ); } } diff --git a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs index 7af675c28..e4797a918 100644 --- a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs @@ -31,7 +31,7 @@ impl BedrockClient { .get_cloned_stack() .await; if !current_stack.is_empty() { - player.drop_item(current_stack.clone()).await; + player.drop_item(current_stack.clone()); player_screen_handler .get_slot(screen_slot) @@ -67,7 +67,7 @@ impl BedrockClient { let old_stack = descriptor_to_stack(&action.old_item); let new_stack = descriptor_to_stack(&action.new_item); if old_stack.is_empty() && !new_stack.is_empty() { - player.drop_item(new_stack).await; + player.drop_item(new_stack); } } else if let Some(window_id) = action.window_id { if let Some(screen_slot) = @@ -119,16 +119,17 @@ impl BedrockClient { } if inventory_updated { + let slots = player + .inventory() + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .map(NetworkItemStackDescriptor::from) + .collect(); self.enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), - slots: player - .inventory() - .main_inventory - .read() - .await - .iter() - .map(NetworkItemStackDescriptor::from) - .collect(), + slots, full_container_name: FullContainerName { container_name: ContainerName::Inventory, dynamic_id: None, @@ -169,16 +170,33 @@ impl BedrockClient { // Click block let client_stack = descriptor_to_stack(&data.item_in_hand); - let mut held_item = player.inventory().held_item().await; + let mut held_item = player.inventory().held_item(); if !client_stack.is_empty() { if held_item.is_empty() || held_item.item.id != client_stack.item.id { held_item = client_stack.clone(); } } - let result = server - .block_registry - .use_with_item( + let result = server.block_registry.use_with_item( + block, + player, + &data.block_position, + &BlockHitResult { + face: &face, + cursor_pos: &data.click_position, + }, + &mut held_item, + &EquipmentSlot::MAIN_HAND, + &server, + &world, + ); + + if result.consumes_action() { + return; + } + + if matches!(result, BlockActionResult::PassToDefaultBlockAction) { + server.block_registry.on_use( block, player, &data.block_position, @@ -186,32 +204,9 @@ impl BedrockClient { face: &face, cursor_pos: &data.click_position, }, - &mut held_item, - &EquipmentSlot::MAIN_HAND, &server, &world, - ) - .await; - - if result.consumes_action() { - return; - } - - if matches!(result, BlockActionResult::PassToDefaultBlockAction) { - server - .block_registry - .on_use( - block, - player, - &data.block_position, - &BlockHitResult { - face: &face, - cursor_pos: &data.click_position, - }, - &server, - &world, - ) - .await; + ); } let mut stack = held_item; @@ -259,18 +254,18 @@ impl BedrockClient { } } } - player.inventory().set_held_item(stack).await; + player.inventory().set_held_item(stack); } } else if data.action_type.0 == 1 { // Click air / Use item let client_stack = descriptor_to_stack(&data.item_in_hand); - let mut held = player.inventory.held_item().await; + let mut held = player.inventory.held_item(); if !client_stack.is_empty() && (held.is_empty() || held.item.id != client_stack.item.id) { held = client_stack.clone(); - player.inventory.set_held_item(held.clone()).await; + player.inventory.set_held_item(held.clone()); } let event = PlayerInteractEvent::new( @@ -303,35 +298,40 @@ impl BedrockClient { || food.can_always_eat || player.hunger_manager.level.load() < 20 { - player - .living_entity - .set_active_hand( - Hand::Left, - held.clone(), - held.get_max_use_time(), - ) - .await; - } - } else { - player - .living_entity - .set_active_hand( + player.living_entity.set_active_hand( Hand::Left, held.clone(), held.get_max_use_time(), - ) - .await; + ); + } + } else { + player.living_entity.set_active_hand( + Hand::Left, + held.clone(), + held.get_max_use_time(), + ); } } if let Some(equippable) = held.get_data_component::() { - let inventory = player.inventory(); - let mut equipment_guard = inventory.entity_equipment.lock().await; - let current_equipped = equipment_guard.get(equippable.slot); - if !current_equipped.are_items_and_components_equal(&held) { + let should_change = { + let inventory = player.inventory(); + let equipment_guard = inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let current_equipped = equipment_guard.get(equippable.slot); + !current_equipped.are_items_and_components_equal(&held) + }; + if should_change { player .enqueue_equipment_change(equippable.slot, &held) .await; + let inventory = player.inventory(); + let mut equipment_guard = inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let equip_item = equipment_guard .equipment .entry(equippable.slot.clone()) @@ -344,7 +344,8 @@ impl BedrockClient { held = equip_item.clone(); *equip_item = old_held; } - player.inventory().set_held_item(held.clone()).await; + drop(equipment_guard); + player.inventory().set_held_item(held.clone()); } } } @@ -367,7 +368,7 @@ impl BedrockClient { 0 | 2 => { let world = player.world(); if let Some(target) = world.get_entity_by_id(target_runtime_id) { - let mut stack = player.inventory().held_item().await; + let mut stack = player.inventory().held_item(); if !target.interact(player, &mut stack).await { let Some(server) = world.server.upgrade() else { return; @@ -376,7 +377,7 @@ impl BedrockClient { .item_registry .use_on_entity(&mut stack, player, target) .await; - player.inventory().set_held_item(stack).await; + player.inventory().set_held_item(stack); } } } @@ -397,14 +398,19 @@ impl BedrockClient { } } TransactionData::ReleaseItem(_data) => { - let item_in_use = player.living_entity.item_in_use.lock().await.clone(); + let item_in_use = player + .living_entity + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(stack) = item_in_use { let Some(server) = player.world().server.upgrade() else { return; }; server.item_registry.on_stopped_using(&stack, player).await; } - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); } } diff --git a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs index 4f75754eb..a03b60264 100644 --- a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs @@ -250,7 +250,7 @@ impl BedrockClient { let count = count.min(source_stack.item_count); if count > 0 { let dropped_stack = source_stack.copy_with_count(count); - player.drop_item(dropped_stack).await; + player.drop_item(dropped_stack); source_stack.decrement(count); let source_stack = if source_stack.is_empty() { @@ -518,16 +518,17 @@ impl BedrockClient { .await; if inventory_updated { + let slots = player + .inventory() + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .map(NetworkItemStackDescriptor::from) + .collect(); self.enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), - slots: player - .inventory() - .main_inventory - .read() - .await - .iter() - .map(NetworkItemStackDescriptor::from) - .collect(), + slots, full_container_name: FullContainerName { container_name: ContainerName::Inventory, dynamic_id: None, @@ -924,7 +925,7 @@ mod tests { build_equipment_slots, crafting::crafting_screen_handler::CraftingTableScreenHandler, entity_equipment::EntityEquipment, }; - use tokio::sync::Mutex; + use std::sync::Mutex; #[tokio::test] async fn crafting_table_maps_bedrock_player_inventory_after_its_ten_slots() { diff --git a/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs b/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs index 2b8809c03..8d13f3419 100644 --- a/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs +++ b/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs @@ -35,7 +35,7 @@ impl BedrockClient { let inv = player.inventory(); inv.set_selected_slot(slot); - let stack = inv.held_item().await; + let stack = inv.held_item(); let equipment = &[(EquipmentSlot::MAIN_HAND, stack)]; player.living_entity.send_equipment_changes(equipment); } diff --git a/crates/pumpkin/src/net/bedrock/play/player_action.rs b/crates/pumpkin/src/net/bedrock/play/player_action.rs index a6bc89097..98f793d78 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_action.rs @@ -39,37 +39,36 @@ impl BedrockClient { } if player.gamemode.load() == GameMode::Creative { - let new_state = world - .break_block( - &location, - Some(player.clone()), - BlockFlags::NOTIFY_NEIGHBORS | BlockFlags::SKIP_DROPS, - ) - .await; + let new_state = world.break_block( + &location, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS | BlockFlags::SKIP_DROPS, + ); if new_state.is_some() { server .block_registry - .broken(&world, block, player, &location, server, state) - .await; + .broken(&world, block, player, &location, server, state); } } else if !state.is_air() { - let speed = crate::block::calc_block_breaking(player, state, block).await; + let speed = crate::block::calc_block_breaking(player, state, block); if speed >= 1.0 { player.stop_mining().await; let broken_state = world.get_block_state(&location); - let can_harvest = player.can_harvest(broken_state, block).await; - let new_state = world - .break_block( - &location, - Some(player.clone()), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + let can_harvest = player.can_harvest(broken_state, block); + let new_state = world.break_block( + &location, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS, + ); if new_state.is_some() { - server - .block_registry - .broken(&world, block, player, &location, server, broken_state) - .await; + server.block_registry.broken( + &world, + block, + player, + &location, + server, + broken_state, + ); player.apply_tool_damage_for_block_break(broken_state).await; if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; @@ -134,7 +133,7 @@ impl BedrockClient { let (block, state) = world.get_block_and_state(&location); if player.gamemode.load() != GameMode::Creative && !state.is_air() { - let speed = crate::block::calc_block_breaking(player, state, block).await; + let speed = crate::block::calc_block_breaking(player, state, block); let elapsed = player.tick_counter.load(Ordering::Relaxed) - player.start_mining_time.load(Ordering::Relaxed) + 1; @@ -145,7 +144,7 @@ impl BedrockClient { { player.stop_mining().await; - let can_harvest = player.can_harvest(state, block).await; + let can_harvest = player.can_harvest(state, block); let flags = if can_harvest { BlockFlags::NOTIFY_NEIGHBORS } else { @@ -153,13 +152,11 @@ impl BedrockClient { }; if world .break_block(&location, Some(player.clone()), flags) - .await .is_some() { server .block_registry - .broken(&world, block, player, &location, server, state) - .await; + .broken(&world, block, player, &location, server, state); player.apply_tool_damage_for_block_break(state).await; if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; diff --git a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs index e2b6bfa0d..91c716be2 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs @@ -21,7 +21,7 @@ impl BedrockClient { let entity = player.get_entity(); let on_ground = packet.input_data.get(InputData::VerticalCollision as usize) && packet.delta.y < 0.0 - && !entity.has_vehicle().await; + && !entity.has_vehicle(); entity.on_ground.store(on_ground, Ordering::Relaxed); let new_pos = packet @@ -90,7 +90,7 @@ impl BedrockClient { ), ); } else if pos_changed && rot_changed { - world.broadcast_packet_except_editioned_sync( + world.broadcast_packet_except_editioned( &[player.gameprofile.id], &pumpkin_protocol::java::client::play::CUpdateEntityPosRot::new( player.entity_id().into(), @@ -106,7 +106,7 @@ impl BedrockClient { &bedrock_move_packet, ); } else if pos_changed { - world.broadcast_packet_except_editioned_sync( + world.broadcast_packet_except_editioned( &[player.gameprofile.id], &pumpkin_protocol::java::client::play::CUpdateEntityPos::new( player.entity_id().into(), @@ -120,7 +120,7 @@ impl BedrockClient { &bedrock_move_packet, ); } else if rot_changed { - world.broadcast_packet_except_editioned_sync( + world.broadcast_packet_except_editioned( &[player.gameprofile.id], &pumpkin_protocol::java::client::play::CUpdateEntityRot::new( player.entity_id().into(), @@ -166,7 +166,7 @@ impl BedrockClient { if input_data.get(InputData::StartCrawling as usize) { entity.set_pose(EntityPose::Swimming); } else if input_data.get(InputData::StopCrawling as usize) { - player.update_player_pose().await; + player.update_player_pose(); } if input_data.get(InputData::StartFlying as usize) { @@ -176,6 +176,7 @@ impl BedrockClient { server; PlayerToggleFlightEvent::new(player.clone(), true); 'after: { + player.living_entity.fall_distance.store(0.0); { player.abilities.lock().await.flying = true; }; diff --git a/crates/pumpkin/src/net/java/mod.rs b/crates/pumpkin/src/net/java/mod.rs index 965879b48..ec2642fba 100644 --- a/crates/pumpkin/src/net/java/mod.rs +++ b/crates/pumpkin/src/net/java/mod.rs @@ -959,8 +959,7 @@ impl JavaClient { .await; } id if id == SEditBook::to_id(version) => { - self.handle_edit_book(player, SEditBook::read(&mut payload, &version)?) - .await; + self.handle_edit_book(player, &SEditBook::read(&mut payload, &version)?); } id if id == SUseItemOn::to_id(version) => { self.handle_use_item_on(player, SUseItemOn::read(&mut payload, &version)?, server) @@ -1076,16 +1075,14 @@ impl JavaClient { server, player, &SLockDifficulty::read(&mut payload, &version)?, - ) - .await; + ); } id if id == SChangeDifficulty::to_id(version) => { self.handle_change_difficulty( server, player, &SChangeDifficulty::read(&mut payload, &version)?, - ) - .await; + ); } id if id == SSetBeacon::to_id(version) => { self.handle_set_beacon(player, &SSetBeacon::read(&mut payload, &version)?) diff --git a/crates/pumpkin/src/net/java/play/change_difficulty.rs b/crates/pumpkin/src/net/java/play/change_difficulty.rs index 3b86fac76..d778e1f4e 100644 --- a/crates/pumpkin/src/net/java/play/change_difficulty.rs +++ b/crates/pumpkin/src/net/java/play/change_difficulty.rs @@ -3,7 +3,7 @@ use super::*; use pumpkin_protocol::java::server::play::SChangeDifficulty; impl JavaClient { - pub async fn handle_change_difficulty( + pub fn handle_change_difficulty( &self, server: &Server, player: &Player, @@ -26,7 +26,7 @@ impl JavaClient { return; } - server.set_difficulty(packet.difficulty, false).await; + server.set_difficulty(packet.difficulty, false); info!( "Player {} changed difficulty to {:?}", diff --git a/crates/pumpkin/src/net/java/play/chat_message.rs b/crates/pumpkin/src/net/java/play/chat_message.rs index 8608973f3..b086c536c 100644 --- a/crates/pumpkin/src/net/java/play/chat_message.rs +++ b/crates/pumpkin/src/net/java/play/chat_message.rs @@ -75,7 +75,7 @@ impl JavaClient { message, player.gameprofile.name.clone() ); - world.broadcast_editioned(&je_packet, &be_packet).await; + world.broadcast_editioned(&je_packet, &be_packet); } } }} diff --git a/crates/pumpkin/src/net/java/play/edit_book.rs b/crates/pumpkin/src/net/java/play/edit_book.rs index bf8bf1223..b6928e358 100644 --- a/crates/pumpkin/src/net/java/play/edit_book.rs +++ b/crates/pumpkin/src/net/java/play/edit_book.rs @@ -2,8 +2,8 @@ use super::*; impl JavaClient { - pub async fn handle_edit_book(&self, player: &Player, packet: SEditBook<'_>) { - let held_stack = player.inventory().held_item().await; + pub fn handle_edit_book(&self, player: &Player, packet: &SEditBook<'_>) { + let held_stack = player.inventory().held_item(); if held_stack.item.id != Item::WRITABLE_BOOK.id { return; } @@ -20,7 +20,7 @@ impl JavaClient { written_book .patch .push((DataComponent::WrittenBookContent, Some(content.to_dyn()))); - player.inventory().set_held_item(written_book).await; + player.inventory().set_held_item(written_book); } else { let mut writable_book = held_stack; let content = WritableBookContentImpl { pages }; @@ -30,7 +30,7 @@ impl JavaClient { writable_book .patch .push((DataComponent::WritableBookContent, Some(content.to_dyn()))); - player.inventory().set_held_item(writable_book).await; + player.inventory().set_held_item(writable_book); } } } diff --git a/crates/pumpkin/src/net/java/play/interact.rs b/crates/pumpkin/src/net/java/play/interact.rs index 2366f58e3..2cd126b1c 100644 --- a/crates/pumpkin/src/net/java/play/interact.rs +++ b/crates/pumpkin/src/net/java/play/interact.rs @@ -99,12 +99,12 @@ impl JavaClient { return; } } - let mut stack = player.inventory().held_item().await; + let mut stack = player.inventory().held_item(); let target_entity = event.target.get_entity(); if target_entity.entity_type.resource_name == "zombie_villager" && stack.item.registry_key == "golden_apple" { - player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::CuredZombieVillager).await; + player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::CuredZombieVillager); } let interacted = event.target.interact(player, &mut stack).await; @@ -114,7 +114,7 @@ impl JavaClient { .use_on_entity(&mut stack, player, event.target) .await; } - player.inventory().set_held_item(stack).await; + player.inventory().set_held_item(stack); } } } diff --git a/crates/pumpkin/src/net/java/play/lock_difficulty.rs b/crates/pumpkin/src/net/java/play/lock_difficulty.rs index 2f4a56514..0b32c9b29 100644 --- a/crates/pumpkin/src/net/java/play/lock_difficulty.rs +++ b/crates/pumpkin/src/net/java/play/lock_difficulty.rs @@ -3,14 +3,14 @@ use super::*; use pumpkin_protocol::java::server::play::SLockDifficulty; impl JavaClient { - pub async fn handle_lock_difficulty( + pub fn handle_lock_difficulty( &self, server: &Server, player: &Player, packet: &SLockDifficulty, ) { if player.permission_lvl.load() >= PermissionLvl::Two { - server.set_difficulty_locked(packet.locked).await; + server.set_difficulty_locked(packet.locked); info!( "Player {} locked difficulty: {}", player.gameprofile.name, packet.locked diff --git a/crates/pumpkin/src/net/java/play/mod.rs b/crates/pumpkin/src/net/java/play/mod.rs index f516cc5b2..4dd112c9e 100644 --- a/crates/pumpkin/src/net/java/play/mod.rs +++ b/crates/pumpkin/src/net/java/play/mod.rs @@ -80,7 +80,6 @@ use pumpkin_util::math::{polynomial_rolling_hash, position::BlockPos, wrap_degre use pumpkin_util::{GameMode, text::TextComponent}; use pumpkin_world::generation::structure::structures::jigsaw::JigsawJointType; use pumpkin_world::world::BlockFlags; -use tokio::sync::Mutex; /// In secure chat mode, Player will be kicked if they send a chat message with a timestamp that is older than this (in ms) /// Vanilla: 2 minutes diff --git a/crates/pumpkin/src/net/java/play/paddle_boat.rs b/crates/pumpkin/src/net/java/play/paddle_boat.rs index e2a5ea705..09d77a87a 100644 --- a/crates/pumpkin/src/net/java/play/paddle_boat.rs +++ b/crates/pumpkin/src/net/java/play/paddle_boat.rs @@ -5,9 +5,7 @@ impl JavaClient { pub async fn handle_paddle_boat(&self, player: &Arc, packet: SPaddleBoat) { let vehicle = player.get_entity().vehicle.lock().await.clone(); if let Some(vehicle) = vehicle { - vehicle - .set_paddle_state(packet.left_paddle, packet.right_paddle) - .await; + vehicle.set_paddle_state(packet.left_paddle, packet.right_paddle); } } } diff --git a/crates/pumpkin/src/net/java/play/pick_item.rs b/crates/pumpkin/src/net/java/play/pick_item.rs index 1ac03ab0f..832474b77 100644 --- a/crates/pumpkin/src/net/java/play/pick_item.rs +++ b/crates/pumpkin/src/net/java/play/pick_item.rs @@ -24,7 +24,7 @@ impl JavaClient { }; let stack = ItemStack::new(1, item); - let slot_with_stack = player.inventory().get_slot_with_stack(&stack).await; + let slot_with_stack = player.inventory().get_slot_with_stack(&stack); if slot_with_stack != -1 { if PlayerInventory::is_valid_hotbar_index(slot_with_stack as usize) { @@ -32,11 +32,10 @@ impl JavaClient { } else { player .inventory - .swap_slot_with_hotbar(slot_with_stack as usize) - .await; + .swap_slot_with_hotbar(slot_with_stack as usize); } } else if player.gamemode.load() == GameMode::Creative { - player.inventory.swap_stack_with_hotbar(stack).await; + player.inventory.swap_stack_with_hotbar(stack); } player @@ -87,7 +86,7 @@ impl JavaClient { if let Some(item) = found_egg.and_then(Item::from_id) { let stack = ItemStack::new(1, item); - let slot_with_stack = player.inventory().get_slot_with_stack(&stack).await; + let slot_with_stack = player.inventory().get_slot_with_stack(&stack); if slot_with_stack != -1 { if PlayerInventory::is_valid_hotbar_index(slot_with_stack as usize) { @@ -95,11 +94,10 @@ impl JavaClient { } else { player .inventory - .swap_slot_with_hotbar(slot_with_stack as usize) - .await; + .swap_slot_with_hotbar(slot_with_stack as usize); } } else if player.gamemode.load() == GameMode::Creative { - player.inventory.swap_stack_with_hotbar(stack).await; + player.inventory.swap_stack_with_hotbar(stack); } player diff --git a/crates/pumpkin/src/net/java/play/player_action.rs b/crates/pumpkin/src/net/java/play/player_action.rs index 4b6c22ab3..dd75542ce 100644 --- a/crates/pumpkin/src/net/java/play/player_action.rs +++ b/crates/pumpkin/src/net/java/play/player_action.rs @@ -52,19 +52,16 @@ impl JavaClient { pumpkin_data::block_properties::NoteBlockLikeProperties::from_state_id( state.id, block, ); - crate::block::blocks::note::NoteBlock::play_note(&props, &world, &position) - .await; - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::PlayNoteblock as i32, - 1, - ) - .await; + crate::block::blocks::note::NoteBlock::play_note(&props, &world, &position); + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::PlayNoteblock as i32, + 1, + ); } let inventory = player.inventory(); - let held = inventory.held_item().await; + let held = inventory.held_item(); if !server.item_registry.can_mine(held.item, player) { self.enqueue_client_packet(&CBlockUpdate::new( position, @@ -79,18 +76,15 @@ impl JavaClient { // TODO: Config if player.gamemode.load() == GameMode::Creative { // Block break & play sound - let new_state = world - .break_block( - &position, - Some(player.clone()), - BlockFlags::NOTIFY_NEIGHBORS | BlockFlags::SKIP_DROPS, - ) - .await; + let new_state = world.break_block( + &position, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS | BlockFlags::SKIP_DROPS, + ); if new_state.is_some() { server .block_registry - .broken(&world, block, player, &position, server, state) - .await; + .broken(&world, block, player, &position, server, state); } self.sync_block_state_to_client(&world, position).await; self.update_sequence(player, player_action.sequence.0); @@ -101,38 +95,36 @@ impl JavaClient { Ordering::Relaxed, ); if !state.is_air() { - let speed = block::calc_block_breaking(player, state, block).await; + let speed = block::calc_block_breaking(player, state, block); // Instant break if speed >= 1.0 { let broken_state = world.get_block_state(&position); - let can_harvest = player.can_harvest(broken_state, block).await; - let new_state = world - .break_block( - &position, - Some(player.clone()), - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; + let can_harvest = player.can_harvest(broken_state, block); + let new_state = world.break_block( + &position, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS, + ); if new_state.is_some() { - server - .block_registry - .broken(&world, block, player, &position, server, broken_state) - .await; + server.block_registry.broken( + &world, + block, + player, + &position, + server, + broken_state, + ); player.apply_tool_damage_for_block_break(broken_state).await; if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } - let item_id = player.inventory().held_item().await.item.id; - player - .increment_stat(StatisticCategory::Used, item_id as i32, 1) - .await; - player - .increment_stat( - StatisticCategory::Mined, - broken_state.id.as_u16() as i32, - 1, - ) - .await; + let item_id = player.inventory().held_item().item.id; + player.increment_stat(StatisticCategory::Used, item_id as i32, 1); + player.increment_stat( + StatisticCategory::Mined, + broken_state.id.as_u16() as i32, + 1, + ); } self.sync_block_state_to_client(&world, position).await; } else { @@ -204,36 +196,33 @@ impl JavaClient { let (block, state) = world.get_block_and_state(&location); let block_drop = player.gamemode.load() != GameMode::Creative - && player.can_harvest(state, block).await; + && player.can_harvest(state, block); - let new_state = world - .break_block( - &location, - Some(player.clone()), - if block_drop { - BlockFlags::NOTIFY_NEIGHBORS - } else { - BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS - }, - ) - .await; + let new_state = world.break_block( + &location, + Some(player.clone()), + if block_drop { + BlockFlags::NOTIFY_NEIGHBORS + } else { + BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS + }, + ); if new_state.is_some() { server .block_registry - .broken(&world, block, player, &location, server, state) - .await; + .broken(&world, block, player, &location, server, state); player.apply_tool_damage_for_block_break(state).await; if block_drop { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } - let item_id = player.inventory().held_item().await.item.id; - player - .increment_stat(StatisticCategory::Used, item_id as i32, 1) - .await; - player - .increment_stat(StatisticCategory::Mined, state.id.as_u16() as i32, 1) - .await; + let item_id = player.inventory().held_item().item.id; + player.increment_stat(StatisticCategory::Used, item_id as i32, 1); + player.increment_stat( + StatisticCategory::Mined, + state.id.as_u16() as i32, + 1, + ); } self.sync_block_state_to_client(&world, location).await; @@ -247,12 +236,17 @@ impl JavaClient { player.drop_held_item(true).await; } Status::ReleaseItemInUse => { - let item_in_use = player.living_entity.item_in_use.lock().await.clone(); + let item_in_use = player + .living_entity + .item_in_use + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(stack) = item_in_use { server.item_registry.on_stopped_using(&stack, player).await; } - player.living_entity.clear_active_hand().await; + player.living_entity.clear_active_hand(); } Status::SwapItem => { player.swap_item().await; diff --git a/crates/pumpkin/src/net/java/play/player_command.rs b/crates/pumpkin/src/net/java/play/player_command.rs index 85d1d0a6d..a5cf0e7af 100644 --- a/crates/pumpkin/src/net/java/play/player_command.rs +++ b/crates/pumpkin/src/net/java/play/player_command.rs @@ -40,7 +40,7 @@ impl JavaClient { }} } } - Action::LeaveBed => player.wake_up().await, + Action::LeaveBed => player.wake_up(), Action::StartHorseJump | Action::StopHorseJump | Action::OpenVehicleInventory => { debug!("todo"); diff --git a/crates/pumpkin/src/net/java/play/player_position.rs b/crates/pumpkin/src/net/java/play/player_position.rs index 04f59bc7f..31639b31a 100644 --- a/crates/pumpkin/src/net/java/play/player_position.rs +++ b/crates/pumpkin/src/net/java/play/player_position.rs @@ -52,7 +52,7 @@ impl JavaClient { if !player.has_client_loaded() { return; } - if player.get_entity().has_vehicle().await { + if player.get_entity().has_vehicle() { return; } // Ignore movement packets while awaiting a teleport confirmation (vanilla behavior) @@ -95,9 +95,7 @@ impl JavaClient { let cm = (distance * 100.0) as i32; if cm > 0 { let stat = player.get_movement_statistic().await; - player - .increment_stat(StatisticCategory::Custom, stat as i32, cm) - .await; + player.increment_stat(StatisticCategory::Custom, stat as i32, cm); } let height_difference = pos.y - last_pos.y; @@ -115,7 +113,7 @@ impl JavaClient { // TODO: Warn when player moves to quickly if !Self::sync_position(player, world, pos, last_pos, entity.yaw.load(), entity.pitch.load(), packet.collision & FLAG_ON_GROUND != 0) { // Send the new position to all other players. - world.broadcast_packet_except_editioned_sync( + world.broadcast_packet_except_editioned( &[player.gameprofile.id], &CUpdateEntityPos::new( player.entity_id().into(), @@ -147,14 +145,12 @@ impl JavaClient { && player.living_entity.health.load() > 0.0 && !player.living_entity.dead.load(Ordering::Relaxed) { - player.living_entity - .fall( - player.clone(), - height_difference, - packet.collision & FLAG_ON_GROUND != 0, - player.gamemode.load() == GameMode::Creative, - ) - .await; + player.living_entity.fall( + player.as_ref(), + height_difference, + packet.collision & FLAG_ON_GROUND != 0, + player.gamemode.load() == GameMode::Creative, + ); } chunker::update_position(player).await; let delta = Vector3::new( @@ -185,7 +181,7 @@ impl JavaClient { if !player.has_client_loaded() { return; } - if player.get_entity().has_vehicle().await { + if player.get_entity().has_vehicle() { return; } // Ignore movement packets while awaiting a teleport confirmation (vanilla behavior) @@ -233,9 +229,7 @@ impl JavaClient { let cm = (distance * 100.0) as i32; if cm > 0 { let stat = player.get_movement_statistic().await; - player - .increment_stat(StatisticCategory::Custom, stat as i32, cm) - .await; + player.increment_stat(StatisticCategory::Custom, stat as i32, cm); } let height_difference = pos.y - last_pos.y; @@ -263,7 +257,7 @@ impl JavaClient { sync_position(player, &world, pos, last_pos, yaw, pitch, (packet.collision & FLAG_ON_GROUND) != 0) { // Send the new position to all other players. - world.broadcast_packet_except_editioned_sync( + world.broadcast_packet_except_editioned( &[player.gameprofile.id], &CUpdateEntityPosRot::new( entity_id.into(), @@ -303,14 +297,12 @@ impl JavaClient { && player.living_entity.health.load() > 0.0 && !player.living_entity.dead.load(Ordering::Relaxed) { - player.living_entity - .fall( - player.clone(), - height_difference, - (packet.collision & FLAG_ON_GROUND) != 0, - player.gamemode.load() == GameMode::Creative, - ) - .await; + player.living_entity.fall( + player.as_ref(), + height_difference, + (packet.collision & FLAG_ON_GROUND) != 0, + player.gamemode.load() == GameMode::Creative, + ); } chunker::update_position(player).await; let delta = Vector3::new( diff --git a/crates/pumpkin/src/net/java/play/player_rotation.rs b/crates/pumpkin/src/net/java/play/player_rotation.rs index 342e42020..94b2fd423 100644 --- a/crates/pumpkin/src/net/java/play/player_rotation.rs +++ b/crates/pumpkin/src/net/java/play/player_rotation.rs @@ -51,11 +51,7 @@ impl JavaClient { VarULong(0), ); - world.broadcast_packet_except_editioned_sync( - &[player.gameprofile.id], - &je_packet, - &be_packet, - ); + world.broadcast_packet_except_editioned(&[player.gameprofile.id], &je_packet, &be_packet); let je_packet = CHeadRot::new(entity_id.into(), yaw as u8); world.broadcast_packet_except(&[player.gameprofile.id], &je_packet); diff --git a/crates/pumpkin/src/net/java/play/set_command_block.rs b/crates/pumpkin/src/net/java/play/set_command_block.rs index 2e5328339..c94ef8f6e 100644 --- a/crates/pumpkin/src/net/java/play/set_command_block.rs +++ b/crates/pumpkin/src/net/java/play/set_command_block.rs @@ -46,14 +46,11 @@ impl JavaClient { props.conditional = command.is_conditional(); let new_state_id = props.to_state_id(&block_type); - player - .world() - .set_block_state( - &command.pos, - new_state_id, - BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; + player.world().set_block_state( + &command.pos, + new_state_id, + BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ); let mut cmd = command.command; if cmd.starts_with('/') { @@ -69,8 +66,14 @@ impl JavaClient { .into(), auto: command.is_automatic().into(), dirty: old_command_block.dirty.load(Ordering::SeqCst).into(), - command: Mutex::new(cmd.to_string()), - last_output: old_command_block.last_output.lock().await.clone().into(), + command: std::sync::Mutex::new(cmd.to_string()), + last_output: std::sync::Mutex::new( + old_command_block + .last_output + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), track_output: command.track_output().into(), success_count: AtomicU32::new(0), }; diff --git a/crates/pumpkin/src/net/java/play/set_creative_slot.rs b/crates/pumpkin/src/net/java/play/set_creative_slot.rs index 88e9a4f63..f76a4bcba 100644 --- a/crates/pumpkin/src/net/java/play/set_creative_slot.rs +++ b/crates/pumpkin/src/net/java/play/set_creative_slot.rs @@ -78,7 +78,7 @@ impl JavaClient { drop(player_screen_handler); } else if is_negative && is_legal { // Item drop - player.drop_item(item_stack).await; + player.drop_item(item_stack); } Ok(()) } diff --git a/crates/pumpkin/src/net/java/play/set_held_item.rs b/crates/pumpkin/src/net/java/play/set_held_item.rs index c4637f469..3def8d707 100644 --- a/crates/pumpkin/src/net/java/play/set_held_item.rs +++ b/crates/pumpkin/src/net/java/play/set_held_item.rs @@ -31,7 +31,7 @@ impl JavaClient { let inv = player.inventory(); inv.set_selected_slot(slot); - let stack = inv.held_item().await; + let stack = inv.held_item(); let equipment = &[(EquipmentSlot::MAIN_HAND, stack)]; player.living_entity.send_equipment_changes(equipment); } diff --git a/crates/pumpkin/src/net/java/play/swing_arm.rs b/crates/pumpkin/src/net/java/play/swing_arm.rs index 5f2aabf00..0316b1079 100644 --- a/crates/pumpkin/src/net/java/play/swing_arm.rs +++ b/crates/pumpkin/src/net/java/play/swing_arm.rs @@ -27,19 +27,16 @@ impl JavaClient { } let (yaw, pitch) = player.rotation(); - let hit_result = player - .world() - .raycast( - player.eye_position(), - player - .eye_position() - .add(&(Vector3::rotation_vector(f64::from(pitch), f64::from(yaw)) * 4.5)), - async |pos, world| { - let block = world.get_block(pos); - block != &Block::AIR && block != &Block::WATER && block != &Block::LAVA - }, - ) - .await; + let hit_result = player.world().raycast( + player.eye_position(), + player + .eye_position() + .add(&(Vector3::rotation_vector(f64::from(pitch), f64::from(yaw)) * 4.5)), + |pos, world| { + let block = world.get_block(pos); + block != &Block::AIR && block != &Block::WATER && block != &Block::LAVA + }, + ); let event = if let Some((hit_pos, _hit_dir)) = hit_result { PlayerInteractEvent::new( @@ -56,7 +53,7 @@ impl JavaClient { &server; event; 'after: { - player.swing_hand(hand, false).await; + player.swing_hand(hand, false); } }} } diff --git a/crates/pumpkin/src/net/java/play/update_sign.rs b/crates/pumpkin/src/net/java/play/update_sign.rs index 7045eb28d..3a7507611 100644 --- a/crates/pumpkin/src/net/java/play/update_sign.rs +++ b/crates/pumpkin/src/net/java/play/update_sign.rs @@ -50,7 +50,10 @@ impl JavaClient { sign_data.line_3.into(), sign_data.line_4.into(), ]; - *sign_entity.currently_editing_player.lock().await = None; + *sign_entity + .currently_editing_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; world.update_block_entity(&block_entity); } } diff --git a/crates/pumpkin/src/net/java/play/use_item.rs b/crates/pumpkin/src/net/java/play/use_item.rs index 5a5b424c3..7509a4d31 100644 --- a/crates/pumpkin/src/net/java/play/use_item.rs +++ b/crates/pumpkin/src/net/java/play/use_item.rs @@ -20,7 +20,7 @@ impl JavaClient { }; self.update_sequence(player, use_item.sequence.0); - let mut item_in_hand = inventory.get_stack_in_hand(hand).await; + let mut item_in_hand = inventory.get_stack_in_hand(hand); let mut consume_event = crate::plugin::api::events::player::player_item_consume::PlayerItemConsumeEvent::new( @@ -33,24 +33,19 @@ impl JavaClient { } let (item_id, _item) = (item_in_hand.item.id, item_in_hand.item); - player - .increment_stat(StatisticCategory::Used, item_id as i32, 1) - .await; + player.increment_stat(StatisticCategory::Used, item_id as i32, 1); - let hit_result = player - .world() - .raycast( - player.eye_position(), - player.eye_position().add( - &(Vector3::rotation_vector(f64::from(use_item.pitch), f64::from(use_item.yaw)) - * 4.5), - ), - async |pos, world| { - let block = world.get_block(pos); - block != &Block::AIR && block != &Block::WATER && block != &Block::LAVA - }, - ) - .await; + let hit_result = player.world().raycast( + player.eye_position(), + player.eye_position().add( + &(Vector3::rotation_vector(f64::from(use_item.pitch), f64::from(use_item.yaw)) + * 4.5), + ), + |pos, world| { + let block = world.get_block(pos); + block != &Block::AIR && block != &Block::WATER && block != &Block::LAVA + }, + ); let event = if let Some((hit_pos, _hit_dir)) = hit_result { PlayerInteractEvent::new( @@ -109,16 +104,16 @@ impl JavaClient { || food.can_always_eat || player.hunger_manager.level.load() < 20 { - player - .living_entity - .set_active_hand(hand, held.clone(), held.get_max_use_time()) - .await; + player.living_entity.set_active_hand( + hand, + held.clone(), + held.get_max_use_time(), + ); } } else { player .living_entity - .set_active_hand(hand, held.clone(), held.get_max_use_time()) - .await; + .set_active_hand(hand, held.clone(), held.get_max_use_time()); } } let equipment_slot = held @@ -127,7 +122,11 @@ impl JavaClient { if let Some(slot) = equipment_slot { // The equipment lock has to be released before touching the hand again: // the off hand lives in the same map, so holding it here would deadlock. - let current_equipped = inventory.entity_equipment.lock().await.get(&slot); + let current_equipped = inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(&slot); if current_equipped.are_items_and_components_equal(held) { return; } @@ -141,8 +140,12 @@ impl JavaClient { } else { std::mem::replace(held, current_equipped) }; - inventory.entity_equipment.lock().await.put(&slot, equipped); - inventory.set_stack_in_hand(hand, held.clone()).await; + inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(&slot, equipped); + inventory.set_stack_in_hand(hand, held.clone()); } } diff --git a/crates/pumpkin/src/net/java/play/use_item_on.rs b/crates/pumpkin/src/net/java/play/use_item_on.rs index 00952241d..71cf67afd 100644 --- a/crates/pumpkin/src/net/java/play/use_item_on.rs +++ b/crates/pumpkin/src/net/java/play/use_item_on.rs @@ -39,16 +39,14 @@ impl JavaClient { } let inventory = player.inventory(); - let held_item = inventory.held_item().await; - let off_hand_item = inventory.off_hand_item().await; + let held_item = inventory.held_item(); + let off_hand_item = inventory.off_hand_item(); let held_item_empty = held_item.is_empty(); let off_hand_item_empty = off_hand_item.is_empty(); - let mut item = inventory.get_stack_in_hand(hand).await; + let mut item = inventory.get_stack_in_hand(hand); let item_id = item.item.id; - player - .increment_stat(StatisticCategory::Used, item_id as i32, 1) - .await; + player.increment_stat(StatisticCategory::Used, item_id as i32, 1); let entity = &player.get_entity(); let world = entity.world.load_full(); @@ -85,24 +83,22 @@ impl JavaClient { // Code based on the java class ServerPlayerInteractionManager if !(sneaking && (!held_item_empty || !off_hand_item_empty)) { - let result = self - .call_use_item_on( - player, - &position, - &cursor_pos, - &face, - &mut item, - &equipment_slot, - &world, - block, - server, - ) - .await; + let result = Self::call_use_item_on( + player, + &position, + &cursor_pos, + face, + &mut item, + &equipment_slot, + &world, + block, + server, + ); if result.consumes_action() { // TODO: Trigger ANY_BLOCK_USE Criteria if matches!(result, BlockActionResult::SuccessServer) { - player.swing_hand(hand, true).await; + player.swing_hand(hand, true); } return Ok(()); } @@ -162,38 +158,37 @@ impl JavaClient { if !after.are_equal(&before) { player.sync_hand_slot(slot_index, after.clone()).await; - inventory.set_stack_in_hand(hand, after).await; + inventory.set_stack_in_hand(hand, after); } Ok(()) } #[expect(clippy::too_many_arguments)] - async fn call_use_item_on( - &self, + fn call_use_item_on( player: &Arc, position: &BlockPos, cursor_pos: &Vector3, - face: &BlockDirection, + face: BlockDirection, held_item: &mut ItemStack, equipment_slot: &EquipmentSlot, world: &Arc, block: &Block, server: &Arc, ) -> BlockActionResult { - let result = server - .block_registry - .use_with_item( - block, - player, - position, - &BlockHitResult { face, cursor_pos }, - held_item, - equipment_slot, - server, - world, - ) - .await; + let result = server.block_registry.use_with_item( + block, + player, + position, + &BlockHitResult { + face: &face, + cursor_pos, + }, + held_item, + equipment_slot, + server, + world, + ); if result.consumes_action() { // TODO: Trigger ITEM_USED_ON_BLOCK Criteria @@ -201,17 +196,17 @@ impl JavaClient { } if matches!(result, BlockActionResult::PassToDefaultBlockAction) { - let result = server - .block_registry - .on_use( - block, - player, - position, - &BlockHitResult { face, cursor_pos }, - server, - world, - ) - .await; + let result = server.block_registry.on_use( + block, + player, + position, + &BlockHitResult { + face: &face, + cursor_pos, + }, + server, + world, + ); if result.consumes_action() { // TODO: Trigger DEFAULT_BLOCK_USE Criteria diff --git a/crates/pumpkin/src/net/java/recipe_helper.rs b/crates/pumpkin/src/net/java/recipe_helper.rs index a143ce913..acd5d82a5 100644 --- a/crates/pumpkin/src/net/java/recipe_helper.rs +++ b/crates/pumpkin/src/net/java/recipe_helper.rs @@ -28,7 +28,10 @@ pub async fn take_n_ingredient( let mut taken = 0u8; let mut result: Option = None; - let mut main_inventory = inventory.main_inventory.write().await; + let mut main_inventory = inventory + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); for stack in main_inventory.iter_mut() { if !stack.is_empty() && ingredient.match_item(stack.item) { let to_take = (count - taken).min(stack.item_count); @@ -53,7 +56,10 @@ pub async fn compute_biggest_craftable( inventory: &PlayerInventory, ) -> u8 { let mut available: Vec<(&'static Item, u32)> = Vec::new(); - let main_inventory = inventory.main_inventory.read().await; + let main_inventory = inventory + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); for stack in main_inventory.iter() { if !stack.is_empty() { if let Some(e) = available.iter_mut().find(|(i, _)| i.id == stack.item.id) { diff --git a/crates/pumpkin/src/plugin/api/gui.rs b/crates/pumpkin/src/plugin/api/gui.rs index f53bafd5c..cc25a3892 100644 --- a/crates/pumpkin/src/plugin/api/gui.rs +++ b/crates/pumpkin/src/plugin/api/gui.rs @@ -42,7 +42,7 @@ impl Clearable for PluginInventory { impl Inventory for PluginInventory { fn size(&self) -> usize { - futures::executor::block_on(self.slots.read()).len() + self.slots.blocking_read().len() } fn is_empty(&self) -> InventoryFuture<'_, bool> { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs index a084d1769..f4a4404b1 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs @@ -433,11 +433,19 @@ impl HostCommandBlockEntity for PluginHostState { async fn last_output(&mut self, res: Resource) -> wasmtime::Result { let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; - if let Some(cmd) = entity.as_any().downcast_ref::() { - Ok(cmd.last_output.lock().await.clone()) - } else { - Err(wasmtime::Error::msg("Not a command block entity")) - } + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a command block entity")), + |cmd| { + Ok(cmd + .last_output + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone()) + }, + ) } async fn track_output(&mut self, res: Resource) -> wasmtime::Result { @@ -464,11 +472,19 @@ impl HostCommandBlockEntity for PluginHostState { async fn command(&mut self, res: Resource) -> wasmtime::Result { let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; - if let Some(cmd) = entity.as_any().downcast_ref::() { - Ok(cmd.command.lock().await.clone()) - } else { - Err(wasmtime::Error::msg("Not a command block entity")) - } + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a command block entity")), + |cmd| { + Ok(cmd + .command + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone()) + }, + ) } async fn auto(&mut self, res: Resource) -> wasmtime::Result { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs index eecf033cf..90063c8e7 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs @@ -174,9 +174,7 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player - .update_bossbar_title(&pbb.bossbar.uuid, title.clone()) - .await; + player.update_bossbar_title(&pbb.bossbar.uuid, title.clone()); } } } @@ -194,9 +192,7 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player - .update_bossbar_health(&pbb.bossbar.uuid, health) - .await; + player.update_bossbar_health(&pbb.bossbar.uuid, health); } } } @@ -214,14 +210,12 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player - .update_bossbar_style( - &pbb.bossbar.uuid, - pbb.bossbar.color, - pbb.bossbar.division, - pbb.bossbar.flags, - ) - .await; + player.update_bossbar_style( + &pbb.bossbar.uuid, + pbb.bossbar.color, + pbb.bossbar.division, + pbb.bossbar.flags, + ); } } } @@ -243,14 +237,12 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player - .update_bossbar_style( - &pbb.bossbar.uuid, - pbb.bossbar.color, - pbb.bossbar.division, - pbb.bossbar.flags, - ) - .await; + player.update_bossbar_style( + &pbb.bossbar.uuid, + pbb.bossbar.color, + pbb.bossbar.division, + pbb.bossbar.flags, + ); } } } @@ -272,9 +264,7 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player - .update_bossbar_flags(&pbb.bossbar.uuid, pbb.bossbar.flags) - .await; + player.update_bossbar_flags(&pbb.bossbar.uuid, pbb.bossbar.flags); } } } @@ -323,7 +313,7 @@ impl boss_bar::HostBossBar for PluginHostState { if !pbb.players.contains(&uuid) { pbb.players.push(uuid); - player.send_bossbar(&pbb.bossbar).await; + player.send_bossbar(&pbb.bossbar); } Ok(()) } @@ -341,7 +331,7 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(idx) = pbb.players.iter().position(|&x| x == uuid) { pbb.players.remove(idx); - player.remove_bossbar(pbb.bossbar.uuid).await; + player.remove_bossbar(pbb.bossbar.uuid); } Ok(()) } @@ -351,7 +341,7 @@ impl boss_bar::HostBossBar for PluginHostState { if let Some(server) = pbb.server.upgrade() { for uuid in &pbb.players { if let Some(player) = server.get_player_by_uuid(*uuid) { - player.remove_bossbar(pbb.bossbar.uuid).await; + player.remove_bossbar(pbb.bossbar.uuid); } } } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs index 9d07fff2d..6a89c563c 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs @@ -137,62 +137,65 @@ impl HostDisplayEntity for PluginHostState { display: Resource, ) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - let translation = d.get_translation().await; - let scale = d.get_scale().await; - let left_rot = d.get_left_rotation().await; - let right_rot = d.get_right_rotation().await; + get_display_entity(display_res.provider.as_ref()).map_or_else( + || { + Ok(DisplayTransformation { + translation: Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }, + scale: Vector3f { + x: 1.0, + y: 1.0, + z: 1.0, + }, + left_rotation: Quaternionf { + x: 0.0, + y: 0.0, + z: 0.0, + w: 1.0, + }, + right_rotation: Quaternionf { + x: 0.0, + y: 0.0, + z: 0.0, + w: 1.0, + }, + }) + }, + |d| { + let translation = d.get_translation(); + let scale = d.get_scale(); + let left_rot = d.get_left_rotation(); + let right_rot = d.get_right_rotation(); - Ok(DisplayTransformation { - translation: Vector3f { - x: translation.x, - y: translation.y, - z: translation.z, - }, - scale: Vector3f { - x: scale.x, - y: scale.y, - z: scale.z, - }, - left_rotation: Quaternionf { - x: left_rot[0], - y: left_rot[1], - z: left_rot[2], - w: left_rot[3], - }, - right_rotation: Quaternionf { - x: right_rot[0], - y: right_rot[1], - z: right_rot[2], - w: right_rot[3], - }, - }) - } else { - Ok(DisplayTransformation { - translation: Vector3f { - x: 0.0, - y: 0.0, - z: 0.0, - }, - scale: Vector3f { - x: 1.0, - y: 1.0, - z: 1.0, - }, - left_rotation: Quaternionf { - x: 0.0, - y: 0.0, - z: 0.0, - w: 1.0, - }, - right_rotation: Quaternionf { - x: 0.0, - y: 0.0, - z: 0.0, - w: 1.0, - }, - }) - } + Ok(DisplayTransformation { + translation: Vector3f { + x: translation.x, + y: translation.y, + z: translation.z, + }, + scale: Vector3f { + x: scale.x, + y: scale.y, + z: scale.z, + }, + left_rotation: Quaternionf { + x: left_rot[0], + y: left_rot[1], + z: left_rot[2], + w: left_rot[3], + }, + right_rotation: Quaternionf { + x: right_rot[0], + y: right_rot[1], + z: right_rot[2], + w: right_rot[3], + }, + }) + }, + ) } async fn set_transformation( @@ -206,28 +209,24 @@ impl HostDisplayEntity for PluginHostState { transformation.translation.x, transformation.translation.y, transformation.translation.z, - )) - .await; + )); d.set_scale(Vector3::new( transformation.scale.x, transformation.scale.y, transformation.scale.z, - )) - .await; + )); d.set_left_rotation([ transformation.left_rotation.x, transformation.left_rotation.y, transformation.left_rotation.z, transformation.left_rotation.w, - ]) - .await; + ]); d.set_right_rotation([ transformation.right_rotation.x, transformation.right_rotation.y, transformation.right_rotation.z, transformation.right_rotation.w, - ]) - .await; + ]); } Ok(()) } @@ -327,11 +326,8 @@ impl HostDisplayEntity for PluginHostState { async fn get_view_range(&mut self, display: Resource) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - Ok(d.get_view_range().await) - } else { - Ok(1.0) - } + get_display_entity(display_res.provider.as_ref()) + .map_or_else(|| Ok(1.0), |d| Ok(d.get_view_range())) } async fn set_view_range( @@ -341,7 +337,7 @@ impl HostDisplayEntity for PluginHostState { ) -> wasmtime::Result<()> { let display_res = self.get_display_entity_res(&display)?; if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - d.set_view_range(range).await; + d.set_view_range(range); } Ok(()) } @@ -351,11 +347,8 @@ impl HostDisplayEntity for PluginHostState { display: Resource, ) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - Ok(d.get_shadow_radius().await) - } else { - Ok(0.0) - } + get_display_entity(display_res.provider.as_ref()) + .map_or_else(|| Ok(0.0), |d| Ok(d.get_shadow_radius())) } async fn set_shadow_radius( @@ -365,7 +358,7 @@ impl HostDisplayEntity for PluginHostState { ) -> wasmtime::Result<()> { let display_res = self.get_display_entity_res(&display)?; if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - d.set_shadow_radius(radius).await; + d.set_shadow_radius(radius); } Ok(()) } @@ -375,11 +368,8 @@ impl HostDisplayEntity for PluginHostState { display: Resource, ) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - Ok(d.get_shadow_strength().await) - } else { - Ok(1.0) - } + get_display_entity(display_res.provider.as_ref()) + .map_or_else(|| Ok(1.0), |d| Ok(d.get_shadow_strength())) } async fn set_shadow_strength( @@ -389,7 +379,7 @@ impl HostDisplayEntity for PluginHostState { ) -> wasmtime::Result<()> { let display_res = self.get_display_entity_res(&display)?; if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - d.set_shadow_strength(strength).await; + d.set_shadow_strength(strength); } Ok(()) } @@ -399,11 +389,8 @@ impl HostDisplayEntity for PluginHostState { display: Resource, ) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - Ok(d.get_display_width().await) - } else { - Ok(0.0) - } + get_display_entity(display_res.provider.as_ref()) + .map_or_else(|| Ok(0.0), |d| Ok(d.get_display_width())) } async fn set_display_width( @@ -413,7 +400,7 @@ impl HostDisplayEntity for PluginHostState { ) -> wasmtime::Result<()> { let display_res = self.get_display_entity_res(&display)?; if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - d.set_display_width(width).await; + d.set_display_width(width); } Ok(()) } @@ -423,11 +410,8 @@ impl HostDisplayEntity for PluginHostState { display: Resource, ) -> wasmtime::Result { let display_res = self.get_display_entity_res(&display)?; - if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - Ok(d.get_display_height().await) - } else { - Ok(0.0) - } + get_display_entity(display_res.provider.as_ref()) + .map_or_else(|| Ok(0.0), |d| Ok(d.get_display_height())) } async fn set_display_height( @@ -437,7 +421,7 @@ impl HostDisplayEntity for PluginHostState { ) -> wasmtime::Result<()> { let display_res = self.get_display_entity_res(&display)?; if let Some(d) = get_display_entity(display_res.provider.as_ref()) { - d.set_display_height(height).await; + d.set_display_height(height); } Ok(()) } @@ -611,7 +595,7 @@ impl HostItemDisplayEntity for PluginHostState { .cast_any() .downcast_ref::() { - let item = i.get_item().await; + let item = i.get_item(); if *item.item == pumpkin_data::item::Item::AIR || item.item_count == 0 { Ok(None) } else { @@ -639,7 +623,7 @@ impl HostItemDisplayEntity for PluginHostState { } else { pumpkin_data::item_stack::ItemStack::new(0, &pumpkin_data::item::Item::AIR) }; - i.set_item(stack).await; + i.set_item(stack); } Ok(()) } @@ -728,7 +712,7 @@ impl HostTextDisplayEntity for PluginHostState { .cast_any() .downcast_ref::() { - let text = t.get_text().await; + let text = t.get_text(); self.add_text_component(text) } else { self.add_text_component(pumpkin_util::text::TextComponent::text("")) @@ -747,7 +731,7 @@ impl HostTextDisplayEntity for PluginHostState { .cast_any() .downcast_ref::() { - t.set_text(text_val).await; + t.set_text(text_val); } Ok(()) } @@ -994,15 +978,11 @@ impl HostInteractionEntity for PluginHostState { interaction: Resource, ) -> wasmtime::Result { let int_res = self.get_interaction_entity_res(&interaction)?; - if let Some(i) = int_res + int_res .provider .cast_any() .downcast_ref::() - { - Ok(i.get_width().await) - } else { - Ok(1.0) - } + .map_or_else(|| Ok(1.0), |i| Ok(i.get_width())) } async fn set_width( @@ -1016,7 +996,7 @@ impl HostInteractionEntity for PluginHostState { .cast_any() .downcast_ref::() { - i.set_width(width).await; + i.set_width(width); } Ok(()) } @@ -1026,15 +1006,11 @@ impl HostInteractionEntity for PluginHostState { interaction: Resource, ) -> wasmtime::Result { let int_res = self.get_interaction_entity_res(&interaction)?; - if let Some(i) = int_res + int_res .provider .cast_any() .downcast_ref::() - { - Ok(i.get_height().await) - } else { - Ok(1.0) - } + .map_or_else(|| Ok(1.0), |i| Ok(i.get_height())) } async fn set_height( @@ -1048,7 +1024,7 @@ impl HostInteractionEntity for PluginHostState { .cast_any() .downcast_ref::() { - i.set_height(height).await; + i.set_height(height); } Ok(()) } @@ -1086,16 +1062,17 @@ impl HostInteractionEntity for PluginHostState { interaction: Resource, ) -> wasmtime::Result> { let int_res = self.get_interaction_entity_res(&interaction)?; - if let Some(i) = int_res + int_res .provider .cast_any() .downcast_ref::() - { - let action = i.get_last_attacker().await; - Ok(action.map(|a| Uuid::to_wit(&a.player))) - } else { - Ok(None) - } + .map_or_else( + || Ok(None), + |i| { + let action = i.get_last_attacker(); + Ok(action.map(|a| Uuid::to_wit(&a.player))) + }, + ) } async fn get_last_interaction( @@ -1103,16 +1080,17 @@ impl HostInteractionEntity for PluginHostState { interaction: Resource, ) -> wasmtime::Result> { let int_res = self.get_interaction_entity_res(&interaction)?; - if let Some(i) = int_res + int_res .provider .cast_any() .downcast_ref::() - { - let action = i.get_target().await; - Ok(action.map(|a| Uuid::to_wit(&a.player))) - } else { - Ok(None) - } + .map_or_else( + || Ok(None), + |i| { + let action = i.get_target(); + Ok(action.map(|a| Uuid::to_wit(&a.player))) + }, + ) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs index 8a1522c95..548b33c09 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs @@ -3,7 +3,7 @@ use wasmtime::component::Resource; use pumpkin_util::math::vector3::Vector3; -use crate::entity::ai::goal::{Goal, GoalFuture}; +use crate::entity::ai::goal::Goal; use crate::entity::mob::Mob; use crate::plugin::loader::wasm::wasm_host::{PluginInstance, WasmPlugin}; use crate::plugin::loader::wasm::wasm_host::{ @@ -358,7 +358,7 @@ impl HostEntity for PluginHostState { swimming: bool, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().set_swimming(swimming).await; + entity.get_entity().set_swimming(swimming); Ok(()) } @@ -368,7 +368,7 @@ impl HostEntity for PluginHostState { invisible: bool, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().set_invisible(invisible).await; + entity.get_entity().set_invisible(invisible); Ok(()) } @@ -378,7 +378,7 @@ impl HostEntity for PluginHostState { glowing: bool, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().set_glowing(glowing).await; + entity.get_entity().set_glowing(glowing); Ok(()) } @@ -415,7 +415,7 @@ impl HostEntity for PluginHostState { on_fire: bool, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().set_on_fire(on_fire).await; + entity.get_entity().set_on_fire(on_fire); Ok(()) } @@ -555,9 +555,7 @@ impl HostEntity for PluginHostState { damage_type: WitDamageType, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity - .damage(&*entity, amount, from_wit_damage_type(damage_type)) - .await; + entity.damage(&*entity, amount, from_wit_damage_type(damage_type)); Ok(()) } @@ -629,8 +627,7 @@ impl HostEntity for PluginHostState { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); } Ok(()) } @@ -653,8 +650,7 @@ impl HostEntity for PluginHostState { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); } Ok(()) } @@ -672,8 +668,7 @@ impl HostEntity for PluginHostState { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); } Ok(()) } @@ -723,8 +718,7 @@ impl HostEntity for PluginHostState { crate::entity::attributes::send_attribute_updates_for_living( living, vec![attribute.clone()], - ) - .await; + ); } Ok(()) } @@ -732,7 +726,7 @@ impl HostEntity for PluginHostState { async fn reset_all_attributes(&mut self, entity: Resource) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; if let Some(living) = entity.get_living_entity() { - living.reset_effects_and_attributes().await; + living.reset_effects_and_attributes(); } Ok(()) } @@ -745,7 +739,10 @@ impl HostEntity for PluginHostState { let entity = entity_from_resource(self, &entity)?; if let Some(living) = entity.get_living_entity() { let slot = from_wit_equipment_slot(slot); - let equipment = living.entity_equipment.lock().await; + let equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let stack = equipment.get(&slot); if !stack.is_empty() { return Ok(Some( @@ -772,7 +769,10 @@ impl HostEntity for PluginHostState { }; { - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); equipment.put(&slot, item_stack.clone()); }; @@ -784,7 +784,10 @@ impl HostEntity for PluginHostState { async fn clear_equipment(&mut self, entity: Resource) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; if let Some(living) = entity.get_living_entity() { - let mut equipment = living.entity_equipment.lock().await; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let slots_to_clear: Vec<( pumpkin_data::data_component_impl::EquipmentSlot, pumpkin_data::item_stack::ItemStack, @@ -1086,7 +1089,7 @@ impl HostEntity for PluginHostState { visual_fire: bool, ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().set_on_fire(visual_fire).await; + entity.get_entity().set_on_fire(visual_fire); Ok(()) } @@ -1161,7 +1164,7 @@ impl HostEntity for PluginHostState { async fn remove(&mut self, entity: Resource) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; - entity.get_entity().remove().await; + entity.get_entity().remove(); Ok(()) } @@ -1225,7 +1228,7 @@ impl HostEntity for PluginHostState { async fn clear_ai_goals(&mut self, entity: Resource) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &entity)?; if let Some(mob) = entity.get_mob() { - mob.get_mob_entity().clear_ai_goals(mob).await; + mob.get_mob_entity().clear_ai_goals(mob); } Ok(()) } @@ -1261,7 +1264,7 @@ impl HostEntity for PluginHostState { None }; if let Some(mob) = entity.get_mob() { - mob.get_mob_entity().set_target(target_entity).await; + mob.get_mob_entity().set_target(target_entity); } Ok(()) } @@ -1272,7 +1275,7 @@ impl HostEntity for PluginHostState { ) -> wasmtime::Result>> { let entity = entity_from_resource(self, &entity)?; if let Some(mob) = entity.get_mob() - && let Some(target) = mob.get_mob_entity().get_target().await + && let Some(target) = mob.get_mob_entity().get_target() { return Ok(Some(self.add_entity(target)?)); } @@ -1434,118 +1437,22 @@ fn current_mob_entity(mob: &dyn Mob) -> Option(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let mut store = self.plugin.store.lock().await; - if let Some(entity_arc) = current_mob_entity(mob) { - match self.plugin.plugin_instance { - PluginInstance::V0_1(ref plugin) => { - let Some(server) = store.data_mut().server.clone() else { - return false; - }; - let Ok(server_res) = store.data_mut().add_server(server) else { - return false; - }; - let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else { - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(server_res.rep()), - ); - return false; - }; - let server_rep = server_res.rep(); - let entity_rep = entity_res.rep(); - let result = plugin - .call_handle_ai_goal_can_start( - &mut *store, - self.goal_id, - server_res, - entity_res, - ) - .await - .unwrap_or(false); - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(server_rep), - ); - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(entity_rep), - ); - result - } - } - } else { - false - } - }) + fn can_start(&mut self, _mob: &dyn Mob) -> bool { + false } - fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { - Box::pin(async { - let mut store = self.plugin.store.lock().await; - if let Some(entity_arc) = current_mob_entity(mob) { - match self.plugin.plugin_instance { - PluginInstance::V0_1(ref plugin) => { - let Some(server) = store.data_mut().server.clone() else { - return false; - }; - let Ok(server_res) = store.data_mut().add_server(server) else { - return false; - }; - let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else { - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(server_res.rep()), - ); - return false; - }; - let server_rep = server_res.rep(); - let entity_rep = entity_res.rep(); - let result = plugin - .call_handle_ai_goal_should_continue( - &mut *store, - self.goal_id, - server_res, - entity_res, - ) - .await - .unwrap_or(false); - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(server_rep), - ); - let _ = store - .data_mut() - .resource_table - .delete::( - wasmtime::component::Resource::new_own(entity_rep), - ); - result - } - } - } else { - false - } - }) + fn should_continue(&self, _mob: &dyn Mob) -> bool { + false } - fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mut store = self.plugin.store.lock().await; - if let Some(entity_arc) = current_mob_entity(mob) { - match self.plugin.plugin_instance { - PluginInstance::V0_1(ref plugin) => { + fn start(&mut self, mob: &dyn Mob) { + if let Some(entity_arc) = current_mob_entity(mob) { + let plugin = self.plugin.clone(); + let goal_id = self.goal_id; + tokio::spawn(async move { + let mut store = plugin.store.lock().await; + match plugin.plugin_instance { + PluginInstance::V0_1(ref plugin_inst) => { let Some(server) = store.data_mut().server.clone() else { return; }; @@ -1563,13 +1470,8 @@ impl Goal for CustomWasmGoal { }; let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); - let _ = plugin - .call_handle_ai_goal_start( - &mut *store, - self.goal_id, - server_res, - entity_res, - ) + let _ = plugin_inst + .call_handle_ai_goal_start(&mut *store, goal_id, server_res, entity_res) .await; let _ = store .data_mut() @@ -1585,16 +1487,18 @@ impl Goal for CustomWasmGoal { ); } } - } - }) + }); + } } - fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mut store = self.plugin.store.lock().await; - if let Some(entity_arc) = current_mob_entity(mob) { - match self.plugin.plugin_instance { - PluginInstance::V0_1(ref plugin) => { + fn tick(&mut self, mob: &dyn Mob) { + if let Some(entity_arc) = current_mob_entity(mob) { + let plugin = self.plugin.clone(); + let goal_id = self.goal_id; + tokio::spawn(async move { + let mut store = plugin.store.lock().await; + match plugin.plugin_instance { + PluginInstance::V0_1(ref plugin_inst) => { let Some(server) = store.data_mut().server.clone() else { return; }; @@ -1612,13 +1516,8 @@ impl Goal for CustomWasmGoal { }; let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); - let _ = plugin - .call_handle_ai_goal_tick( - &mut *store, - self.goal_id, - server_res, - entity_res, - ) + let _ = plugin_inst + .call_handle_ai_goal_tick(&mut *store, goal_id, server_res, entity_res) .await; let _ = store .data_mut() @@ -1634,16 +1533,18 @@ impl Goal for CustomWasmGoal { ); } } - } - }) + }); + } } - fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { - Box::pin(async { - let mut store = self.plugin.store.lock().await; - if let Some(entity_arc) = current_mob_entity(mob) { - match self.plugin.plugin_instance { - PluginInstance::V0_1(ref plugin) => { + fn stop(&mut self, mob: &dyn Mob) { + if let Some(entity_arc) = current_mob_entity(mob) { + let plugin = self.plugin.clone(); + let goal_id = self.goal_id; + tokio::spawn(async move { + let mut store = plugin.store.lock().await; + match plugin.plugin_instance { + PluginInstance::V0_1(ref plugin_inst) => { let Some(server) = store.data_mut().server.clone() else { return; }; @@ -1661,13 +1562,8 @@ impl Goal for CustomWasmGoal { }; let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); - let _ = plugin - .call_handle_ai_goal_stop( - &mut *store, - self.goal_id, - server_res, - entity_res, - ) + let _ = plugin_inst + .call_handle_ai_goal_stop(&mut *store, goal_id, server_res, entity_res) .await; let _ = store .data_mut() @@ -1683,7 +1579,7 @@ impl Goal for CustomWasmGoal { ); } } - } - }) + }); + } } } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs index 51e1f7eb6..ac544c05f 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs @@ -1245,7 +1245,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { ) -> wasmtime::Result>> { let player = player_from_resource(self, &player)?; let hand = from_wasm_hand(hand); - let stack = player.inventory().get_stack_in_hand(hand).await; + let stack = player.inventory().get_stack_in_hand(hand); if stack.is_empty() { Ok(None) } else { @@ -1485,7 +1485,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { show_icon: effect.show_icon, blend: false, }; - player.add_effect(effect_obj).await; + player.add_effect(effect_obj); } Ok(()) } @@ -1518,13 +1518,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; let effect_type = super::status_effect::from_wasm_status_effect_type(effect); - if let Some(status_effect) = + Ok( pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name()) - { - Ok(player.has_effect(status_effect).await) - } else { - Ok(false) - } + .is_some_and(|status_effect| player.has_effect(status_effect)), + ) } async fn get_effect( @@ -1536,7 +1533,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { let effect_type = super::status_effect::from_wasm_status_effect_type(effect); if let Some(status_effect) = pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name()) - && let Some(eff) = player.get_effect(status_effect).await + && let Some(eff) = player.get_effect(status_effect) { return Ok(super::status_effect::to_wasm_status_effect_instance(&eff)); } @@ -1548,7 +1545,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { player: Resource, ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; - let effects = player.get_active_effects().await; + let effects = player.get_active_effects(); let mut list = Vec::with_capacity(effects.len()); for eff in &effects { if let Some(instance) = super::status_effect::to_wasm_status_effect_instance(eff) { @@ -1560,7 +1557,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn heal(&mut self, player: Resource, amount: f32) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.heal(amount).await; + player.heal(amount); Ok(()) } @@ -1571,15 +1568,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { damage_type: WitDamageType, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .damage(&*player, amount, from_wit_damage_type(damage_type)) - .await; + player.damage(&*player, amount, from_wit_damage_type(damage_type)); Ok(()) } async fn kill(&mut self, player: Resource) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.kill().await; + player.kill(); Ok(()) } @@ -1603,9 +1598,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { value: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .set_stat(from_wit_statistic_category(category), stat_id, value) - .await; + player.set_stat(from_wit_statistic_category(category), stat_id, value); Ok(()) } @@ -1617,9 +1610,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { amount: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .increment_stat(from_wit_statistic_category(category), stat_id, amount) - .await; + player.increment_stat(from_wit_statistic_category(category), stat_id, amount); Ok(()) } @@ -1641,9 +1632,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { value: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .set_custom_stat(from_wit_custom_statistic(stat), value) - .await; + player.set_custom_stat(from_wit_custom_statistic(stat), value); Ok(()) } @@ -1654,9 +1643,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { amount: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .increment_custom_stat(from_wit_custom_statistic(stat), amount) - .await; + player.increment_custom_stat(from_wit_custom_statistic(stat), amount); Ok(()) } @@ -2025,12 +2012,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { >, > { let player = player_from_resource(self, &player)?; - let res = player - .get_target_block( - &player.living_entity.entity.world.load_full(), - f64::from(max_distance), - ) - .await; + let res = player.get_target_block( + &player.living_entity.entity.world.load_full(), + f64::from(max_distance), + ); Ok(res.map(|p| { let vec3 = pumpkin_util::math::vector3::Vector3::new( f64::from(p.0.x), @@ -2299,7 +2284,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn set_health(&mut self, player: Resource, health: f32) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_health(health).await; + player.set_health(health); Ok(()) } @@ -2314,7 +2299,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { max_health: f32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_max_health(max_health).await; + player.set_max_health(max_health); Ok(()) } @@ -2344,7 +2329,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { saturation: f32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_saturation(saturation).await; + player.set_saturation(saturation); Ok(()) } @@ -2359,7 +2344,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { exhaustion: f32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_exhaustion(exhaustion).await; + player.set_exhaustion(exhaustion); Ok(()) } @@ -2374,7 +2359,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { absorption: f32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_absorption(absorption).await; + player.set_absorption(absorption); Ok(()) } @@ -2457,7 +2442,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn is_flying(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player.is_flying().await) + Ok(player.is_flying()) } async fn set_flying(&mut self, player: Resource, flying: bool) -> wasmtime::Result<()> { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs index 33638c2ea..d9aa675ef 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs @@ -758,25 +758,23 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { world_ref .provider .clone() - .set_block_state(&internal_pos, state, internal_flags) - .await; + .set_block_state(&internal_pos, state, internal_flags); Ok(()) } async fn get_time_of_day(&mut self, world: Resource) -> wasmtime::Result { - Ok(self.get_world_res(&world)?.provider.get_time_of_day().await as u64) + Ok(self.get_world_res(&world)?.provider.get_time_of_day() as u64) } async fn set_time_of_day(&mut self, world: Resource, time: u64) -> wasmtime::Result<()> { self.get_world_res(&world)? .provider - .set_time_of_day(time as i64) - .await; + .set_time_of_day(time as i64); Ok(()) } async fn get_world_age(&mut self, world: Resource) -> wasmtime::Result { - Ok(self.get_world_res(&world)?.provider.get_world_age().await as u64) + Ok(self.get_world_res(&world)?.provider.get_world_age() as u64) } async fn get_dimension(&mut self, world: Resource) -> wasmtime::Result { @@ -814,7 +812,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { } async fn is_raining(&mut self, world: Resource) -> wasmtime::Result { - Ok(self.get_world_res(&world)?.provider.is_raining().await) + Ok(self.get_world_res(&world)?.provider.is_raining()) } async fn set_raining(&mut self, world: Resource, raining: bool) -> wasmtime::Result<()> { @@ -826,7 +824,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { } async fn is_thundering(&mut self, world: Resource) -> wasmtime::Result { - Ok(self.get_world_res(&world)?.provider.is_thundering().await) + Ok(self.get_world_res(&world)?.provider.is_thundering()) } async fn set_thundering( @@ -850,8 +848,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { let msg = self.get_text_provider(&message)?; self.get_world_res(&world)? .provider - .broadcast_system_message(&msg, overlay) - .await; + .broadcast_system_message(&msg, overlay); Ok(()) } @@ -1081,7 +1078,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { uuid::Uuid::new_v4(), ); - world_provider.spawn_entity(entity.clone()).await; + world_provider.spawn_entity(entity.clone()); self.add_entity(entity) } @@ -1129,11 +1126,9 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { let world_provider = self.get_world_res(&world)?.provider.clone(); let start_pos = super::events::from_wasm_position(start); let end_pos = super::events::from_wasm_position(end); - let res = world_provider - .raycast(start_pos, end_pos, async |pos, w| { - !w.get_block_state(pos).is_air() - }) - .await; + let res = world_provider.raycast(start_pos, end_pos, |pos, w| { + !w.get_block_state(pos).is_air() + }); Ok(res.map(|(p, _)| { super::events::to_wasm_position(pumpkin_util::math::vector3::Vector3::new( f64::from(p.0.x), @@ -1516,7 +1511,7 @@ impl pumpkin::plugin::world::HostChunk for PluginHostState { chunk_data.mark_dirty(true); let absolute_pos = BlockPos::new(chunk_data.x * 16 + pos.x, pos.y, chunk_data.z * 16 + pos.z); - world.register_block_change(absolute_pos, state).await; + world.register_block_change(absolute_pos, state); } Ok(()) diff --git a/crates/pumpkin/src/plugin/mod.rs b/crates/pumpkin/src/plugin/mod.rs index 175d64a52..c1d5d0642 100644 --- a/crates/pumpkin/src/plugin/mod.rs +++ b/crates/pumpkin/src/plugin/mod.rs @@ -1172,6 +1172,35 @@ impl PluginManager { } } + /// Fire an event to all registered handlers synchronously (blocking if handlers exist). + /// If no handlers are registered for this event, returns immediately without runtime overhead. + pub fn fire_blocking( + &self, + server: &Arc, + event: &mut E, + ) { + let handlers_map = self.handlers.load(); + if handlers_map.is_empty() { + return; + } + + let Some(handlers) = handlers_map.get(E::get_name_static()) else { + return; + }; + + if handlers.is_empty() { + return; + } + + if tokio::runtime::Handle::try_current().is_ok() { + tokio::task::block_in_place(|| { + server.runtime.block_on(self.fire(server, event)); + }); + } else { + server.runtime.block_on(self.fire(server, event)); + } + } + #[expect(clippy::result_unit_err)] pub async fn send_message( &self, diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs index 20cde6927..20ef3e069 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -41,6 +41,7 @@ use pumpkin_world::world_info::anvil::{ }; use pumpkin_world::world_info::{LevelData, WorldInfoError, WorldInfoReader, WorldInfoWriter}; use rand::seq::{IndexedRandom, SliceRandom}; +use rayon::prelude::*; use rsa::RsaPublicKey; use std::collections::HashSet; use std::fs; @@ -49,7 +50,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32}; use std::{future::Future, sync::atomic::Ordering, time::Duration}; use tokio::sync::{Mutex, OnceCell}; -use tokio::task::{JoinHandle, JoinSet}; +use tokio::task::JoinHandle; use tokio_util::task::TaskTracker; mod connection_cache; @@ -128,7 +129,7 @@ pub struct Server { /// Manages the server's tick rate, freezing, and sprinting pub tick_rate_manager: Arc, /// Stores the duration of the last 100 ticks for performance analysis - pub tick_times_nanos: Mutex<[i64; 100]>, + pub tick_times_nanos: std::sync::Mutex<[i64; 100]>, /// Aggregated tick times for efficient rolling average calculation pub aggregated_tick_times_nanos: AtomicI64, /// Total number of ticks processed by the server @@ -142,7 +143,7 @@ pub struct Server { /// Manages scheduled tasks (e.g. from plugins) pub task_scheduler: Arc, tasks: TaskTracker, - runtime: tokio::runtime::Handle, + pub runtime: tokio::runtime::Handle, // world stuff which maybe should be put into a struct pub level_info: Arc>, @@ -299,7 +300,7 @@ impl Server { advancement_manager, white_list, tick_rate_manager, - tick_times_nanos: Mutex::new([0; 100]), + tick_times_nanos: std::sync::Mutex::new([0; 100]), aggregated_tick_times_nanos: AtomicI64::new(0), tick_count: AtomicI32::new(0), debug_profiler: debug_profiler::DebugProfiler::new(), @@ -533,10 +534,7 @@ impl Server { }); let mut event = crate::plugin::api::events::world::world_init::WorldInitEvent::new(world.clone()); - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(server.plugin_manager.fire(&server, &mut event)); - }); + server.plugin_manager.fire_blocking(&server, &mut event); world }) .await @@ -754,13 +752,11 @@ impl Server { } pub async fn remove_player(&self, player: &Player) { - player - .increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::LeaveGame as i32, - 1, - ) - .await; + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::LeaveGame as i32, + 1, + ); // TODO: Config if we want decrease online self.listing.lock().await.remove_player(player); } @@ -828,9 +824,7 @@ impl Server { 'after: { for world in self.worlds.load().iter() { - world - .broadcast_message(&event.message, &event.sender, chat_type, target_name) - .await; + world.broadcast_message(&event.message, &event.sender, chat_type, target_name); } } }} @@ -856,7 +850,7 @@ impl Server { /// # Note /// /// This function does not handle the actual mob spawn options update, which is a TODO item for future implementation. - pub async fn set_difficulty(&self, difficulty: Difficulty, force_update: bool) { + pub fn set_difficulty(&self, difficulty: Difficulty, force_update: bool) { let current_info = self.level_info.load(); if current_info.difficulty_locked && !force_update { return; @@ -876,19 +870,17 @@ impl Server { for world in self.worlds.load().iter() { world.set_difficulty(difficulty); - world - .broadcast_editioned( - &CChangeDifficulty::new(difficulty as u8, locked), - &pumpkin_protocol::bedrock::client::CSetDifficulty { - difficulty: (difficulty as u32).into(), - }, - ) - .await; + world.broadcast_editioned( + &CChangeDifficulty::new(difficulty as u8, locked), + &pumpkin_protocol::bedrock::client::CSetDifficulty { + difficulty: (difficulty as u32).into(), + }, + ); } } /// Sets the difficulty lock status of the server and broadcasts the update to all players. - pub async fn set_difficulty_locked(&self, locked: bool) { + pub fn set_difficulty_locked(&self, locked: bool) { let current_info = self.level_info.load(); let mut new_info = (**current_info).clone(); new_info.difficulty_locked = locked; @@ -896,14 +888,12 @@ impl Server { self.level_info.store(Arc::new(new_info)); for world in self.worlds.load().iter() { - world - .broadcast_editioned( - &CChangeDifficulty::new(difficulty as u8, locked), - &pumpkin_protocol::bedrock::client::CSetDifficulty { - difficulty: (difficulty as u32).into(), - }, - ) - .await; + world.broadcast_editioned( + &CChangeDifficulty::new(difficulty as u8, locked), + &pumpkin_protocol::bedrock::client::CSetDifficulty { + difficulty: (difficulty as u32).into(), + }, + ); } } @@ -1072,67 +1062,69 @@ impl Server { /// Main server tick method. This now handles both player/network ticking (which always runs) /// and world/game logic ticking (which is affected by freeze state). - pub async fn tick(self: &Arc) { + pub fn tick(self: &Arc) { if self.tick_rate_manager.runs_normally() || self.tick_rate_manager.is_sprinting() { - self.tick_worlds().await; + self.tick_worlds(); // Always run player and network ticking, even when game is frozen } else { - self.tick_players_and_network().await; + self.tick_players_and_network(); } } /// Ticks essential server functions that must run even when the game is frozen. /// This includes player ticking (network, keep-alives) and flushing world updates to clients. - pub async fn tick_players_and_network(self: &Arc) { + pub fn tick_players_and_network(self: &Arc) { let worlds = self.worlds.load(); for world in worlds.iter() { - world.flush_block_updates().await; - world.flush_synced_block_events().await; + world.flush_block_updates(); + world.flush_synced_block_events(); } - let mut set = JoinSet::new(); + let mut all_players = Vec::new(); for world in worlds.iter() { let players = world.players.load(); - for player in players.iter() { - let player_clone = player.clone(); - let server_clone = self.clone(); - set.spawn(async move { - player_clone.tick(&server_clone).await; - }); - } + all_players.extend(players.iter().cloned()); } - set.join_all().await; + + all_players.par_iter().for_each(|player| { + player.tick(self); + }); } + /// Ticks the game logic for all worlds. This is the part that is affected by `/tick freeze`. - pub async fn tick_worlds(self: &Arc) { - self.task_scheduler.tick(self).await; + pub fn tick_worlds(self: &Arc) { + let server_clone1 = self.clone(); + self.runtime.spawn(async move { + server_clone1.task_scheduler.tick(&server_clone1).await; + }); - let mut set = JoinSet::new(); + let worlds = self.worlds.load(); + let handle = self.runtime.clone(); - for world in self.worlds.load().iter() { - let world = world.clone(); - let server = self.clone(); - - set.spawn(async move { - world.tick(server).await; - }); - } - - set.join_all().await; + worlds.par_iter().for_each(|world| { + let _guard = handle.enter(); + world.tick(self); + }); // Global tasks - if let Err(e) = self.player_data_storage.tick(self).await { - error!("Error ticking player data: {e}"); - } + let server_clone2 = self.clone(); + self.runtime.spawn(async move { + if let Err(e) = server_clone2.player_data_storage.tick(&server_clone2).await { + error!("Error ticking player data: {e}"); + } + }); } /// Updates the tick time statistics with the duration of the last tick. - pub async fn update_tick_times(&self, tick_duration_nanos: i64) { + pub fn update_tick_times(&self, tick_duration_nanos: i64) { let tick_count = self.tick_count.fetch_add(1, Ordering::Relaxed); let index = (tick_count % 100) as usize; - let mut tick_times = self.tick_times_nanos.lock().await; + let mut tick_times = self + .tick_times_nanos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let old_time = tick_times[index]; tick_times[index] = tick_duration_nanos; drop(tick_times); @@ -1193,8 +1185,11 @@ impl Server { } /// Returns a copy of the last 100 tick times. - pub async fn get_tick_times_nanos_copy(&self) -> [i64; 100] { - *self.tick_times_nanos.lock().await + pub fn get_tick_times_nanos_copy(&self) -> [i64; 100] { + *self + .tick_times_nanos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) } #[allow(clippy::too_many_lines, clippy::option_if_let_else)] diff --git a/crates/pumpkin/src/server/ticker.rs b/crates/pumpkin/src/server/ticker.rs index 8ac09e852..4214a26d7 100644 --- a/crates/pumpkin/src/server/ticker.rs +++ b/crates/pumpkin/src/server/ticker.rs @@ -7,52 +7,49 @@ use crate::{ }; use std::sync::Arc; use std::sync::atomic::Ordering; -use std::time::Duration; -use tokio::time::{Instant, sleep_until}; +use std::time::{Duration, Instant}; use tracing::debug; pub struct Ticker; impl Ticker { - /// IMPORTANT: Run this in a new thread/tokio task. - pub async fn run(server: &Arc) { + /// Runs the main server tick loop on a dedicated thread. + pub fn run(server: &Arc) { let mut next_tick = Instant::now(); 'ticker: loop { - let tick_start_time = std::time::Instant::now(); + let tick_start_time = Instant::now(); let manager = &server.tick_rate_manager; manager.tick(); let tick_number = server.tick_count.load(Ordering::Relaxed); - server - .plugin_manager - .fire(server, &mut ServerTickStartEvent::new(tick_number)) - .await; + server.runtime.block_on( + server + .plugin_manager + .fire(server, &mut ServerTickStartEvent::new(tick_number)), + ); if manager.is_sprinting() { manager.start_sprint_tick_work(); - server.tick().await; + server.tick(); if manager.end_sprint_tick_work() { manager.finish_tick_sprint(server); } } else { - server.tick().await; + server.tick(); } let tick_duration_nanos = tick_start_time.elapsed().as_nanos() as i64; let tick_number = server.tick_count.load(Ordering::Relaxed); - server - .plugin_manager - .fire( - server, - &mut ServerTickEndEvent::new(tick_number, tick_duration_nanos), - ) - .await; + server.runtime.block_on(server.plugin_manager.fire( + server, + &mut ServerTickEndEvent::new(tick_number, tick_duration_nanos), + )); - server.update_tick_times(tick_duration_nanos).await; + server.update_tick_times(tick_duration_nanos); let tick_interval = if manager.is_sprinting() { Duration::ZERO @@ -62,12 +59,22 @@ impl Ticker { next_tick += tick_interval; - // Explicitly yield to tokio to allow pending network packets / I/O tasks to be processed - tokio::task::yield_now().await; + if STOP_INTERRUPT.is_cancelled() { + break 'ticker; + } - tokio::select! { - () = sleep_until(next_tick) => {}, - () = STOP_INTERRUPT.cancelled() => { + let now = Instant::now(); + if next_tick > now { + let sleep_duration = next_tick - now; + let cancelled = STOP_INTERRUPT.clone(); + server.runtime.block_on(async { + tokio::select! { + () = tokio::time::sleep(sleep_duration) => {}, + () = cancelled.cancelled() => {}, + } + }); + + if STOP_INTERRUPT.is_cancelled() { break 'ticker; } } diff --git a/crates/pumpkin/src/world/bossbar.rs b/crates/pumpkin/src/world/bossbar.rs index a8f468fcb..ae815f0fd 100644 --- a/crates/pumpkin/src/world/bossbar.rs +++ b/crates/pumpkin/src/world/bossbar.rs @@ -106,133 +106,93 @@ pub const fn bossbar_bedrock_id(uuid: &Uuid) -> VarLong { /// Extra methods for [`Player`] to send and manage the bossbar. impl Player { - pub async fn send_bossbar(&self, bossbar: &Bossbar) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::Add { - title: bossbar.title.clone(), - health: bossbar.health, - color: (bossbar.color as u8).into(), - division: (bossbar.division as u8).into(), - flags: bossbar.flags.bits(), - }; + pub fn send_bossbar(&self, bossbar: &Bossbar) { + let boss_action = BosseventAction::Add { + title: bossbar.title.clone(), + health: bossbar.health, + color: (bossbar.color as u8).into(), + division: (bossbar.division as u8).into(), + flags: bossbar.flags.bits(), + }; - let packet = CBossEvent::new(&bossbar.uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(&bossbar.uuid); - let player_id = VarLong(self.entity_id() as i64); - let packet = BBossEvent::show( - boss_id, - player_id, - bossbar.title.clone().get_text(), - bossbar.health, - bossbar.color.to_bedrock(), - bossbar.division.to_bedrock(), - ); - bedrock.send_packet(&packet).await; + let je_packet = CBossEvent::new(&bossbar.uuid, boss_action); + let boss_id = bossbar_bedrock_id(&bossbar.uuid); + let player_id = VarLong(self.entity_id() as i64); + let be_packet = BBossEvent::show( + boss_id, + player_id, + bossbar.title.clone().get_text(), + bossbar.health, + bossbar.color.to_bedrock(), + bossbar.division.to_bedrock(), + ); - let register_packet = BBossEvent::register_player(boss_id, player_id); - bedrock.send_packet(®ister_packet).await; + self.try_enqueue_packet_editioned(&je_packet, &be_packet); + if let ClientPlatform::Bedrock(bedrock) = self.client.as_ref() { + let register_packet = BBossEvent::register_player(boss_id, player_id); + if let Ok(data) = bedrock.serialize_packet(®ister_packet) { + bedrock.try_enqueue_packet(data); } } } - pub async fn remove_bossbar(&self, uuid: Uuid) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::Remove; + pub fn remove_bossbar(&self, uuid: Uuid) { + let boss_action = BosseventAction::Remove; + let je_packet = CBossEvent::new(&uuid, boss_action); + let boss_id = bossbar_bedrock_id(&uuid); + let player_id = VarLong(self.entity_id() as i64); + let unregister_packet = BBossEvent::unregister_player(boss_id, player_id); + let be_packet = BBossEvent::hide(boss_id); - let packet = CBossEvent::new(&uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(&uuid); - let player_id = VarLong(self.entity_id() as i64); - let unregister_packet = BBossEvent::unregister_player(boss_id, player_id); - bedrock.send_packet(&unregister_packet).await; - - let packet = BBossEvent::hide(boss_id); - bedrock.send_packet(&packet).await; - } + self.try_enqueue_packet_editioned(&je_packet, &be_packet); + if let ClientPlatform::Bedrock(bedrock) = self.client.as_ref() + && let Ok(data) = bedrock.serialize_packet(&unregister_packet) + { + bedrock.try_enqueue_packet(data); } } - pub async fn update_bossbar_health(&self, uuid: &Uuid, health: f32) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::UpdateHealth(health); - - let packet = CBossEvent::new(uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(uuid); - let packet = BBossEvent::update_health(boss_id, health); - bedrock.send_packet(&packet).await; - } - } + pub fn update_bossbar_health(&self, uuid: &Uuid, health: f32) { + let boss_action = BosseventAction::UpdateHealth(health); + let je_packet = CBossEvent::new(uuid, boss_action); + let boss_id = bossbar_bedrock_id(uuid); + let be_packet = BBossEvent::update_health(boss_id, health); + self.try_enqueue_packet_editioned(&je_packet, &be_packet); } - pub async fn update_bossbar_title(&self, uuid: &Uuid, title: TextComponent) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::UpdateTile(title); - - let packet = CBossEvent::new(uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(uuid); - let packet = BBossEvent::update_title(boss_id, title.get_text()); - bedrock.send_packet(&packet).await; - } - } + pub fn update_bossbar_title(&self, uuid: &Uuid, title: TextComponent) { + let text = title.clone().get_text(); + let boss_action = BosseventAction::UpdateTile(title); + let je_packet = CBossEvent::new(uuid, boss_action); + let boss_id = bossbar_bedrock_id(uuid); + let be_packet = BBossEvent::update_title(boss_id, text); + self.try_enqueue_packet_editioned(&je_packet, &be_packet); } - pub async fn update_bossbar_style( + pub fn update_bossbar_style( &self, uuid: &Uuid, color: BossbarColor, dividers: BossbarDivisions, _flags: BossbarFlags, ) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::UpdateStyle { - color: (color as u8).into(), - dividers: (dividers as u8).into(), - }; + let boss_action = BosseventAction::UpdateStyle { + color: (color as u8).into(), + dividers: (dividers as u8).into(), + }; - let packet = CBossEvent::new(uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(uuid); - let packet = BBossEvent::update_properties( - boss_id, - color.to_bedrock(), - dividers.to_bedrock(), - ); - bedrock.send_packet(&packet).await; - } - } + let je_packet = CBossEvent::new(uuid, boss_action); + let boss_id = bossbar_bedrock_id(uuid); + let be_packet = + BBossEvent::update_properties(boss_id, color.to_bedrock(), dividers.to_bedrock()); + self.try_enqueue_packet_editioned(&je_packet, &be_packet); } - pub async fn update_bossbar_flags(&self, uuid: &Uuid, flags: BossbarFlags) { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - let boss_action = BosseventAction::UpdateFlags(flags.bits()); - - let packet = CBossEvent::new(uuid, boss_action); - java.enqueue_client_packet(&packet).await; - } - ClientPlatform::Bedrock(bedrock) => { - let boss_id = bossbar_bedrock_id(uuid); - let packet = BBossEvent::update_properties(boss_id, 0, 0); - bedrock.send_packet(&packet).await; - } - } + pub fn update_bossbar_flags(&self, uuid: &Uuid, flags: BossbarFlags) { + let boss_action = BosseventAction::UpdateFlags(flags.bits()); + let je_packet = CBossEvent::new(uuid, boss_action); + let boss_id = bossbar_bedrock_id(uuid); + let be_packet = BBossEvent::update_properties(boss_id, 0, 0); + self.try_enqueue_packet_editioned(&je_packet, &be_packet); } } diff --git a/crates/pumpkin/src/world/custom_bossbar.rs b/crates/pumpkin/src/world/custom_bossbar.rs index 642855605..a897dc0a0 100644 --- a/crates/pumpkin/src/world/custom_bossbar.rs +++ b/crates/pumpkin/src/world/custom_bossbar.rs @@ -108,7 +108,7 @@ impl CustomBossbars { None } - pub async fn remove_bossbar( + pub fn remove_bossbar( &mut self, server: &Server, resource_location: String, @@ -125,7 +125,7 @@ impl CustomBossbars { if bossbar.visible { for player in online_players { - player.remove_bossbar(bossbar.bossbar_data.uuid).await; + player.remove_bossbar(bossbar.bossbar_data.uuid); } } @@ -141,7 +141,7 @@ impl CustomBossbars { self.custom_bossbars.contains_key(resource_location) } - pub async fn update_value( + pub fn update_value( &mut self, server: &Server, resource_location: String, @@ -175,8 +175,7 @@ impl CustomBossbars { .filter(|player| bossbar.players.contains(&player.gameprofile.id)); for player in matching_players { player - .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health) - .await; + .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health); } return Ok(()); @@ -186,7 +185,7 @@ impl CustomBossbars { )) } - pub async fn update_max( + pub fn update_max( &mut self, server: &Server, resource_location: String, @@ -220,8 +219,7 @@ impl CustomBossbars { .filter(|player| bossbar.players.contains(&player.gameprofile.id)); for player in matching_players { player - .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health) - .await; + .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health); } return Ok(()); @@ -231,7 +229,7 @@ impl CustomBossbars { )) } - pub async fn update_health( + pub fn update_health( &mut self, server: &Server, resource_location: String, @@ -268,8 +266,7 @@ impl CustomBossbars { .filter(|player| bossbar.players.contains(&player.gameprofile.id)); for player in matching_players { player - .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health) - .await; + .update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health); } return Ok(()); @@ -279,7 +276,7 @@ impl CustomBossbars { )) } - pub async fn update_visibility( + pub fn update_visibility( &mut self, server: &Server, resource_location: String, @@ -304,9 +301,9 @@ impl CustomBossbars { for player in online_players { if bossbar.visible { - player.send_bossbar(&bossbar.bossbar_data).await; + player.send_bossbar(&bossbar.bossbar_data); } else { - player.remove_bossbar(bossbar.bossbar_data.uuid).await; + player.remove_bossbar(bossbar.bossbar_data.uuid); } } @@ -317,35 +314,25 @@ impl CustomBossbars { )) } - pub async fn update_name( + pub fn update_name( &mut self, server: &Server, resource_location: &str, - new_title: TextComponent, + new_title: &TextComponent, ) -> Result<(), BossbarUpdateError> { let bossbar = self.custom_bossbars.get_mut(resource_location); if let Some(bossbar) = bossbar { - if bossbar.bossbar_data.title == new_title { - return Err(BossbarUpdateError::NoChanges("name", None)); - } - - bossbar.bossbar_data.title = new_title; - - if !bossbar.visible { - return Ok(()); - } + bossbar.bossbar_data.title = new_title.clone(); let players: Vec> = server.get_all_players(); - let matching_players = players + let online_players = players .iter() .filter(|player| bossbar.players.contains(&player.gameprofile.id)); - for player in matching_players { - player - .update_bossbar_title( - &bossbar.bossbar_data.uuid, - bossbar.bossbar_data.title.clone(), - ) - .await; + + if bossbar.visible { + for player in online_players { + player.update_bossbar_title(&bossbar.bossbar_data.uuid, new_title.clone()); + } } return Ok(()); @@ -355,87 +342,73 @@ impl CustomBossbars { )) } - pub async fn update_color( + pub fn update_color( &mut self, server: &Server, - resource_location: String, + resource_location: &str, new_color: BossbarColor, ) -> Result<(), BossbarUpdateError> { - let bossbar = self.custom_bossbars.get_mut(&resource_location); + let bossbar = self.custom_bossbars.get_mut(resource_location); if let Some(bossbar) = bossbar { - if bossbar.bossbar_data.color == new_color { - return Err(BossbarUpdateError::NoChanges("color", None)); - } - bossbar.bossbar_data.color = new_color; - if !bossbar.visible { - return Ok(()); - } - let players: Vec> = server.get_all_players(); - let matching_players = players + let online_players = players .iter() .filter(|player| bossbar.players.contains(&player.gameprofile.id)); - for player in matching_players { - player - .update_bossbar_style( + + if bossbar.visible { + for player in online_players { + player.update_bossbar_style( &bossbar.bossbar_data.uuid, - bossbar.bossbar_data.color, + new_color, bossbar.bossbar_data.division, bossbar.bossbar_data.flags, - ) - .await; + ); + } } return Ok(()); } Err(BossbarUpdateError::InvalidResourceLocation( - resource_location, + resource_location.to_string(), )) } - pub async fn update_division( + pub fn update_style( &mut self, server: &Server, - resource_location: String, - new_division: BossbarDivisions, + resource_location: &str, + new_style: BossbarDivisions, ) -> Result<(), BossbarUpdateError> { - let bossbar = self.custom_bossbars.get_mut(&resource_location); + let bossbar = self.custom_bossbars.get_mut(resource_location); if let Some(bossbar) = bossbar { - if bossbar.bossbar_data.division == new_division { - return Err(BossbarUpdateError::NoChanges("style", None)); - } - - bossbar.bossbar_data.division = new_division; - - if !bossbar.visible { - return Ok(()); - } + bossbar.bossbar_data.division = new_style; let players: Vec> = server.get_all_players(); - let matching_players = players + let online_players = players .iter() .filter(|player| bossbar.players.contains(&player.gameprofile.id)); - for player in matching_players { - player - .update_bossbar_style( + + if bossbar.visible { + for player in online_players { + player.update_bossbar_style( &bossbar.bossbar_data.uuid, bossbar.bossbar_data.color, - bossbar.bossbar_data.division, + new_style, bossbar.bossbar_data.flags, - ) - .await; + ); + } } return Ok(()); } Err(BossbarUpdateError::InvalidResourceLocation( - resource_location, + resource_location.to_string(), )) } - pub async fn update_players( + pub fn set_players( &mut self, server: &Server, resource_location: String, @@ -467,7 +440,7 @@ impl CustomBossbars { continue; }; - player.remove_bossbar(bossbar.bossbar_data.uuid).await; + player.remove_bossbar(bossbar.bossbar_data.uuid); } } @@ -482,7 +455,7 @@ impl CustomBossbars { continue; }; - player.send_bossbar(&bossbar.bossbar_data).await; + player.send_bossbar(&bossbar.bossbar_data); } return Ok(()); diff --git a/crates/pumpkin/src/world/dragon_fight.rs b/crates/pumpkin/src/world/dragon_fight.rs index fbdd9b6c8..779ecacc6 100644 --- a/crates/pumpkin/src/world/dragon_fight.rs +++ b/crates/pumpkin/src/world/dragon_fight.rs @@ -4,9 +4,7 @@ //! Matches vanilla `EnderDragonFight` behaviour as closely as `PumpkinMC`'s //! current API allows. -use std::sync::Arc; - -use tokio::sync::Mutex; +use std::sync::{Arc, Mutex}; use tracing::{debug, info}; use uuid::Uuid; @@ -132,7 +130,7 @@ impl DragonFight { // ── Main tick ───────────────────────────────────────────────────────────── - pub async fn tick(fight_mutex: &Mutex, world: &Arc) { + pub fn tick(fight_mutex: &Mutex, world: &Arc) { let ( ticks_since_last_player_scan, needs_state_scanning, @@ -140,7 +138,9 @@ impl DragonFight { dragon_killed, dragon_uuid, ) = { - let mut fight = fight_mutex.lock().await; + let mut fight = fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); fight.ticks_since_last_player_scan += 1; ( fight.ticks_since_last_player_scan, @@ -153,12 +153,20 @@ impl DragonFight { // 1. Update boss-bar recipients every 20 ticks. if ticks_since_last_player_scan >= PLAYER_SCAN_INTERVAL { - let mut fight = fight_mutex.lock().await; - fight.update_players(world).await; + let mut fight = fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + fight.update_players(world); fight.ticks_since_last_player_scan = 0; } - let is_empty = { fight_mutex.lock().await.bossbar_players.is_empty() }; + let is_empty = { + fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .bossbar_players + .is_empty() + }; // Nothing to do without nearby players. if is_empty { return; @@ -166,26 +174,32 @@ impl DragonFight { // 2. One-time state scan on the first populated tick. if needs_state_scanning { - let mut fight = fight_mutex.lock().await; - fight.scan_state(world).await; + let mut fight = fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + fight.scan_state(world); fight.needs_state_scanning = false; } // 3. Respawn sequence (takes priority over normal dragon-missing logic). if respawn_stage.is_some() { - let mut fight = fight_mutex.lock().await; - fight.tick_respawn(world).await; + let mut fight = fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + fight.tick_respawn(world); return; } // 4. Normal fight ticking. if !dragon_killed { - let mut fight = fight_mutex.lock().await; + let mut fight = fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); fight.ticks_since_dragon_seen += 1; if dragon_uuid.is_none() || fight.ticks_since_dragon_seen >= MAX_TICKS_BEFORE_DRAGON_RESPAWN { - fight.find_or_create_dragon(world).await; + fight.find_or_create_dragon(world); fight.ticks_since_dragon_seen = 0; } @@ -201,7 +215,7 @@ impl DragonFight { /// Runs once on the first tick with nearby players. Determines whether /// this is a fresh fight or a resumed one and reconciles the entity list. - async fn scan_state(&mut self, world: &Arc) { + fn scan_state(&mut self, world: &Arc) { info!("Scanning End fight state..."); let has_active_portal = Self::has_active_exit_portal(world); @@ -213,9 +227,9 @@ impl DragonFight { info!("No exit portal – fight is fresh or in progress."); self.previously_killed = false; if self.portal_location.is_none() { - self.spawn_exit_portal(world, false).await; + self.spawn_exit_portal(world, false); } - self.spawn_crystals(world).await; + self.spawn_crystals(world); } // Reconcile any live dragon entity. @@ -236,7 +250,7 @@ impl DragonFight { .iter() .find(|e| e.get_entity().entity_uuid == uuid) { - e.get_entity().remove().await; + e.get_entity().remove(); } self.dragon_uuid = None; self.dragon_killed = true; @@ -276,7 +290,7 @@ impl DragonFight { // ── Dragon lifecycle ────────────────────────────────────────────────────── - async fn find_or_create_dragon(&mut self, world: &Arc) { + fn find_or_create_dragon(&mut self, world: &Arc) { let uuid = { let entities = world.entities.load(); entities @@ -291,31 +305,31 @@ impl DragonFight { self.ticks_since_dragon_seen = 0; } else { debug!("No dragon found – spawning one."); - self.create_new_dragon(world).await; + self.create_new_dragon(world); } } - async fn create_new_dragon(&mut self, world: &Arc) { + fn create_new_dragon(&mut self, world: &Arc) { let uuid = Uuid::new_v4(); let position = Vector3::new(0.5, DRAGON_SPAWN_Y, 0.5); let dragon = crate::entity::r#type::from_type(&EntityType::ENDER_DRAGON, position, world, uuid); - world.spawn_entity(dragon).await; + world.spawn_entity_non_save(dragon); self.dragon_uuid = Some(uuid); info!("Spawned ender dragon {:?}.", uuid); } /// Called every tick while the dragon is alive. Updates the boss-bar /// health fraction, matching vanilla `EnderDragonFight.updateDragon`. - pub async fn update_dragon(&mut self, world: &Arc, health: f32, max_health: f32) { + pub fn update_dragon(&mut self, world: &Arc, health: f32, max_health: f32) { self.ticks_since_dragon_seen = 0; let fraction = if max_health > 0.0 { (health / max_health).clamp(0.0, 1.0) } else { 0.0 }; - self.update_bossbar_health(world, fraction).await; + self.update_bossbar_health(world, fraction); // Sync fight origin to the dragon so its pathfinding nodes are correctly placed. if let Some(loc) = self.portal_location @@ -329,36 +343,34 @@ impl DragonFight { .cast_any() .downcast_ref::() { - dragon.set_fight_origin(loc).await; + dragon.set_fight_origin(loc); } } /// Called by the dragon entity when it dies. Activates the portal, places /// the egg on a first kill, spawns a gateway, and hides the boss bar. /// Matches vanilla `EnderDragonFight.setDragonKilled`. - pub async fn set_dragon_killed(&mut self, world: &Arc, killed_uuid: Uuid) { + pub fn set_dragon_killed(&mut self, world: &Arc, killed_uuid: Uuid) { if Some(killed_uuid) != self.dragon_uuid { return; } - self.update_bossbar_health(world, 0.0).await; - self.remove_all_bossbar(world).await; + self.update_bossbar_health(world, 0.0); + self.remove_all_bossbar(world); // Activate the exit portal. - self.spawn_exit_portal(world, true).await; + self.spawn_exit_portal(world, true); // Place the dragon egg on the first kill. if !self.previously_killed && let Some(loc) = self.portal_location { let egg_pos = BlockPos::new(loc.0.x, loc.0.y + 4, loc.0.z); - world - .set_block_state( - &egg_pos, - Block::DRAGON_EGG.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &egg_pos, + Block::DRAGON_EGG.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } // Spawn a new end gateway. @@ -384,9 +396,9 @@ impl DragonFight { /// Called when an end crystal is destroyed. If a respawn is in progress /// and this was one of the ritual crystals, the respawn is aborted. /// Matches vanilla `EnderDragonFight.onCrystalDestroyed`. - pub async fn on_crystal_destroyed(&mut self, world: &Arc, crystal_uuid: Uuid) { + pub fn on_crystal_destroyed(&mut self, world: &Arc, crystal_uuid: Uuid) { if self.respawn_stage.is_some() && self.respawn_crystal_uuids.contains(&crystal_uuid) { - self.abort_respawn(world).await; + self.abort_respawn(world); } else { self.update_crystal_count(world); // The dragon entity itself handles the visual beam-break logic; @@ -398,7 +410,7 @@ impl DragonFight { /// Attempt to begin a respawn. Requires four end crystals placed on the /// cardinal sides of the portal, exactly as in vanilla `tryRespawn`. - pub async fn try_respawn(&mut self, world: &Arc) { + pub fn try_respawn(&mut self, world: &Arc) { if !self.dragon_killed || self.respawn_stage.is_some() { return; } @@ -406,7 +418,7 @@ impl DragonFight { // Ensure we know where the portal is. if self.portal_location.is_none() { info!("Tried to respawn but no portal location – placing one."); - self.spawn_exit_portal(world, true).await; + self.spawn_exit_portal(world, true); } let Some(portal_loc) = self.portal_location else { @@ -441,25 +453,25 @@ impl DragonFight { } debug!("Found all four ritual crystals – beginning respawn."); - self.begin_respawn(world, ritual_uuids).await; + self.begin_respawn(world, ritual_uuids); } - async fn begin_respawn(&mut self, world: &Arc, crystal_uuids: Vec) { + fn begin_respawn(&mut self, world: &Arc, crystal_uuids: Vec) { // Tear down the active portal (replace END_PORTAL/BEDROCK with END_STONE) // so the podium resets, matching vanilla. if let Some(loc) = self.portal_location { - self.clear_portal_blocks(world, loc).await; + Self::clear_portal_blocks(world, loc); } self.respawn_stage = Some(DragonRespawnStage::Start); self.respawn_time = 0; self.respawn_crystal_uuids = crystal_uuids; - self.spawn_exit_portal(world, false).await; + self.spawn_exit_portal(world, false); } /// Replace the bedrock/portal blocks of the current podium with end-stone, /// matching the vanilla portal-reset done during respawn. - async fn clear_portal_blocks(&self, world: &Arc, loc: BlockPos) { + fn clear_portal_blocks(world: &Arc, loc: BlockPos) { // The podium is 7×6×7 centred on loc; just scan a generous volume. for dy in -1i32..=5 { for dx in -4i32..=4 { @@ -467,30 +479,28 @@ impl DragonFight { let pos = BlockPos::new(loc.0.x + dx, loc.0.y + dy, loc.0.z + dz); let block = world.get_block(&pos); if block == &Block::BEDROCK || block == &Block::END_PORTAL { - world - .set_block_state( - &pos, - Block::END_STONE.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &pos, + Block::END_STONE.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } } } } } - async fn abort_respawn(&mut self, world: &Arc) { + fn abort_respawn(&mut self, world: &Arc) { debug!("Aborting dragon respawn sequence."); self.respawn_stage = None; self.respawn_time = 0; self.respawn_crystal_uuids.clear(); // Re-activate the portal so the world remains in a valid state. - self.spawn_exit_portal(world, true).await; + self.spawn_exit_portal(world, true); } /// Drive the respawn animation forward by one tick. - async fn tick_respawn(&mut self, world: &Arc) { + fn tick_respawn(&mut self, world: &Arc) { let Some(stage) = self.respawn_stage else { return; }; @@ -503,7 +513,7 @@ impl DragonFight { .iter() .all(|uid| entities.iter().any(|e| e.get_entity().entity_uuid == *uid)); if !all_alive { - self.abort_respawn(world).await; + self.abort_respawn(world); return; } } @@ -526,7 +536,7 @@ impl DragonFight { self.respawn_time = 0; self.respawn_crystal_uuids.clear(); self.dragon_killed = false; - self.create_new_dragon(world).await; + self.create_new_dragon(world); } } } @@ -554,7 +564,7 @@ impl DragonFight { /// Spawn end crystals on the obsidian spike tops. Skips if any crystal /// already exists (resumed world). Matches vanilla `respawnCrystals`. - pub async fn spawn_crystals(&mut self, world: &Arc) { + pub fn spawn_crystals(&mut self, world: &Arc) { if world .entities .load() @@ -585,7 +595,7 @@ impl DragonFight { ); let crystal = Arc::new(EndCrystalEntity::new(entity)); crystal.set_show_bottom(true); - world.spawn_entity(crystal).await; + world.spawn_entity_non_save(crystal); } info!("Spawned end crystals on spike tops."); } @@ -594,7 +604,7 @@ impl DragonFight { /// Place (or activate) the exit podium. `active = true` fills the portal /// disc with `END_PORTAL` blocks after the dragon dies. - pub async fn spawn_exit_portal(&mut self, world: &Arc, active: bool) { + pub fn spawn_exit_portal(&mut self, world: &Arc, active: bool) { // Determine location once and cache it. if self.portal_location.is_none() { let top_y = world.get_top_block(Vector2::new(0, 0)); @@ -610,7 +620,7 @@ impl DragonFight { } if let Some(loc) = self.portal_location { - super::end_podium::place(world, loc, active).await; + super::end_podium::place(world, loc, active); } } @@ -631,20 +641,18 @@ impl DragonFight { } } - async fn update_bossbar_health(&self, world: &Arc, health: f32) { + fn update_bossbar_health(&self, world: &Arc, health: f32) { for player in world.players.load().iter() { if self.bossbar_players.contains(&player.gameprofile.id) { - player - .update_bossbar_health(&self.bossbar_uuid, health) - .await; + player.update_bossbar_health(&self.bossbar_uuid, health); } } } - async fn remove_all_bossbar(&mut self, world: &Arc) { + fn remove_all_bossbar(&mut self, world: &Arc) { for player in world.players.load().iter() { if self.bossbar_players.contains(&player.gameprofile.id) { - player.remove_bossbar(self.bossbar_uuid).await; + player.remove_bossbar(self.bossbar_uuid); } } self.bossbar_players.clear(); @@ -652,7 +660,7 @@ impl DragonFight { /// Sync the boss-bar recipient list with nearby players. /// Matches vanilla `updatePlayers`. - async fn update_players(&mut self, world: &Arc) { + fn update_players(&mut self, world: &Arc) { let players = world.players.load(); let current: Vec = players @@ -673,7 +681,7 @@ impl DragonFight { if !self.dragon_killed && let Some(p) = players.iter().find(|p| p.gameprofile.id == uid) { - p.send_bossbar(&self.make_bossbar()).await; + p.send_bossbar(&self.make_bossbar()); } self.bossbar_players.push(uid); } @@ -693,7 +701,7 @@ impl DragonFight { .find(|player| &player.gameprofile.id == uid) .cloned(); if let Some(player) = player { - player.remove_bossbar(self.bossbar_uuid).await; + player.remove_bossbar(self.bossbar_uuid); } self.bossbar_players.retain(|u| u != uid); } diff --git a/crates/pumpkin/src/world/end_podium.rs b/crates/pumpkin/src/world/end_podium.rs index 6f7248c8b..3cee240b5 100644 --- a/crates/pumpkin/src/world/end_podium.rs +++ b/crates/pumpkin/src/world/end_podium.rs @@ -15,7 +15,7 @@ use pumpkin_world::world::BlockFlags; use super::World; /// Place the podium structure centred on `origin` into `world`. -pub async fn place(world: &Arc, origin: BlockPos, active: bool) { +pub fn place(world: &Arc, origin: BlockPos, active: bool) { let ox = origin.0.x; let oy = origin.0.y; let oz = origin.0.z; @@ -56,21 +56,17 @@ pub async fn place(world: &Arc, origin: BlockPos, active: bool) { } }; - world - .set_block_state(&pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, state_id, BlockFlags::NOTIFY_ALL); } } } for y in oy..=(oy + 3) { - world - .set_block_state( - &BlockPos::new(ox, y, oz), - Block::BEDROCK.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &BlockPos::new(ox, y, oz), + Block::BEDROCK.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } // Wall torches on N/S/E/W faces at pillar height 2 @@ -84,12 +80,10 @@ pub async fn place(world: &Arc, origin: BlockPos, active: bool) { ] { let props = WallTorchLikeProperties { facing }; let state_id = props.to_state_id(&Block::WALL_TORCH); - world - .set_block_state( - &BlockPos::new(ox + dx, torch_y, oz + dz), - state_id, - BlockFlags::NOTIFY_ALL, - ) - .await; + world.set_block_state( + &BlockPos::new(ox + dx, torch_y, oz + dz), + state_id, + BlockFlags::NOTIFY_ALL, + ); } } diff --git a/crates/pumpkin/src/world/explosion.rs b/crates/pumpkin/src/world/explosion.rs index 2eb24dad0..a41406eb5 100644 --- a/crates/pumpkin/src/world/explosion.rs +++ b/crates/pumpkin/src/world/explosion.rs @@ -360,7 +360,7 @@ impl Explosion { map } - async fn damage_entities(&self, world: &Arc) { + fn damage_entities(&self, world: &Arc) { // Explosion is too small if self.power < 1.0e-5 { return; @@ -410,7 +410,7 @@ impl Explosion { let exposure = if !should_damage && knockback_multiplier == 0.0 { 0.0 } else { - Self::calculate_exposure(&self.pos, entity, world).await as f64 + Self::calculate_exposure(&self.pos, entity, world) as f64 }; if exposure == 0.0 { @@ -420,9 +420,7 @@ impl Explosion { if should_damage { let damage = calc.get_entity_damage_amount(self, entity_base.as_ref(), exposure as f32); - entity - .damage(entity_base.as_ref(), damage, DamageType::EXPLOSION) - .await; + entity.damage(entity_base.as_ref(), damage, DamageType::EXPLOSION); } // Calculate and apply knockback @@ -442,7 +440,7 @@ impl Explosion { } } - async fn calculate_exposure( + fn calculate_exposure( explosion_pos: &Vector3, entity: &Entity, world: &Arc, @@ -476,11 +474,10 @@ impl Explosion { let vec3d = Vector3::new(n + offset_x, o, p + offset_z); if world - .raycast(vec3d, *explosion_pos, async |pos, world_ref| { + .raycast(vec3d, *explosion_pos, |pos, world_ref| { let state = world_ref.get_block_state(pos); !state.is_air() && !state.collision_shapes.is_empty() }) - .await .is_none() { visible_points += 1; @@ -503,7 +500,7 @@ impl Explosion { /// Returns the removed block count pub async fn explode(&self, world: &Arc) -> u32 { - self.damage_entities(world).await; + self.damage_entities(world); match self.block_interaction { BlockInteraction::Keep => 0, @@ -512,13 +509,11 @@ impl Explosion { for (pos, (block, _state)) in &blocks { let pumpkin_block = world.block_registry.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .explode(ExplodeArgs { - world, - block, - position: pos, - }) - .await; + pumpkin_block.explode(ExplodeArgs { + world, + block, + position: pos, + }); } } 0 @@ -546,16 +541,14 @@ impl Explosion { let explosion_radius = decay_drops.then_some(self.power); for (pos, (block, state)) in &blocks { - world - .set_block_state(pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); world.close_container_screens_at(pos).await; let pumpkin_block = world.block_registry.get_pumpkin_block(block.id); if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) { - let is_raining = world.is_raining().await; - let is_thundering = world.is_thundering().await; + let is_raining = world.is_raining(); + let is_thundering = world.is_thundering(); let params = LootContextParameters { block_state: Some(state), explosion_radius, @@ -572,13 +565,11 @@ impl Explosion { drop_loot(world, block, pos, false, params).await; } if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .explode(ExplodeArgs { - world, - block, - position: pos, - }) - .await; + pumpkin_block.explode(ExplodeArgs { + world, + block, + position: pos, + }); } } // TODO: fire diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index d6f3fd948..a00a77427 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -1,6 +1,5 @@ use crate::block::entities::{BlockEntity, block_entity_from_nbt}; use dashmap::DashMap; -use pumpkin_data::attributes::Attributes; use pumpkin_data::chunk::Biome; use pumpkin_data::item::{BedrockItem, BedrockItemVersion}; use pumpkin_protocol::bedrock::client::item_registry::{CItemRegistry, ItemData}; @@ -9,6 +8,7 @@ use pumpkin_protocol::bedrock::client::{CBiomeDefinitionList, block_actor_data:: use pumpkin_protocol::bedrock::network_item::{NetworkItemDescriptor, NetworkItemStackDescriptor}; use pumpkin_protocol::codec::data_component::data_to_proto_sound; use pumpkin_world::generation::proto_chunk::GenerationCache; +use rayon::prelude::*; use std::sync::atomic::Ordering::Relaxed; use std::sync::{Arc, Weak}; use std::{ @@ -27,12 +27,11 @@ pub mod time; pub mod villager_poi; use crate::block::RandomTickArgs; +use crate::world::chunker::get_view_distance; use crate::world::chunker::is_within_view_distance; -use crate::world::{chunker::get_view_distance, loot::LootContextParameters}; use crate::{block::BlockEvent, entity::item::ItemEntity}; use crate::{ block::{ - self, registry::BlockRegistry, {OnNeighborUpdateArgs, OnScheduledTickArgs}, }, @@ -40,12 +39,9 @@ use crate::{ entity::{Entity, EntityBase, RemovalReason, player::Player, r#type::from_type}, error::PumpkinError, net::{ClientPlatform, bedrock::BedrockClient, java::JavaClient}, - plugin::{ - block::block_break::BlockBreakEvent, - player::{ - player_change_world::PlayerChangeWorldEvent, player_join::PlayerJoinEvent, - player_leave::PlayerLeaveEvent, player_respawn::PlayerRespawnEvent, - }, + plugin::player::{ + player_change_world::PlayerChangeWorldEvent, player_join::PlayerJoinEvent, + player_leave::PlayerLeaveEvent, player_respawn::PlayerRespawnEvent, }, server::Server, }; @@ -63,7 +59,7 @@ use pumpkin_data::chunk_gen_settings::GenerationSettings; use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::MobCategory; -use pumpkin_data::fluid::{Falling, FluidProperties, FluidState}; +use pumpkin_data::fluid::FluidState; use pumpkin_data::game_rules::{GameRule, GameRuleValue}; use pumpkin_data::{ Block, BlockStateId, @@ -177,8 +173,6 @@ use pumpkin_world::chunk::ChunkHeightmapType::{self, MotionBlocking}; use uuid::Uuid; use weather::Weather; -type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; - const MAX_LIGHT_LEVEL: u8 = 15; fn bedrock_chest_block_actor(state_id: BlockStateId, position: BlockPos) -> Option { @@ -257,27 +251,27 @@ pub struct World { /// The world's worldborder, defining the playable area and controlling its expansion or contraction. pub worldborder: Mutex, /// The world's time, including counting ticks for weather, time cycles, and statistics. - pub level_time: Mutex, + pub level_time: std::sync::Mutex, /// The type of dimension the world is in. pub dimension: Dimension, pub sea_level: i32, pub min_y: i32, /// The world's weather, including rain and thunder levels. - pub weather: Mutex, + pub weather: std::sync::Mutex, /// Block Behaviour pub block_registry: Arc, pub server: Weak, - synced_block_event_queue: Mutex>, + synced_block_event_queue: std::sync::Mutex>, /// A map of unsent block changes, keyed by block position. - unsent_block_changes: Mutex>, + unsent_block_changes: std::sync::Mutex>, /// Persisted vanilla POI storage for portal and villager lookups. - pub portal_poi: Mutex, + pub portal_poi: std::sync::Mutex, /// Villager job sites and their current owners. - pub villager_poi: Mutex, + pub villager_poi: std::sync::Mutex, /// Active raids in this world. - pub raids: Mutex, + pub raids: std::sync::Mutex, /// End Dragon fight manager (only present in `THE_END` dimension). - pub dragon_fight: Option>, + pub dragon_fight: Option>, pub spawn_state: ArcSwap, pub active_chunks: ArcSwap>>, pub forced_chunks: std::sync::Mutex>>, @@ -359,7 +353,7 @@ impl World { // Load portal POI from disk (PoiStorage::new automatically loads from disk if files exist) let portal_poi = portal::PortalPoiStorage::new(level.level_folder.poi_folder.clone()); let dragon_fight = (dimension.minecraft_name == Dimension::THE_END.minecraft_name) - .then(|| Mutex::new(dragon_fight::DragonFight::new())); + .then(|| std::sync::Mutex::new(dragon_fight::DragonFight::new())); let custom_data_path = level .level_folder @@ -385,17 +379,17 @@ impl World { entities: ArcSwap::new(Arc::new(Vec::new())), scoreboard: Mutex::new(Scoreboard::default()), worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 5.999_996_8E7, 0, 5, 300)), - level_time: Mutex::new(LevelTime::new()), + level_time: std::sync::Mutex::new(LevelTime::new()), dimension, - weather: Mutex::new(Weather::new()), + weather: std::sync::Mutex::new(Weather::new()), block_registry, sea_level: generation_settings.sea_level, min_y: i32::from(generation_settings.shape.min_y), - synced_block_event_queue: Mutex::new(Vec::new()), - unsent_block_changes: Mutex::new(HashMap::new()), - portal_poi: Mutex::new(portal_poi), - villager_poi: Mutex::new(villager_poi::VillagerPoiStorage::default()), - raids: Mutex::new(raid::Raids::default()), + synced_block_event_queue: std::sync::Mutex::new(Vec::new()), + unsent_block_changes: std::sync::Mutex::new(HashMap::new()), + portal_poi: std::sync::Mutex::new(portal_poi), + villager_poi: std::sync::Mutex::new(villager_poi::VillagerPoiStorage::default()), + raids: std::sync::Mutex::new(raid::Raids::default()), dragon_fight, spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())), active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())), @@ -485,7 +479,11 @@ impl World { } // Save portal POI to disk - let save_result = self.portal_poi.lock().await.save_all(); + let save_result = self + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .save_all(); if let Err(e) = save_result { error!("Failed to save portal POI: {e}"); } @@ -555,7 +553,7 @@ impl World { data: VarInt(0), fire_at_position: None, }; - self.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet); + self.broadcast_to_chunk_editioned(chunk_pos, &je_packet, &be_packet); } else { self.broadcast_to_chunk(chunk_pos, &je_packet); } @@ -576,7 +574,7 @@ impl World { tick: VarULong(0), ambient: false, }; - self.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet); + self.broadcast_to_chunk_editioned(chunk_pos, &je_packet, &be_packet); } pub fn send_add_mob_effect(&self, entity: &Entity, effect: &pumpkin_data::potion::Effect) { @@ -611,7 +609,7 @@ impl World { ambient: effect.ambient, }; - self.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet); + self.broadcast_to_chunk_editioned(chunk_pos, &je_packet, &be_packet); } pub fn set_difficulty(&self, difficulty: Difficulty) { @@ -644,30 +642,38 @@ impl World { self.level_info.store(Arc::new(new_info)); } - pub async fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) { - let mut queue = self.synced_block_event_queue.lock().await; + pub fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) { + let mut queue = self + .synced_block_event_queue + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); queue.push(BlockEvent { pos, r#type, data }); } - pub async fn flush_synced_block_events(self: &Arc) { + pub fn flush_synced_block_events(self: &Arc) { // THIS IS IMPORTANT // it prevents deadlocks and also removes the need to wait for a lock when adding a new synced block let events = { - let mut queue = self.synced_block_event_queue.lock().await; + let mut queue = self + .synced_block_event_queue + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); std::mem::take(&mut *queue) }; for event in events { let block = self.get_block(&event.pos); - if !self - .block_registry - .on_synced_block_event(block, self, &event.pos, event.r#type, event.data) - .await - { + if !self.block_registry.on_synced_block_event( + block, + self, + &event.pos, + event.r#type, + event.data, + ) { continue; } let chunk_pos = event.pos.chunk_position(); - self.broadcast_to_chunk_editioned_sync( + self.broadcast_to_chunk_editioned( chunk_pos, &CBlockEvent::new( event.pos, @@ -760,15 +766,6 @@ impl World { } } - async fn broadcast_bedrock_grouped_async<'a, P: BClientPacket>( - packet: &P, - recipients: impl Iterator>, - ) { - for recipient in recipients { - recipient.enqueue_client_packet(packet).await; - } - } - /// Broadcasts a packet to all connected players within the world. /// Please avoid this as we want to replace it with `broadcast_editioned` /// @@ -781,16 +778,10 @@ impl World { Self::broadcast_java_grouped(packet, recipients_by_version); } - pub fn broadcast_packet_all_sync(&self, packet: &P) { - let players = self.players.load(); - let recipients_by_version = Self::collect_java_recipients_by_version(players.iter()); - Self::broadcast_java_grouped(packet, recipients_by_version); - } - - pub async fn broadcast_system_message(&self, message: &TextComponent, overlay: bool) { + pub fn broadcast_system_message(&self, message: &TextComponent, overlay: bool) { let je_packet = CSystemChatMessage::new(message, overlay); let be_packet = Self::component_to_bedrock_text(message); - self.broadcast_editioned(&je_packet, &be_packet).await; + self.broadcast_editioned(&je_packet, &be_packet); } fn component_to_bedrock_text(message: &TextComponent) -> SText<'static> { @@ -815,7 +806,7 @@ impl World { } } - pub async fn broadcast_message( + pub fn broadcast_message( &self, message: &TextComponent, sender_name: &TextComponent, @@ -826,11 +817,11 @@ impl World { let je_packet = CDisguisedChatMessage::new(message, (chat_type + 1).into(), sender_name, target_name); - self.broadcast_editioned(&je_packet, &be_packet).await; + self.broadcast_editioned(&je_packet, &be_packet); } // This should replace broadcast_packet_all at some point - pub async fn broadcast_editioned( + pub fn broadcast_editioned( &self, je_packet: &J, be_packet: &B, @@ -839,14 +830,13 @@ impl World { let je_recipients_by_version = Self::collect_java_recipients_by_version(players.iter()); Self::broadcast_java_grouped(je_packet, je_recipients_by_version); - Self::broadcast_bedrock_grouped_async( + Self::broadcast_bedrock_grouped( be_packet, players.iter().filter_map(|p| match p.client.as_ref() { ClientPlatform::Bedrock(be) => Some(be), ClientPlatform::Java(_) => None, }), - ) - .await; + ); } pub async fn broadcast_secure_player_chat( @@ -926,7 +916,7 @@ impl World { sender.chat_session.lock().await.messages_sent += 1; } - pub fn broadcast_packet_except_editioned_sync( + pub fn broadcast_packet_except_editioned( &self, except: &[uuid::Uuid], je_packet: &J, @@ -954,7 +944,7 @@ impl World { /// Broadcasts the skin layers of a player, encoding the metadata for each Java client's own /// protocol version since the tracked data index differs between versions. - fn broadcast_skin_parts_sync( + fn broadcast_skin_parts( &self, except: &[uuid::Uuid], entity_id: i32, @@ -1007,32 +997,6 @@ impl World { Self::broadcast_bedrock_grouped(be_packet, bedrock_recipients.into_iter()); } - pub async fn broadcast_packet_except_editioned( - &self, - except: &[uuid::Uuid], - je_packet: &J, - be_packet: &B, - ) { - let players = self.players.load(); - let mut java_recipients = Vec::new(); - let mut bedrock_recipients = Vec::new(); - - for p in players.iter() { - if except.contains(&p.gameprofile.id) { - continue; - } - match p.client.as_ref() { - ClientPlatform::Java(_) => java_recipients.push(p), - ClientPlatform::Bedrock(be_client) => bedrock_recipients.push(be_client), - } - } - - let recipients_by_version = - Self::collect_java_recipients_by_version(java_recipients.into_iter()); - Self::broadcast_java_grouped(je_packet, recipients_by_version); - Self::broadcast_bedrock_grouped_async(be_packet, bedrock_recipients.into_iter()).await; - } - /// Broadcasts a packet to all connected players within the world, excluding the specified players. /// /// Sends the specified packet to every player currently logged in to the world, excluding the players listed in the `except` parameter. @@ -1216,29 +1180,46 @@ impl World { } #[expect(clippy::too_many_lines)] - pub async fn tick(self: &Arc, server: Arc) { + pub fn tick(self: &Arc, server: &Arc) { const ENTITY_TICK_BATCH_SIZE: usize = 16; - let start = tokio::time::Instant::now(); + let start = std::time::Instant::now(); - self.flush_block_updates().await; - self.flush_synced_block_events().await; + self.flush_block_updates(); + self.flush_synced_block_events(); self.update_active_chunks(); - self.tick_environment().await; - self.raids.lock().await.tick(self).await; - - let world_for_chunks = self.clone(); - let chunk_future = async move { - let t = tokio::time::Instant::now(); - world_for_chunks.tick_chunks().await; - t.elapsed() + self.tick_environment(); + let mut raids = { + let mut guard = self + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::take(&mut *guard) }; + raids.tick(self); + { + let mut guard = self + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (id, raid) in guard.raid_map.drain() { + raids.raid_map.insert(id, raid); + } + raids.next_id = raids.next_id.max(guard.next_id); + *guard = raids; + }; + + let t_chunks = std::time::Instant::now(); + self.tick_chunks(server); + let chunk_elapsed = t_chunks.elapsed(); + + let handle = server.runtime.clone(); let players = self.players.load(); let player_count = players.len(); let players_cache = Arc::new( players - .iter() + .par_iter() .map(|player| { let entity = player.get_entity(); let pos = entity.pos.load(); @@ -1252,101 +1233,69 @@ impl World { .collect::>(), ); - let server_for_players = server.clone(); - let player_future = async move { - let t = tokio::time::Instant::now(); - let mut tasks = tokio::task::JoinSet::new(); - for player in players.iter() { - let p_clone = player.clone(); - let s_clone = server_for_players.clone(); - tasks.spawn(async move { - p_clone.tick(&s_clone).await; - }); - } - while let Some(res) = tasks.join_next().await { - if let Err(e) = res { - error!("Player tick panicked: {:?}", e); - } - } - t.elapsed() - }; + let t_players = std::time::Instant::now(); + players.par_iter().for_each(|player| { + player.tick(server); + }); + let player_elapsed = t_players.elapsed(); let entities_to_tick = self.entities.load(); let entity_count = entities_to_tick.len(); - let server_for_entities = server.clone(); + let server_for_entities = (*server).clone(); let active_chunks = self.active_chunks.load(); let level_for_entities = self.level.clone(); + let entity_handle = handle.clone(); - let entity_future = async move { - let t = tokio::time::Instant::now(); - - let mut tickable = Vec::new(); - for entity in entities_to_tick.iter() { - // Only tick entities that sit in an active (ticking) chunk — the - // same set block-entity ticking and mob spawning already use, and - // like vanilla, which ticks entities only within the simulation - // distance. Use the live position: fast movers such as minecarts - // and projectiles write `pos` directly and leave the cached - // chunk_pos stale. + let t_entities = std::time::Instant::now(); + let tickable: Vec<_> = entities_to_tick + .par_iter() + .filter_map(|entity| { let entity_pos = entity.get_entity().pos.load(); let entity_chunk = Vector2::new( get_section_cord(entity_pos.x.floor() as i32), get_section_cord(entity_pos.z.floor() as i32), ); if !active_chunks.contains(&entity_chunk) { - continue; + return None; } - - // A chunk stays active while it is still being generated. Mobs spawned by the - // generator are added to the world before their chunk is published, and every - // block read in a missing chunk reports air, so ticking them here would let - // them fall through the terrain that is about to appear. if !level_for_entities.is_chunk_loaded(&entity_chunk) { - continue; + return None; } + Some((entity.clone(), entity_chunk)) + }) + .collect(); - tickable.push((entity.clone(), entity_chunk)); - } - - let mut tasks = tokio::task::JoinSet::new(); - for entity_batch in tickable.chunks(ENTITY_TICK_BATCH_SIZE) { - let batch = entity_batch.to_vec(); + tickable + .par_chunks(ENTITY_TICK_BATCH_SIZE) + .for_each(|batch| { + let _guard = entity_handle.enter(); let s_clone = server_for_entities.clone(); let p_cache = players_cache.clone(); - tasks.spawn(async move { - for (entity, entity_chunk) in batch { - entity.get_entity().age.fetch_add(1, Relaxed); - entity.tick(&entity, &s_clone).await; + for (entity, entity_chunk) in batch { + entity.get_entity().age.fetch_add(1, Relaxed); + entity.tick(entity, &s_clone); - let entity_inner = entity.get_entity(); - let entity_pos = entity_inner.pos.load(); - let entity_bb = entity_inner.bounding_box.load(); + let entity_inner = entity.get_entity(); + let entity_pos = entity_inner.pos.load(); + let entity_bb = entity_inner.bounding_box.load(); - for (player, player_pos, player_bb, player_chunk) in p_cache.iter() { - if (player_chunk.x - entity_chunk.x).abs() <= 1 - && (player_chunk.y - entity_chunk.y).abs() <= 1 - && (player_pos.x - entity_pos.x).abs() < 5.0 - && (player_pos.y - entity_pos.y).abs() < 5.0 - && (player_pos.z - entity_pos.z).abs() < 5.0 - && player_bb.intersects(&entity_bb) - { - entity.on_player_collision(player).await; - break; - } + for (player, player_pos, player_bb, player_chunk) in p_cache.iter() { + if (player_chunk.x - entity_chunk.x).abs() <= 1 + && (player_chunk.y - entity_chunk.y).abs() <= 1 + && (player_pos.x - entity_pos.x).abs() < 5.0 + && (player_pos.y - entity_pos.y).abs() < 5.0 + && (player_pos.z - entity_pos.z).abs() < 5.0 + && player_bb.intersects(&entity_bb) + { + entity.on_player_collision(player); + break; } } - }); - } - while let Some(res) = tasks.join_next().await { - if let Err(e) = res { - error!("Entity tick panicked: {:?}", e); } - } - t.elapsed() - }; + }); + let entity_elapsed = t_entities.elapsed(); - let active_chunks = self.active_chunks.load(); let mut block_entities: Vec> = Vec::new(); for chunk_pos in active_chunks.iter() { if let Some(chunk_block_entities) = self.block_entities.get(chunk_pos) { @@ -1355,33 +1304,17 @@ impl World { } let block_entity_count = block_entities.len(); + let t_be = std::time::Instant::now(); let world_for_be = self.clone(); - let block_entity_future = async move { - let t = tokio::time::Instant::now(); - let mut tasks = tokio::task::JoinSet::new(); - for be_batch in block_entities.chunks(16) { - let batch = be_batch.to_vec(); - let w_clone = world_for_be.clone(); - tasks.spawn(async move { - for be in batch { - be.tick(&w_clone).await; - } - }); + let be_handle = handle; + block_entities.par_chunks(16).for_each(|batch| { + let _guard = be_handle.enter(); + let w_clone = world_for_be.clone(); + for be in batch { + be.tick(&w_clone); } - while let Some(res) = tasks.join_next().await { - if let Err(e) = res { - error!("Block entity panicked: {:?}", e); - } - } - t.elapsed() - }; - - let (chunk_elapsed, player_elapsed, entity_elapsed, block_entity_elapsed) = tokio::join!( - chunk_future, - player_future, - entity_future, - block_entity_future - ); + }); + let block_entity_elapsed = t_be.elapsed(); self.level .chunk_loading @@ -1390,7 +1323,7 @@ impl World { .send_change(); if let Some(ref fight_mutex) = self.dragon_fight { - dragon_fight::DragonFight::tick(fight_mutex, self).await; + dragon_fight::DragonFight::tick(fight_mutex, self); } let total_elapsed = start.elapsed(); @@ -1409,31 +1342,37 @@ impl World { } } - pub async fn register_block_change(&self, position: BlockPos, block_state_id: BlockStateId) { + pub fn register_block_change(&self, position: BlockPos, block_state_id: BlockStateId) { self.unsent_block_changes .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .insert(position, block_state_id); } /// Queues block state changes for broadcast to nearby players. /// /// Call [`flush_block_updates`](Self::flush_block_updates) afterward to send the packets. - pub async fn queue_block_updates(&self, changes: &[(BlockPos, BlockStateId)]) { - let mut guard = self.unsent_block_changes.lock().await; + pub fn queue_block_updates(&self, changes: &[(BlockPos, BlockStateId)]) { + let mut guard = self + .unsent_block_changes + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (pos, state_id) in changes { guard.insert(*pos, *state_id); } } #[expect(clippy::too_many_lines)] - pub async fn flush_block_updates(&self) { + pub fn flush_block_updates(&self) { let mut block_state_updates_by_chunk_section: HashMap< Vector3, Vec<(BlockPos, BlockStateId)>, > = HashMap::new(); let changes = { - let mut guard = self.unsent_block_changes.lock().await; + let mut guard = self + .unsent_block_changes + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); std::mem::take(&mut *guard) }; for (position, block_state_id) in changes { @@ -1454,7 +1393,7 @@ impl World { if updates.len() == 1 { let (block_pos, block_state_id) = updates[0]; let be_block_id = BlockState::to_be_network_id(block_state_id); - self.broadcast_to_chunk_editioned_sync( + self.broadcast_to_chunk_editioned( chunk_pos, &CBlockUpdate::new(block_pos, i32::from(block_state_id.as_u16()).into()), &pumpkin_protocol::bedrock::client::CUpdateBlock::new( @@ -1582,9 +1521,12 @@ impl World { } } - async fn tick_environment(&self) { + pub fn tick_environment(self: &Arc) { let (world_age, is_night, time_of_day) = { - let mut level_time = self.level_time.lock().await; + let mut level_time = self + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let advance_time = self.level_info.load().game_rules.advance_time; level_time.tick(advance_time); @@ -1593,8 +1535,11 @@ impl World { self.level.should_unload.store(true, Relaxed); let cleaned_chunks = self.level.clean_memory(); if !cleaned_chunks.is_empty() { - self.remove_entities_in_chunks(&cleaned_chunks).await; - self.level.clean_entity_chunks(&cleaned_chunks); + let world_clone = self.clone(); + tokio::spawn(async move { + world_clone.remove_entities_in_chunks(&cleaned_chunks).await; + world_clone.level.clean_entity_chunks(&cleaned_chunks); + }); } // If autosave is configured and this tick will trigger an autosave, don't double notify if self.level.autosave_ticks == 0 { @@ -1620,80 +1565,108 @@ impl World { ) }; - let mut weather = self.weather.lock().await; - weather.tick_weather(self); + let (should_reset_weather, weather_cycle_enabled) = { + let mut weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + weather.tick_weather(self); + ( + weather.raining || weather.thundering, + weather.weather_cycle_enabled, + ) + }; if self.should_skip_night() && is_night { - let mut level_time = self.level_time.lock().await; - let time = time_of_day + 24000; - level_time.set_time(time - time % 24000); - level_time.send_time(self).await; - drop(level_time); + let level_time = { + let mut guard = self + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let time = time_of_day + 24000; + guard.set_time(time - time % 24000); + guard.clone() + }; + level_time.send_time(self); for player in self.players.load().iter() { - player.wake_up().await; + player.wake_up(); } - if weather.weather_cycle_enabled && (weather.raining || weather.thundering) { + if weather_cycle_enabled && should_reset_weather { + let mut weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); weather.reset_weather_cycle(self); } } else if world_age % 20 == 0 { - let level_time = self.level_time.lock().await; - level_time.send_time(self).await; + let level_time = self + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + level_time.send_time(self); } } #[expect(clippy::too_many_lines)] - pub async fn tick_chunks(self: &Arc) { + pub fn tick_chunks(self: &Arc, server: &Arc) { const BATCH_SIZE: usize = 32; let active_chunks = self.active_chunks.load(); let tick_data = self.level.get_tick_data(&active_chunks); + let handle = server.runtime.clone(); - // ONE JoinSet for all chunk operations - let mut chunk_tasks = tokio::task::JoinSet::new(); - - // 1. Spawn Block Ticks - for chunk_batch in tick_data.block_ticks.chunks(BATCH_SIZE) { - let batch = chunk_batch.to_vec(); - let world = self.clone(); - chunk_tasks.spawn(async move { + // 1. Parallel Block Ticks via Rayon + let world = self.clone(); + let block_handle = handle.clone(); + tick_data + .block_ticks + .par_chunks(BATCH_SIZE) + .for_each(|batch| { + let _guard = block_handle.enter(); + let world = world.clone(); for scheduled_tick in batch { let pos = scheduled_tick.position; let block = world.get_block(&pos); if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(block.id) { - pumpkin_block - .on_scheduled_tick(OnScheduledTickArgs { - world: &world, - block, - position: &pos, - }) - .await; + pumpkin_block.on_scheduled_tick(OnScheduledTickArgs { + world: &world, + block, + position: &pos, + }); } } }); - } - // 2. Spawn Fluid Ticks - for chunk_batch in tick_data.fluid_ticks.chunks(BATCH_SIZE) { - let batch = chunk_batch.to_vec(); - let world = self.clone(); - chunk_tasks.spawn(async move { + // 2. Parallel Fluid Ticks via Rayon + let world = self.clone(); + let fluid_handle = handle.clone(); + tick_data + .fluid_ticks + .par_chunks(BATCH_SIZE) + .for_each(|batch| { + let _guard = fluid_handle.enter(); + let world = world.clone(); for scheduled_tick in batch { let pos = scheduled_tick.position; let fluid = world.get_fluid(&pos); if let Some(pumpkin_fluid) = world.block_registry.get_pumpkin_fluid(fluid.id) { - pumpkin_fluid.on_scheduled_tick(&world, fluid, &pos).await; + pumpkin_fluid.on_scheduled_tick(&world, fluid, &pos); } } }); - } - // 3. Spawn Random Ticks - for chunk_batch in tick_data.random_ticks.chunks(BATCH_SIZE) { - let batch = chunk_batch.to_vec(); - let world = self.clone(); - chunk_tasks.spawn(async move { + // 3. Parallel Random Ticks via Rayon + let world = self.clone(); + let random_handle = handle; + tick_data + .random_ticks + .par_chunks(BATCH_SIZE) + .for_each(|batch| { + let _guard = random_handle.enter(); + let world = world.clone(); for scheduled_tick in batch { let pos = scheduled_tick.position; let (block, fluid) = @@ -1711,24 +1684,21 @@ impl World { && let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(block.id) { - pumpkin_block - .random_tick(RandomTickArgs { - world: &world, - block, - position: &pos, - }) - .await; + pumpkin_block.random_tick(RandomTickArgs { + world: &world, + block, + position: &pos, + }); } if let Some(fluid) = fluid && let Some(pumpkin_fluid) = world.block_registry.get_pumpkin_fluid(fluid.id) { - pumpkin_fluid.random_tick(fluid, &world, &pos).await; + pumpkin_fluid.random_tick(fluid, &world, &pos); } } }); - } // 4. Calculate Spawn List (Sequential setup) let spawn_state = self.spawn_state.load(); @@ -1740,7 +1710,7 @@ impl World { lock.difficulty == Difficulty::Peaceful, ) }; - let spawn_passives = self.level_time.lock().await.time_of_day % 400 == 0; + let spawn_passives = self.get_time_of_day() % 400 == 0; let spawn_enemies = !peaceful && spawn_monsters && spawn_mobs; let spawn_passives = spawn_passives && spawn_mobs; @@ -1751,7 +1721,7 @@ impl World { spawn_passives, )); - // 5. Spawn Chunk Spawners into the SAME JoinSet + // 5. Parallel Chunk Spawners via Rayon if !spawn_list.is_empty() { let mut spawning_chunks = Vec::new(); for pos in active_chunks.iter() { @@ -1762,35 +1732,25 @@ impl World { spawning_chunks.shuffle(&mut rng()); - for chunk_batch in spawning_chunks.chunks(8) { - let batch = chunk_batch.to_vec(); - let world = self.clone(); + let world = self.clone(); + spawning_chunks.par_chunks(8).for_each(|batch| { + let world = world.clone(); let s_list = spawn_list.clone(); let s_state = spawn_state.clone(); - - chunk_tasks.spawn(async move { - for (pos, chunk) in batch { - world - .tick_spawning_chunk(pos, &chunk, &s_list, &s_state) - .await; - } - }); - } + for (pos, chunk) in batch { + world.tick_spawning_chunk(*pos, chunk, &s_list, &s_state); + } + }); } - while let Some(res) = chunk_tasks.join_next().await { - if let Err(e) = res { - error!("Chunk task panicked: {:?}", e); - } - } - - // Update chunk inhabited time for active chunks + // Update chunk inhabited time for active chunks in parallel with Rayon let loaded_chunks = self.level.loaded_chunks.clone(); - for pos in active_chunks.iter() { + let active_chunks_vec: Vec<_> = active_chunks.iter().copied().collect(); + active_chunks_vec.par_iter().for_each(|pos| { if let Some(chunk) = loaded_chunks.get(pos) { chunk.inhabited_time.fetch_add(1, Relaxed); } - } + }); } pub fn check_fluid_collision(self: &Arc, bounding_box: BoundingBox) -> bool { @@ -1986,8 +1946,8 @@ impl World { } // For adjusting movement - pub async fn get_block_collisions( - self: &Arc, + pub fn get_block_collisions( + &self, bounding_box: BoundingBox, entity: &dyn EntityBase, ) -> (Vec, Vec<(usize, BlockPos)>) { @@ -2012,7 +1972,6 @@ impl World { if block == &Block::POWDER_SNOW { if let Some(shape) = crate::block::blocks::powder_snow::collision_shape_for_entity(entity, &pos) - .await { let shape = shape.at_pos(pos); if shape.intersects(&bounding_box) { @@ -2079,7 +2038,7 @@ impl World { } } - pub async fn tick_spawning_chunk( + pub fn tick_spawning_chunk( self: &Arc, chunk_pos: Vector2, chunk: &Arc, @@ -2088,10 +2047,7 @@ impl World { ) { // this.level.tickThunder(chunk); //TODO check in simulation distance - let (is_raining, is_thundering) = { - let weather = self.weather.lock().await; - (weather.raining, weather.thundering) - }; + let (is_raining, is_thundering) = (self.is_raining(), self.is_thundering()); if is_raining && is_thundering && rng().random_range(0..100_000) == 0 { let rand_value = rng().random::() >> 2; @@ -2127,14 +2083,14 @@ impl World { random_pos.to_f64(), &EntityType::SKELETON_HORSE, ); - self.spawn_entity(Arc::new(entity)).await; + self.spawn_entity_non_save(Arc::new(entity)); } let entity = Entity::new( self.clone(), random_pos.to_f64().add_raw(0.5, 0., 0.5), &EntityType::LIGHTNING_BOLT, ); - self.spawn_entity(Arc::new(entity)).await; + self.spawn_entity_non_save(Arc::new(entity)); } } @@ -2151,30 +2107,45 @@ impl World { is_thundering, ); for entity in entities { - self.spawn_entity(entity).await; + self.spawn_entity_non_save(entity); } } - pub async fn get_world_age(&self) -> i64 { - self.level_time.lock().await.world_age + pub fn get_world_age(&self) -> i64 { + self.level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .world_age } - pub async fn get_time_of_day(&self) -> i64 { - self.level_time.lock().await.time_of_day + pub fn get_time_of_day(&self) -> i64 { + self.level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .time_of_day } - pub async fn set_time_of_day(&self, time: i64) { - let mut level_time = self.level_time.lock().await; - level_time.set_time(time); - level_time.send_time(self).await; + pub fn set_time_of_day(&self, time: i64) { + let level_time = { + let mut guard = self + .level_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.set_time(time); + guard.clone() + }; + level_time.send_time(self); } - pub async fn is_raining(&self) -> bool { - self.weather.lock().await.raining + pub fn is_raining(&self) -> bool { + self.weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .raining } - pub async fn is_raining_at(&self, pos: &BlockPos) -> bool { - if !self.is_raining().await { + pub fn is_raining_at(&self, pos: &BlockPos) -> bool { + if !self.is_raining() { return false; } if self.get_heightmap_height(MotionBlocking, pos.0.x, pos.0.z) + 1 > pos.0.y { @@ -2199,15 +2170,21 @@ impl World { return; } } - let mut weather = self.weather.lock().await; + let mut weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if weather.raining != raining { let thunder = weather.thundering; weather.set_weather_parameters(self, 0, 0, raining, thunder); } } - pub async fn is_thundering(&self) -> bool { - self.weather.lock().await.thundering + pub fn is_thundering(&self) -> bool { + self.weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .thundering } pub async fn set_thundering(&self, thundering: bool) { @@ -2222,7 +2199,10 @@ impl World { return; } } - let mut weather = self.weather.lock().await; + let mut weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if weather.thundering != thundering { let raining = weather.raining; weather.set_weather_parameters(self, 0, 0, raining, thundering); @@ -2297,7 +2277,13 @@ impl World { > = std::sync::OnceLock::new(); let level_info = server.level_info.load(); - let weather = self.weather.lock().await; + let (rain_level, lightning_level) = { + let weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (weather.rain_level, weather.thunder_level) + }; let runtime_id = player.entity_id() as u64; let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) { let position = player.position(); @@ -2353,8 +2339,8 @@ impl World { education_edition_offer: VarUInt(0), has_education_features_enabled: false, education_product_id: String::new(), - rain_level: weather.rain_level, - lightning_level: weather.thunder_level, + rain_level, + lightning_level, has_confirmed_platform_locked_content: false, was_multiplayer_intended: true, was_lan_broadcasting_intended: true, @@ -2401,7 +2387,6 @@ impl World { allow_anonymous_block_drops_in_editor_worlds: false, }; drop(level_info); - drop(weather); let Some(client) = player.client.bedrock() else { return; @@ -2426,7 +2411,7 @@ impl World { is_trial: false, rewind_history_size: VarInt(0), server_authoritative_block_breaking: true, - current_level_time: self.level_time.lock().await.world_age as _, + current_level_time: self.get_world_age() as _, enchantment_seed: VarInt(0), block_properties_size: VarUInt(0), // TODO Make this unique @@ -2843,7 +2828,7 @@ impl World { }; let gamemode = player.gamemode.load(); - self.broadcast_packet_except_editioned_sync( + self.broadcast_packet_except_editioned( &[gameprofile.id], &CPlayerInfoUpdate::new( (PlayerInfoFlags::ADD_PLAYER @@ -2905,7 +2890,7 @@ impl World { build_platform: BuildPlatform::Unknown, }; - self.broadcast_packet_except_editioned_sync( + self.broadcast_packet_except_editioned( &[gameprofile.id], &CSpawnEntity::new( (runtime_id as i32).into(), @@ -2921,12 +2906,12 @@ impl World { &bedrock_add_player, ); - self.send_player_equipment(&player).await; + self.send_player_equipment(&player); // Broadcast metadata to Java players so they can correctly interact with the new player let skin_parts = player.config.load().skin_parts; - self.broadcast_skin_parts_sync( + self.broadcast_skin_parts( &[gameprofile.id], runtime_id as i32, skin_parts, @@ -3001,7 +2986,7 @@ impl World { client.send_packet(&ex_add_player).await; - let ex_held_item = existing_player.inventory().held_item().await; + let ex_held_item = existing_player.inventory().held_item(); let ex_be_mob_equipment = pumpkin_protocol::bedrock::client::CMobEquipment { target_runtime_id: (existing_player.entity_id() as u64).into(), @@ -3028,8 +3013,7 @@ impl World { server.plugin_manager.fire(server, &mut event).await; if !event.cancelled { - self.broadcast_system_message(&event.join_message, false) - .await; + self.broadcast_system_message(&event.join_message, false); info!("{}", event.join_message.to_pretty_console()); } } @@ -3249,8 +3233,7 @@ impl World { &java_player, ); - self.broadcast_editioned(&player_info_update, &bedrock_player_list) - .await; + self.broadcast_editioned(&player_info_update, &bedrock_player_list); // If the player has a custom tab_list_name, send an update for it if let Some(tab_list_name) = player.get_tab_list_name().await { @@ -3413,7 +3396,7 @@ impl World { velocity, ); - self.broadcast_packet_except_editioned_sync( + self.broadcast_packet_except_editioned( &[player.gameprofile.id], &spawn_entity, &bedrock_add_player, @@ -3422,7 +3405,7 @@ impl World { // Broadcast metadata to Java players so they can correctly interact with the new player let skin_parts = player.config.load().skin_parts; - self.broadcast_skin_parts_sync( + self.broadcast_skin_parts( &[gameprofile.id], entity_id, skin_parts, @@ -3587,14 +3570,21 @@ impl World { } { - let held_item = existing_player.inventory.held_item().await; - let mut equipment_list = - vec![(EquipmentSlot::MAIN_HAND.discriminant(), held_item.clone())]; + let held_item = existing_player.inventory.held_item(); + let equipment_list = { + let mut equipment_list = + vec![(EquipmentSlot::MAIN_HAND.discriminant(), held_item.clone())]; - let equipment_guard = existing_player.inventory.entity_equipment.lock().await; - for (slot, item_stack) in &equipment_guard.equipment { - equipment_list.push((slot.discriminant(), item_stack.clone())); - } + let equipment_guard = existing_player + .inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (slot, item_stack) in &equipment_guard.equipment { + equipment_list.push((slot.discriminant(), item_stack.clone())); + } + equipment_list + }; let equipment: Vec<(i8, ItemStackSerializer)> = equipment_list .iter() @@ -3667,17 +3657,22 @@ impl World { .await; // Send initial weather state - let weather = self.weather.lock().await; - if weather.raining { + let (is_raining, rain_level, thunder_level) = { + let weather = self + .weather + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + ( + weather.raining, + weather.rain_level.clamp(0.0, 1.0), + weather.thunder_level.clamp(0.0, 1.0), + ) + }; + if is_raining { client .enqueue_client_packet(&CGameEvent::new(GameEvent::BeginRaining, 0.0)) .await; - // Calculate rain and thunder levels directly from public fields - let rain_level = weather.rain_level.clamp(0.0, 1.0); - let thunder_level = weather.thunder_level.clamp(0.0, 1.0); - drop(weather); - client .enqueue_client_packet(&CGameEvent::new(GameEvent::RainLevelChange, rain_level)) .await; @@ -3697,7 +3692,7 @@ impl World { .map(|bars| bars.into_iter().cloned().collect::>()); if let Some(bossbars) = player_bossbars { for bossbar in &bossbars { - player.send_bossbar(bossbar).await; + player.send_bossbar(bossbar); } } @@ -3708,7 +3703,7 @@ impl World { player.send_active_effects().await; player.breath_manager.send_air_supply(player); - self.send_player_equipment(player).await; + self.send_player_equipment(player); if let crate::net::ClientPlatform::Java(java_client) = player.client.as_ref() && server.advanced_config.recipe.send_recipes @@ -3735,21 +3730,25 @@ impl World { server.plugin_manager.fire(server, &mut event).await; if !event.cancelled { - self.broadcast_system_message(&event.join_message, false) - .await; + self.broadcast_system_message(&event.join_message, false); // TODO: Switch to structured logging, e.g. info!(player = %name, "connected") info!("{}", event.join_message.to_pretty_console()); } } - async fn send_player_equipment(&self, from: &Player) { - let held_item = from.inventory.held_item().await; + fn send_player_equipment(&self, from: &Player) { + let held_item = from.inventory.held_item(); let mut equipment_list = vec![(EquipmentSlot::MAIN_HAND.discriminant(), held_item.clone())]; - let equipment_guard = from.inventory.entity_equipment.lock().await; + let equipment_guard = from + .inventory + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (slot, item_stack) in &equipment_guard.equipment { equipment_list.push((slot.discriminant(), item_stack.clone())); } + drop(equipment_guard); let equipment: Vec<(i8, ItemStackSerializer)> = equipment_list .iter() @@ -3771,8 +3770,7 @@ impl World { &[from.get_entity().entity_uuid], &je_packet, &be_mob_equipment, - ) - .await; + ); } pub async fn send_world_info( @@ -3819,7 +3817,7 @@ impl World { chunker::update_position(player).await; // Update commands - player.set_health(20.0).await; + player.set_health(20.0); } pub async fn explode( @@ -4176,7 +4174,7 @@ impl World { ) .await; - player.living_entity.reset_state().await; + player.living_entity.reset_state(); player.send_permission_lvl_update(); @@ -4345,7 +4343,7 @@ impl World { let entity = from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, uuid); entity.read_nbt_non_mut(entity_nbt).await; - entity.init_data_tracker().await; + entity.init_data_tracker(); let base_entity = entity.get_entity(); // Clear velocity so the client does not replay the drop @@ -4758,14 +4756,12 @@ impl World { }], }; - self.broadcast_editioned(&CRemovePlayerInfo::new(&[uuid]), &bedrock_remove_player) - .await; + self.broadcast_editioned(&CRemovePlayerInfo::new(&[uuid]), &bedrock_remove_player); self.broadcast_editioned( &CRemoveEntities::new(&[entity_id.into()]), &CRemoveActor::new(VarLong(entity_id as i64)), - ) - .await; + ); if fire_event { let msg_comp = TextComponent::translate_cross( @@ -4791,9 +4787,10 @@ impl World { removed_player } - pub fn spawn_entity_non_save(&self, entity: &Arc) { + #[expect(clippy::needless_pass_by_value)] + pub fn spawn_entity_non_save(&self, entity: Arc) { let _base_entity = entity.get_entity(); - self.broadcast_entity_spawn(entity); + self.broadcast_entity_spawn(&entity); self.spawn_state.load().add_entity(self, entity.as_ref()); self.entities.rcu(|current_entities| { @@ -4803,7 +4800,7 @@ impl World { }); } - pub async fn spawn_entity(self: &Arc, entity: Arc) { + pub fn spawn_entity(self: &Arc, entity: Arc) { let mut event = crate::plugin::api::events::entity::entity_spawn::EntitySpawnEvent::new( entity.get_entity().entity_id, entity.get_entity().entity_type.id.to_string(), @@ -4811,15 +4808,15 @@ impl World { self.clone(), ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; } self.broadcast_entity_spawn(&entity); - entity.init_data_tracker().await; - self.add_entity_silent(entity).await; + entity.init_data_tracker(); + self.add_entity_silent(entity); } pub fn broadcast_entity_spawn(&self, entity: &Arc) { @@ -4837,8 +4834,8 @@ impl World { } } - #[allow(clippy::unused_async)] - pub async fn add_entity_silent(&self, entity: Arc) { + #[expect(clippy::needless_pass_by_value)] + pub fn add_entity_silent(&self, entity: Arc) { let base_entity = entity.get_entity(); // Guard against duplicate entities with the same UUID. @@ -4865,8 +4862,7 @@ impl World { }); } - #[allow(clippy::unused_async)] - pub async fn remove_entity(&self, entity: &dyn EntityBase) { + pub fn remove_entity(&self, entity: &dyn EntityBase) { let base_entity = entity.get_entity(); if base_entity .removal_reason @@ -4885,7 +4881,7 @@ impl World { }); let chunk_pos = base_entity.chunk_pos.load(); - self.broadcast_to_chunk_editioned_sync( + self.broadcast_to_chunk_editioned( chunk_pos, &CRemoveEntities::new(&[base_entity.entity_id.into()]), &CRemoveActor::new(VarLong(base_entity.entity_id as i64)), @@ -4978,22 +4974,17 @@ impl World { &[from.entity_uuid], &je_packet, &be_packet, - ) - .await; + ); } else { self.broadcast_to_chunk_except(chunk_pos, &[from.entity_uuid], &je_packet); } } - /// Sets a block and returns the old block id - /// - /// **DO NOT LOCK `world.portal_poi` BEFORE RUNNING THIS, AS IT WILL CAUSE A DEADLOCK** - #[expect(clippy::too_many_lines)] - pub async fn set_block_state( + pub fn set_block_state( self: &Arc, position: &BlockPos, block_state_id: BlockStateId, - flags: BlockFlags, + _flags: BlockFlags, ) -> BlockStateId { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let replaced_block_state_id = self @@ -5019,7 +5010,7 @@ impl World { self.unsent_block_changes .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .insert(*position, block_state_id); let old_block = Block::from_state_id(replaced_block_state_id); @@ -5027,15 +5018,16 @@ impl World { self.villager_poi .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .update_block(*position, new_block); - let block_moved = flags.contains(BlockFlags::MOVED); - let is_new_block = old_block != new_block; if is_new_block { - let mut poi = self.portal_poi.lock().await; + let mut poi = self + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if villager_poi::profession_for_block(old_block).is_some() { poi.remove(position); } @@ -5044,99 +5036,13 @@ impl World { } } - // WorldChunk.java line 305-314 if is_new_block && old_block.default_state.block_entity_type != u16::MAX - && let Some(entity) = self.get_block_entity(position) + && self.get_block_entity(position).is_some() { - entity.on_block_replaced(self.clone(), *position).await; self.remove_block_entity(position); } - // WorldChunk.java line 317 - if is_new_block && (flags.contains(BlockFlags::NOTIFY_NEIGHBORS) || block_moved) { - self.block_registry - .on_state_replaced( - self, - old_block, - position, - replaced_block_state_id, - block_moved, - ) - .await; - } - - // WorldChunk.java line 318 - if !flags.contains(BlockFlags::SKIP_BLOCK_ADDED_CALLBACK) && new_block != old_block { - self.block_registry - .on_placed( - self, - new_block, - block_state_id, - position, - replaced_block_state_id, - block_moved, - ) - .await; - let new_fluid = self.get_fluid(position); - self.block_registry - .on_placed_fluid( - self, - new_fluid, - block_state_id, - position, - replaced_block_state_id, - block_moved, - ) - .await; - } - - // Ig they do this cause it could be modified in chunkPos.setBlockState? - if self.get_block_state_id(position) == block_state_id { - if flags.contains(BlockFlags::NOTIFY_LISTENERS) { - // Mob AI update - } - - if flags.contains(BlockFlags::NOTIFY_NEIGHBORS) { - self.update_neighbors(position, None).await; - // TODO: updateComparators - } - - if !flags.contains(BlockFlags::FORCE_STATE) { - let mut new_flags = flags; - new_flags.remove(BlockFlags::NOTIFY_NEIGHBORS); - new_flags.remove(BlockFlags::NOTIFY_LISTENERS); - self.block_registry - .prepare( - self, - position, - Block::from_state_id(replaced_block_state_id), - replaced_block_state_id, - new_flags, - ) - .await; - self.block_registry - .update_neighbors( - self, - position, - Block::from_state_id(block_state_id), - new_flags, - ) - .await; - self.block_registry - .prepare( - self, - position, - Block::from_state_id(block_state_id), - block_state_id, - new_flags, - ) - .await; - } - } - - let (_chunk_coordinate, _) = position.chunk_and_chunk_relative_position(); - self.level .light_engine .update_lighting_at(&self.level, *position); @@ -5144,6 +5050,35 @@ impl World { replaced_block_state_id } + pub fn break_block( + self: &Arc, + position: &BlockPos, + _cause: Option>, + flags: BlockFlags, + ) -> Option { + let (broken_block, broken_block_state) = self.get_block_and_state_id(position); + if is_air(broken_block_state) { + return None; + } + let new_state_id = if broken_block + .properties(broken_block_state) + .and_then(|properties| { + properties + .to_props() + .into_iter() + .find(|p| p.0 == "waterlogged") + .map(|(_, value)| value == "true") + }) + .unwrap_or(false) + { + Block::WATER.default_state.id + } else { + Block::AIR.default_state.id + }; + + Some(self.set_block_state(position, new_state_id, flags)) + } + pub fn get_max_local_raw_brightness(&self, pos: &BlockPos) -> u8 { let sky_light = self.get_sky_light_level(pos); let block_light = self.get_block_light_level(pos).unwrap_or(0); @@ -5230,140 +5165,6 @@ impl World { self.level.is_fluid_tick_scheduled(block_pos, fluid) } - // Return new state - #[allow(clippy::too_many_lines)] - pub async fn break_block( - self: &Arc, - position: &BlockPos, - cause: Option>, - flags: BlockFlags, - ) -> Option { - let (broken_block, broken_block_state) = self.get_block_and_state_id(position); - if is_air(broken_block_state) { - return None; - } - let mut event = BlockBreakEvent::new( - cause.clone(), - broken_block, - *position, - 0, - !flags.contains(BlockFlags::SKIP_DROPS), - ); - - if let Some(server) = self.server.upgrade() { - server - .plugin_manager - .fire::(&server, &mut event) - .await; - } - - if !event.cancelled { - let mut flags = flags; - if event.drop { - flags.remove(BlockFlags::SKIP_DROPS); - } else { - flags.insert(BlockFlags::SKIP_DROPS); - } - let new_state_id = if broken_block - .properties(broken_block_state) - .and_then(|properties| { - properties - .to_props() - .into_iter() - .find(|p| p.0 == "waterlogged") - .map(|(_, value)| value == "true") - }) - .unwrap_or(false) - { - let mut water_props = FlowingFluidProperties::default(&Fluid::FLOWING_WATER); - water_props.level = pumpkin_data::fluid::Level::L8; - water_props.falling = Falling::False; - water_props.to_state_id(&Fluid::FLOWING_WATER) - } else { - BlockStateId::AIR - }; - - let broken_state_id = self.set_block_state(position, new_state_id, flags).await; - - // Close container screens for any players viewing this block - self.close_container_screens_at(position).await; - - let luck = cause.as_ref().map_or(0.0, |player| { - player.living_entity.get_attribute_value(&Attributes::LUCK) as f32 - }); - - if Block::from_state_id(broken_state_id) != &Block::FIRE { - let je_particles_packet = CWorldEvent::new( - WorldEvent::ParticlesDestroyBlock as i32, - *position, - broken_state_id.as_u16().into(), - false, - ); - let be_particles_packet = CLevelEvent { - event_id: VarInt(LevelEvent::ParticlesDestroyBlock as i32), - position: Vector3::new( - position.0.x as f32, - position.0.y as f32, - position.0.z as f32, - ), - data: VarInt(BlockState::to_be_network_id(broken_state_id).into()), - }; - let chunk_pos = position.chunk_position(); - match &cause { - Some(player) => { - if let ClientPlatform::Bedrock(client) = player.client.as_ref() { - client.enqueue_client_packet(&be_particles_packet).await; - } - self.broadcast_to_chunk_except_editioned( - chunk_pos, - &[player.get_entity().entity_uuid], - &je_particles_packet, - &be_particles_packet, - ) - .await; - } - None => self.broadcast_to_chunk_editioned_sync( - chunk_pos, - &je_particles_packet, - &be_particles_packet, - ), - } - } - if !flags.contains(BlockFlags::SKIP_DROPS) { - let tool = if let Some(player) = &cause { - let hand_stack = player - .inventory() - .get_stack_in_hand(pumpkin_util::Hand::Right) - .await; - (!hand_stack.is_empty()).then_some(hand_stack) - } else { - None - }; - - let is_raining = self.is_raining().await; - let is_thundering = self.is_thundering().await; - - let params = LootContextParameters { - block_state: Some(BlockState::from_id(broken_state_id)), - luck, - position: Some(pumpkin_util::math::vector3::Vector3::new( - position.0.x as f64, - position.0.y as f64, - position.0.z as f64, - )), - world_time: self.level_info.load().day_time as u64, - tool, - is_raining: Some(is_raining), - is_thundering: Some(is_thundering), - ..Default::default() - }; - block::drop_loot(self, broken_block, position, true, params).await; - } - return Some(new_state_id); - } - None - } - /// Close container screens for all players who have a container open at the given block position. pub async fn close_container_screens_at(&self, position: &BlockPos) { let players = self.players.load(); @@ -5374,7 +5175,7 @@ impl World { } } - pub async fn drop_stack(self: &Arc, pos: &BlockPos, stack: ItemStack) { + pub fn drop_stack(self: &Arc, pos: &BlockPos, stack: ItemStack) { let height = EntityType::ITEM.dimension[1] / 2.0; let spawn_pos = { let mut r = rand::rng(); @@ -5392,14 +5193,16 @@ impl World { stack.item.registry_key.to_string(), ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut item_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut item_event); } if item_event.cancelled { return; } let item_entity = Arc::new(ItemEntity::new(entity, stack)); - self.spawn_entity(item_entity).await; + self.spawn_entity(item_entity); } pub async fn strike_lightning(self: &Arc, pos: Vector3, effect_only: bool) { @@ -5436,7 +5239,7 @@ impl World { bolt.set_visual_only(effect_only); } - self.spawn_entity(lightning).await; + self.spawn_entity(lightning); } /* ItemScatterer.java */ @@ -5451,11 +5254,10 @@ impl World { f64::from(position.0.y), f64::from(position.0.z), inventory.remove_stack(i).await, - ) - .await; + ); } } - pub async fn scatter_stack(self: &Arc, x: f64, y: f64, z: f64, mut stack: ItemStack) { + pub fn scatter_stack(self: &Arc, x: f64, y: f64, z: f64, mut stack: ItemStack) { const TRIANGULAR_DEVIATION: f64 = 0.114_850_001_711_398_36; const XZ_MODE: f64 = 0.0; @@ -5482,7 +5284,7 @@ impl World { let entity = Entity::new(self.clone(), Vector3::new(x, y, z), &EntityType::ITEM); let entity = Arc::new(ItemEntity::new_with_velocity(entity, item, velocity, 10)); - self.spawn_entity(entity).await; + self.spawn_entity(entity); } } /* End ItemScatterer.java */ @@ -5677,7 +5479,7 @@ impl World { } /// Updates neighboring blocks of a block - pub async fn update_neighbors( + pub fn update_neighbors( self: &Arc, block_pos: &BlockPos, except: Option, @@ -5697,7 +5499,7 @@ impl World { *block_pos, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { continue; @@ -5706,32 +5508,29 @@ impl World { if let Some(neighbor_pumpkin_block) = self.block_registry.get_pumpkin_block(neighbor_block.id) { - neighbor_pumpkin_block - .on_neighbor_update(OnNeighborUpdateArgs { - world: self, - block: neighbor_block, - position: &neighbor_pos, - source_block, - notify: false, - }) - .await; + neighbor_pumpkin_block.on_neighbor_update(OnNeighborUpdateArgs { + world: self, + block: neighbor_block, + position: &neighbor_pos, + source_block, + notify: false, + }); } if let Some(neighbor_pumpkin_fluid) = self.block_registry.get_pumpkin_fluid(neighbor_fluid.id) { - neighbor_pumpkin_fluid - .on_neighbor_update(self, neighbor_fluid, &neighbor_pos, false) - .await; + neighbor_pumpkin_fluid.on_neighbor_update( + self, + neighbor_fluid, + &neighbor_pos, + false, + ); } } } - pub async fn update_neighbor( - self: &Arc, - neighbor_block_pos: &BlockPos, - source_block: &Block, - ) { + pub fn update_neighbor(self: &Arc, neighbor_block_pos: &BlockPos, source_block: &Block) { let neighbor_block = self.get_block(neighbor_block_pos); let mut event = crate::plugin::api::events::block::block_physics::BlockPhysicsEvent::new( @@ -5739,7 +5538,7 @@ impl World { *neighbor_block_pos, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; @@ -5748,19 +5547,17 @@ impl World { if let Some(neighbor_pumpkin_block) = self.block_registry.get_pumpkin_block(neighbor_block.id) { - neighbor_pumpkin_block - .on_neighbor_update(OnNeighborUpdateArgs { - world: self, - block: neighbor_block, - position: neighbor_block_pos, - source_block, - notify: false, - }) - .await; + neighbor_pumpkin_block.on_neighbor_update(OnNeighborUpdateArgs { + world: self, + block: neighbor_block, + position: neighbor_block_pos, + source_block, + notify: false, + }); } } - pub async fn update_from_neighbor_shapes( + pub fn update_from_neighbor_shapes( self: &Arc, state_id: BlockStateId, pos: &BlockPos, @@ -5770,23 +5567,20 @@ impl World { for direction in BlockDirection::all() { let neighbor_pos = pos.offset(direction.to_offset()); let neighbor_state_id = self.get_block_state_id(&neighbor_pos); - current_state_id = self - .block_registry - .get_state_for_neighbor_update( - self, - block, - current_state_id, - pos, - direction, - &neighbor_pos, - neighbor_state_id, - ) - .await; + current_state_id = self.block_registry.get_state_for_neighbor_update( + self, + block, + current_state_id, + pos, + direction, + &neighbor_pos, + neighbor_state_id, + ); } current_state_id } - pub async fn replace_with_state_for_neighbor_update( + pub fn replace_with_state_for_neighbor_update( self: &Arc, block_pos: &BlockPos, direction: BlockDirection, @@ -5803,25 +5597,21 @@ impl World { let neighbor_pos = block_pos.offset(direction.to_offset()); let neighbor_state_id = self.get_block_state_id(&neighbor_pos); - let new_state_id = self - .block_registry - .get_state_for_neighbor_update( - self, - block, - block_state_id, - block_pos, - direction, - &neighbor_pos, - neighbor_state_id, - ) - .await; + let new_state_id = self.block_registry.get_state_for_neighbor_update( + self, + block, + block_state_id, + block_pos, + direction, + &neighbor_pos, + neighbor_state_id, + ); if new_state_id != block_state_id { if is_air(new_state_id) { - self.break_block(block_pos, None, flags | BlockFlags::NOTIFY_ALL) - .await; + self.break_block(block_pos, None, flags | BlockFlags::NOTIFY_ALL); } else { - self.set_block_state(block_pos, new_state_id, flags).await; + self.set_block_state(block_pos, new_state_id, flags); } } } @@ -6361,11 +6151,11 @@ impl World { self.ray_trace_entities(start, end).into_iter().next() } - pub async fn raycast( + pub fn raycast( self: &Arc, start_pos: Vector3, end_pos: Vector3, - hit_check: impl AsyncFn(&BlockPos, &Arc) -> bool, + hit_check: impl Fn(&BlockPos, &Arc) -> bool, ) -> Option<(BlockPos, BlockDirection)> { if start_pos == end_pos { return None; @@ -6458,7 +6248,7 @@ impl World { } }; - if hit_check(&block, self).await { + if hit_check(&block, self) { let (collision, direction) = self.ray_outline_check(&block, from, to); if collision { if let Some(dir) = direction { @@ -6508,7 +6298,7 @@ impl World { Self::broadcast_bedrock_grouped(packet, recipients); } - pub fn broadcast_to_chunk_editioned_sync( + pub fn broadcast_to_chunk_editioned( &self, chunk_pos: Vector2, je_packet: &J, @@ -6560,7 +6350,7 @@ impl World { Self::broadcast_java_grouped(packet, recipients_by_version); } - pub async fn broadcast_to_chunk_except_editioned( + pub fn broadcast_to_chunk_except_editioned( &self, chunk_pos: Vector2, except: &[uuid::Uuid], @@ -6591,7 +6381,7 @@ impl World { let je_recipients_by_version = Self::collect_java_recipients_by_version(java_recipients.into_iter()); Self::broadcast_java_grouped(je_packet, je_recipients_by_version); - Self::broadcast_bedrock_grouped_async(be_packet, bedrock_recipients.into_iter()).await; + Self::broadcast_bedrock_grouped(be_packet, bedrock_recipients.into_iter()); } pub async fn emit_game_event(&self, event_key: impl Into, position: Vector3) { @@ -7000,7 +6790,7 @@ impl WorldPortalExt for WorldPortal { ); entity.get_entity().read_nbt_non_mut(&nbt).await; entity.read_nbt_non_mut(&nbt).await; - world.spawn_entity(entity).await; + world.spawn_entity(entity); } }); } diff --git a/crates/pumpkin/src/world/natural_spawner.rs b/crates/pumpkin/src/world/natural_spawner.rs index 72d9867eb..06ca03fcf 100644 --- a/crates/pumpkin/src/world/natural_spawner.rs +++ b/crates/pumpkin/src/world/natural_spawner.rs @@ -580,7 +580,7 @@ pub fn spawn_mobs_for_chunk_generation( entity .get_entity() .set_rotation(rand::random::() * 360., 0.); - world.spawn_entity_non_save(&entity); + world.spawn_entity_non_save(entity); success = true; } diff --git a/crates/pumpkin/src/world/portal/end.rs b/crates/pumpkin/src/world/portal/end.rs index abdd205f1..ed6a620bf 100644 --- a/crates/pumpkin/src/world/portal/end.rs +++ b/crates/pumpkin/src/world/portal/end.rs @@ -14,12 +14,12 @@ impl EndPortal { const FRAME_BLOCK: Block = Block::END_PORTAL_FRAME; const FRAME_BLOCK_ID: BlockId = Self::FRAME_BLOCK.id; - pub async fn get_new_portal(world: &Arc, pos: BlockPos) { + pub fn get_new_portal(world: &Arc, pos: BlockPos) { let mid_pos = Self::get_mid_pos(world, pos); if let Some(mid_pos) = mid_pos && Self::is_valid_portal(world, mid_pos) { - Self::create_portal(world, mid_pos).await; + Self::create_portal(world, mid_pos); } } @@ -88,16 +88,14 @@ impl EndPortal { true } - async fn create_portal(world: &Arc, pos: BlockPos) { + fn create_portal(world: &Arc, pos: BlockPos) { for x in -1..=1 { for z in -1..=1 { - world - .set_block_state( - &pos.offset(Vector3::new(x, 0, z)), - Block::END_PORTAL.default_state.id, - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + world.set_block_state( + &pos.offset(Vector3::new(x, 0, z)), + Block::END_PORTAL.default_state.id, + BlockFlags::NOTIFY_LISTENERS, + ); } } } diff --git a/crates/pumpkin/src/world/portal/mod.rs b/crates/pumpkin/src/world/portal/mod.rs index 0367abef2..889ff5a8c 100644 --- a/crates/pumpkin/src/world/portal/mod.rs +++ b/crates/pumpkin/src/world/portal/mod.rs @@ -112,13 +112,11 @@ impl PortalType { platform_pos.0.y + dy, platform_pos.0.z + dz, ); - dest_world - .set_block_state( - &target_pos, - block.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; + dest_world.set_block_state( + &target_pos, + block.default_state.id, + BlockFlags::NOTIFY_ALL, + ); } } } @@ -215,8 +213,7 @@ impl PortalType { ) .await { - NetherPortal::build_portal_frame(&dest_world, build_pos, axis, is_fallback) - .await; + NetherPortal::build_portal_frame(&dest_world, build_pos, axis, is_fallback); let new_portal = PortalSearchResult { lower_corner: build_pos, axis, diff --git a/crates/pumpkin/src/world/portal/nether.rs b/crates/pumpkin/src/world/portal/nether.rs index eb83b0211..b9529406a 100644 --- a/crates/pumpkin/src/world/portal/nether.rs +++ b/crates/pumpkin/src/world/portal/nether.rs @@ -260,7 +260,7 @@ impl NetherPortal { self.height } - pub async fn create(&self, world: &Arc) { + pub fn create(&self, world: &Arc) { let mut props = NetherPortalLikeProperties::default(&Block::NETHER_PORTAL); props.axis = self.axis; let state = props.to_state_id(&Block::NETHER_PORTAL); @@ -272,14 +272,16 @@ impl NetherPortal { ); for pos in blocks { + world.set_block_state( + &pos, + state, + BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, + ); world - .set_block_state( - &pos, - state, - BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, - ) - .await; - world.portal_poi.lock().await.add_portal(pos); + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .add_portal(pos); } } @@ -486,10 +488,13 @@ impl NetherPortal { max_y }; - let mut poi_storage = world.portal_poi.lock().await; - let portal_positions = - poi_storage.get_in_square(target_pos, search_radius, Some(poi::POI_TYPE_NETHER_PORTAL)); - drop(poi_storage); + let portal_positions = { + let mut poi_storage = world + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + poi_storage.get_in_square(target_pos, search_radius, Some(poi::POI_TYPE_NETHER_PORTAL)) + }; let mut best: Option<(PortalSearchResult, f64, i32)> = None; @@ -720,7 +725,7 @@ impl NetherPortal { true } - pub async fn build_portal_frame( + pub fn build_portal_frame( world: &Arc, lower_corner: BlockPos, axis: HorizontalAxis, @@ -758,9 +763,7 @@ impl NetherPortal { } else { air_state }; - world - .set_block_state(&pos, state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, state, BlockFlags::NOTIFY_ALL); } } } @@ -772,9 +775,7 @@ impl NetherPortal { let pos = lower_corner .offset_dir(direction.to_offset(), portal_dir) .offset_dir(BlockDirection::Up.to_offset(), height); - world - .set_block_state(&pos, obsidian_state, BlockFlags::NOTIFY_ALL) - .await; + world.set_block_state(&pos, obsidian_state, BlockFlags::NOTIFY_ALL); } } } @@ -788,14 +789,16 @@ impl NetherPortal { let pos = lower_corner .offset_dir(direction.to_offset(), x) .offset_dir(BlockDirection::Up.to_offset(), y); + world.set_block_state( + &pos, + portal_state, + BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, + ); world - .set_block_state( - &pos, - portal_state, - BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE, - ) - .await; - world.portal_poi.lock().await.add_portal(pos); + .portal_poi + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .add_portal(pos); } } } diff --git a/crates/pumpkin/src/world/raid.rs b/crates/pumpkin/src/world/raid.rs index e468c7e02..818f295dd 100644 --- a/crates/pumpkin/src/world/raid.rs +++ b/crates/pumpkin/src/world/raid.rs @@ -244,17 +244,17 @@ impl Raid { set } - pub async fn stop(&mut self, world: &World) { + pub fn stop(&mut self, world: &World) { self.active = false; self.status = RaidStatus::Stopped; - self.remove_all_players(world).await; + self.remove_all_players(world); } - pub async fn remove_all_players(&mut self, world: &World) { + pub fn remove_all_players(&mut self, world: &World) { let players = world.players.load(); for player in players.iter() { if self.players_in_raid.contains(&player.gameprofile.id) { - player.remove_bossbar(self.bossbar.uuid).await; + player.remove_bossbar(self.bossbar.uuid); } } self.players_in_raid.clear(); @@ -312,7 +312,7 @@ impl Raid { } } - pub async fn spawn_group(&mut self, world: &Arc, pos: BlockPos) { + pub fn spawn_group(&mut self, world: &Arc, pos: BlockPos) { let mut leader_set = false; let group_number = self.groups_spawned + 1; self.total_health = 0.0; @@ -346,9 +346,9 @@ impl Raid { raider.set_patrol_leader(true); let banner = create_ominous_banner(); let living = &mob.get_mob_entity().living_entity; - let mut equipment = living.entity_equipment.lock().await; - equipment.put(&EquipmentSlot::HEAD, banner.clone()); - drop(equipment); + if let Ok(mut equipment) = living.entity_equipment.try_lock() { + equipment.put(&EquipmentSlot::HEAD, banner.clone()); + } living.send_equipment_changes(&[(EquipmentSlot::HEAD, banner)]); self.group_to_leader_map.insert(group_number, uuid); leader_set = true; @@ -359,7 +359,7 @@ impl Raid { } self.join_raid(group_number, uuid, &entity_base); - world.spawn_entity(entity_base.clone()).await; + world.spawn_entity_non_save(entity_base.clone()); if *raider_type.entity_type() == EntityType::RAVAGER { let mut riding_type: Option<&'static EntityType> = None; @@ -385,7 +385,7 @@ impl Raid { raider.apply_raid_buffs(group_number, false); } self.join_raid(group_number, rider_uuid, &rider_base); - world.spawn_entity(rider_base).await; + world.spawn_entity_non_save(rider_base); } } } @@ -393,7 +393,7 @@ impl Raid { self.wave_spawn_pos = None; self.groups_spawned += 1; - self.update_bossbar(world).await; + self.update_bossbar(world); } pub fn join_raid(&mut self, wave: i32, uuid: Uuid, entity_base: &Arc) { @@ -428,74 +428,60 @@ impl Raid { None } - pub fn play_sound(&self, world: &World, sound_origin: BlockPos) { - let raid_loc = sound_origin.to_f64(); + pub fn play_sound(&self, world: &World, pos: BlockPos) { + let sound_pos = pos.to_centered_f64(); + world.play_sound_fine( + Sound::EventRaidHorn, + pumpkin_data::sound::SoundCategory::Neutral, + &sound_pos, + 64.0, + 1.0, + ); + } + + pub fn update_players(&mut self, world: &World) { + let center_f64 = self.center.to_f64(); let players = world.players.load(); + let nearby_players: HashSet = players + .iter() + .filter(|player| { + let pos = player.get_entity().pos.load(); + pos.squared_distance_to_vec(¢er_f64) <= Self::VALID_RAID_RADIUS_SQR + }) + .map(|player| player.gameprofile.id) + .collect(); + for player in players.iter() { - let player_loc = player.get_entity().pos.load(); - let dx = raid_loc.x - player_loc.x; - let dz = raid_loc.z - player_loc.z; - let dist = dx.hypot(dz); - let sound_pos = if dist > 0.001 { - Vector3::new( - player_loc.x + (13.0 / dist) * dx, - player_loc.y, - player_loc.z + (13.0 / dist) * dz, - ) - } else { - player_loc - }; - if dist <= 64.0 || self.players_in_raid.contains(&player.gameprofile.id) { - world.play_sound( - Sound::EventRaidHorn, - pumpkin_data::sound::SoundCategory::Neutral, - &sound_pos, - ); + let id = player.gameprofile.id; + let was_in = self.players_in_raid.contains(&id); + let is_in = nearby_players.contains(&id); + + if !was_in && is_in { + player.send_bossbar(&self.bossbar); + self.players_in_raid.insert(id); + } else if was_in && !is_in { + player.remove_bossbar(self.bossbar.uuid); + self.players_in_raid.remove(&id); } } } - pub async fn update_players(&mut self, world: &World) { - let center_f64 = self.center.to_f64(); - let players = world.players.load(); - let mut current_nearby = HashSet::new(); - - for player in players.iter() { - let pos = player.get_entity().pos.load(); - let dist_sq = pos.squared_distance_to_vec(¢er_f64); - if dist_sq <= Self::VALID_RAID_RADIUS_SQR && player.living_entity.health.load() > 0.0 { - current_nearby.insert(player.gameprofile.id); - if self.players_in_raid.insert(player.gameprofile.id) { - player.send_bossbar(&self.bossbar).await; - } - } - } - - let mut to_remove = Vec::new(); - for player_uuid in &self.players_in_raid { - if !current_nearby.contains(player_uuid) { - to_remove.push(*player_uuid); - } - } - - for player_uuid in to_remove { - self.players_in_raid.remove(&player_uuid); - if let Some(player) = players.iter().find(|p| p.gameprofile.id == player_uuid) { - player.remove_bossbar(self.bossbar.uuid).await; - } - } - } - - pub fn update_raiders(&mut self, world: &World) { + pub fn update_living_raiders(&mut self, world: &World) { let center_f64 = self.center.to_f64(); + let entities = world.entities.load(); for raiders in self.group_raider_map.values_mut() { let mut wave_dead = Vec::new(); for &raider_uuid in raiders.iter() { - let entity = world.get_entity_by_uuid(raider_uuid); + let entity = entities + .iter() + .find(|e| e.get_entity().entity_uuid == raider_uuid); + match entity { Some(e) => { - let is_dead = e.get_living_entity().is_none_or(|l| l.health.load() <= 0.0); + let is_dead = e + .get_living_entity() + .is_none_or(|living| living.health.load() <= 0.0); let pos = e.get_entity().pos.load(); let dist_sq = pos.squared_distance_to_vec(¢er_f64); if is_dead || dist_sq >= Self::RAID_REMOVAL_THRESHOLD_SQR { @@ -514,7 +500,7 @@ impl Raid { } } - pub async fn update_bossbar(&mut self, world: &World) { + pub fn update_bossbar(&mut self, world: &World) { let living_health = self.get_health_of_living_raiders(world); let progress = if self.total_health > 0.0 { (living_health / self.total_health).clamp(0.0, 1.0) @@ -526,12 +512,8 @@ impl Raid { let players = world.players.load(); for player in players.iter() { if self.players_in_raid.contains(&player.gameprofile.id) { - player - .update_bossbar_health(&self.bossbar.uuid, self.bossbar.health) - .await; - player - .update_bossbar_title(&self.bossbar.uuid, self.bossbar.title.clone()) - .await; + player.update_bossbar_health(&self.bossbar.uuid, self.bossbar.health); + player.update_bossbar_title(&self.bossbar.uuid, self.bossbar.title.clone()); } } } @@ -551,20 +533,20 @@ impl Raid { health } - pub async fn tick(&mut self, world: &Arc) { + pub fn tick(&mut self, world: &Arc) { if self.is_stopped() { return; } if self.status == RaidStatus::Ongoing { if world.level_info.load().difficulty == Difficulty::Peaceful { - self.stop(world).await; + self.stop(world); return; } self.ticks_active += 1; if self.ticks_active >= 48000 { - self.stop(world).await; + self.stop(world); return; } @@ -581,7 +563,7 @@ impl Raid { } if self.raid_cooldown_ticks == 300 || self.raid_cooldown_ticks % 20 == 0 { - self.update_players(world).await; + self.update_players(world); } self.raid_cooldown_ticks -= 1; @@ -591,8 +573,8 @@ impl Raid { } if self.ticks_active.is_multiple_of(20) { - self.update_players(world).await; - self.update_raiders(world); + self.update_players(world); + self.update_living_raiders(world); let alive = self.get_total_raiders_alive(); if alive > 0 && alive <= 2 { self.bossbar.title = TextComponent::translate( @@ -602,7 +584,7 @@ impl Raid { } else { self.bossbar.title = TextComponent::translate("event.minecraft.raid", []); } - self.update_bossbar(world).await; + self.update_bossbar(world); } while self.should_spawn_group() { @@ -612,7 +594,7 @@ impl Raid { .unwrap_or(self.center); self.started = true; - self.spawn_group(world, spawn_pos).await; + self.spawn_group(world, spawn_pos); self.play_sound(world, spawn_pos); } @@ -634,7 +616,7 @@ impl Raid { let players = world.players.load(); for player in players.iter() { if self.heroes_of_the_village.contains(&player.gameprofile.id) { - player.add_effect(effect.clone()).await; + player.add_effect(effect.clone()); } } } @@ -642,12 +624,12 @@ impl Raid { } else if self.is_over() { self.celebration_ticks += 1; if self.celebration_ticks >= 600 { - self.stop(world).await; + self.stop(world); return; } if self.celebration_ticks.is_multiple_of(20) { - self.update_players(world).await; + self.update_players(world); if self.is_victory() { self.bossbar.health = 0.0; self.bossbar.title = @@ -656,7 +638,7 @@ impl Raid { self.bossbar.title = TextComponent::translate("event.minecraft.raid.defeat.full", []); } - self.update_bossbar(world).await; + self.update_bossbar(world); } } } @@ -745,12 +727,12 @@ impl Raids { } } - pub async fn tick(&mut self, world: &Arc) { + pub fn tick(&mut self, world: &Arc) { self.tick_counter += 1; let mut stopped_ids = Vec::new(); for (&id, raid) in &mut self.raid_map { - raid.tick(world).await; + raid.tick(world); if raid.is_stopped() { stopped_ids.push(id); } @@ -758,7 +740,7 @@ impl Raids { for id in stopped_ids { if let Some(mut raid) = self.raid_map.remove(&id) { - raid.remove_all_players(world).await; + raid.remove_all_players(world); } } } diff --git a/crates/pumpkin/src/world/scoreboard.rs b/crates/pumpkin/src/world/scoreboard.rs index bbb002478..971044068 100644 --- a/crates/pumpkin/src/world/scoreboard.rs +++ b/crates/pumpkin/src/world/scoreboard.rs @@ -36,7 +36,7 @@ impl ScoreboardTarget for World { je_packet: &J, be_packet: &B, ) { - self.broadcast_editioned(je_packet, be_packet).await; + self.broadcast_editioned(je_packet, be_packet); } async fn send_je(&self, je_packet: &J) { @@ -64,7 +64,7 @@ impl ScoreboardTarget for std::sync::Arc { je_packet: &J, be_packet: &B, ) { - self.broadcast_editioned(je_packet, be_packet).await; + self.broadcast_editioned(je_packet, be_packet); } async fn send_je(&self, je_packet: &J) { diff --git a/crates/pumpkin/src/world/time.rs b/crates/pumpkin/src/world/time.rs index f90904885..60d659351 100644 --- a/crates/pumpkin/src/world/time.rs +++ b/crates/pumpkin/src/world/time.rs @@ -116,7 +116,7 @@ impl LevelTime { } } - pub async fn send_time(&self, world: &World) { + pub fn send_time(&self, world: &World) { let advance_time = { let lock = world.level_info.load(); lock.game_rules.advance_time @@ -124,12 +124,10 @@ impl LevelTime { let (total_ticks, partial_tick, rate) = self.pack_network_state(advance_time); - world - .broadcast_editioned( - &CUpdateTime::new_clock(self.world_age, 0, total_ticks, partial_tick, rate), - &CSetTime::new(self.time_of_day as _), // TODO do we need to tell bedrock that time is frozen? - ) - .await; + world.broadcast_editioned( + &CUpdateTime::new_clock(self.world_age, 0, total_ticks, partial_tick, rate), + &CSetTime::new(self.time_of_day as _), // TODO do we need to tell bedrock that time is frozen? + ); } pub fn add_time(&mut self, time: i64) {