refactor(entities): from async to sync Part 2

This commit is contained in:
Alexander Medvedev 2026-08-26 16:45:10 +02:00
parent 8ee11b06ce
commit 5d841d6d3b
209 changed files with 7557 additions and 7959 deletions

View File

@ -10,6 +10,7 @@
//! Ender chests track when players open and close them to properly //! Ender chests track when players open and close them to properly
//! manage the viewer count for animation purposes. //! manage the viewer count for animation purposes.
use std::sync::{Mutex, RwLock};
use std::{any::Any, pin::Pin, sync::Arc}; use std::{any::Any, pin::Pin, sync::Arc};
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
@ -17,7 +18,6 @@ use pumpkin_world::{
block::viewer::ViewerCountTracker, block::viewer::ViewerCountTracker,
inventory::{Clearable, Inventory, InventoryFuture}, inventory::{Clearable, Inventory, InventoryFuture},
}; };
use tokio::sync::{Mutex, RwLock};
/// A player's ender chest inventory. /// A player's ender chest inventory.
/// ///
@ -55,21 +55,33 @@ impl EnderChestInventory {
/// Sets the viewer count tracker for this inventory. /// Sets the viewer count tracker for this inventory.
/// ///
/// Used to animate the ender chest lid based on viewers. /// Used to animate the ender chest lid based on viewers.
pub async fn set_tracker(&self, tracker: Arc<ViewerCountTracker>) { pub fn set_tracker(&self, tracker: Arc<ViewerCountTracker>) {
let old = self.tracker.lock().await.replace(tracker); let old = self
.tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.replace(tracker);
if let Some(old_tracker) = old { if let Some(old_tracker) = old {
old_tracker.close_container(); old_tracker.close_container();
} }
} }
/// Checks if this inventory has a tracker set. /// Checks if this inventory has a tracker set.
pub async fn has_tracker(&self) -> bool { pub fn has_tracker(&self) -> bool {
self.tracker.lock().await.is_some() self.tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_some()
} }
/// Checks if the given tracker is associated with this inventory. /// Checks if the given tracker is associated with this inventory.
pub async fn is_tracker(&self, tracker: &Arc<ViewerCountTracker>) -> bool { pub fn is_tracker(&self, tracker: &Arc<ViewerCountTracker>) -> bool {
if let Some(value) = self.tracker.lock().await.as_ref() { if let Some(value) = self
.tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.as_ref()
{
return Arc::ptr_eq(value, tracker); return Arc::ptr_eq(value, tracker);
} }
false false
@ -83,14 +95,20 @@ impl Inventory for EnderChestInventory {
fn is_empty(&self) -> InventoryFuture<'_, bool> { fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move { Box::pin(async move {
let items = self.items.read().await; let items = self
.items
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
items.iter().all(ItemStack::is_empty) items.iter().all(ItemStack::is_empty)
}) })
} }
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let items = self.items.read().await; let items = self
.items
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
items items
.get(slot) .get(slot)
.cloned() .cloned()
@ -100,7 +118,10 @@ impl Inventory for EnderChestInventory {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut items = self.items.write().await; let mut items = self
.items
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if slot < Self::INVENTORY_SIZE { if slot < Self::INVENTORY_SIZE {
std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone())
} else { } else {
@ -111,7 +132,10 @@ impl Inventory for EnderChestInventory {
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut items = self.items.write().await; let mut items = self
.items
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if slot < Self::INVENTORY_SIZE && !items[slot].is_empty() && amount > 0 { if slot < Self::INVENTORY_SIZE && !items[slot].is_empty() && amount > 0 {
items[slot].split(amount) items[slot].split(amount)
} else { } else {
@ -122,7 +146,10 @@ impl Inventory for EnderChestInventory {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
let mut items = self.items.write().await; let mut items = self
.items
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if slot < Self::INVENTORY_SIZE { if slot < Self::INVENTORY_SIZE {
items[slot] = stack; items[slot] = stack;
} }
@ -131,7 +158,12 @@ impl Inventory for EnderChestInventory {
fn on_open(&self) -> InventoryFuture<'_, ()> { fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
if let Some(tracker) = self.tracker.lock().await.as_ref() { if let Some(tracker) = self
.tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.as_ref()
{
tracker.open_container(); tracker.open_container();
} }
}) })
@ -139,7 +171,11 @@ impl Inventory for EnderChestInventory {
fn on_close(&self) -> InventoryFuture<'_, ()> { fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
let tracker = self.tracker.lock().await.take(); let tracker = self
.tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.take();
if let Some(tracker) = tracker { if let Some(tracker) = tracker {
tracker.close_container(); tracker.close_container();
} }
@ -156,7 +192,10 @@ impl Inventory for EnderChestInventory {
impl Clearable for EnderChestInventory { impl Clearable for EnderChestInventory {
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move { Box::pin(async move {
let mut items = self.items.write().await; let mut items = self
.items
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
items.fill_with(|| ItemStack::EMPTY.clone()); items.fill_with(|| ItemStack::EMPTY.clone());
}) })
} }
@ -222,24 +261,24 @@ mod tests {
let tracker1 = Arc::new(ViewerCountTracker::new()); let tracker1 = Arc::new(ViewerCountTracker::new());
let tracker2 = Arc::new(ViewerCountTracker::new()); let tracker2 = Arc::new(ViewerCountTracker::new());
ec.set_tracker(tracker1.clone()).await; ec.set_tracker(tracker1.clone());
assert!(ec.has_tracker().await); assert!(ec.has_tracker());
assert!(ec.is_tracker(&tracker1).await); assert!(ec.is_tracker(&tracker1));
assert!(!ec.is_tracker(&tracker2).await); assert!(!ec.is_tracker(&tracker2));
ec.on_open().await; ec.on_open().await;
assert_eq!(tracker1.get_viewer_count(), 1); assert_eq!(tracker1.get_viewer_count(), 1);
// Setting a new tracker while one is open should close the old tracker // Setting a new tracker while one is open should close the old tracker
ec.set_tracker(tracker2.clone()).await; ec.set_tracker(tracker2.clone());
assert_eq!(tracker1.get_viewer_count(), 0); assert_eq!(tracker1.get_viewer_count(), 0);
assert!(ec.is_tracker(&tracker2).await); assert!(ec.is_tracker(&tracker2));
ec.on_open().await; ec.on_open().await;
assert_eq!(tracker2.get_viewer_count(), 1); assert_eq!(tracker2.get_viewer_count(), 1);
ec.on_close().await; ec.on_close().await;
assert_eq!(tracker2.get_viewer_count(), 0); assert_eq!(tracker2.get_viewer_count(), 0);
assert!(!ec.has_tracker().await); assert!(!ec.has_tracker());
} }
} }

View File

@ -404,6 +404,40 @@ impl PlayerInventory {
inv.swap(selected, slot); inv.swap(selected, slot);
} }
/// Gets the item in the specified slot (synchronously).
pub fn get_slot(&self, slot: usize) -> ItemStack {
if slot < Self::MAIN_SIZE {
let inv = self
.main_inventory
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
inv[slot].clone()
} else if let Some(slot_type) = self.equipment_slots.get(&slot) {
self.entity_equipment
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get(slot_type)
} else {
ItemStack::EMPTY.clone()
}
}
/// Sets the item in the specified slot (synchronously).
pub fn set_slot(&self, slot: usize, stack: ItemStack) {
if slot < Self::MAIN_SIZE {
let mut inv = self
.main_inventory
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
inv[slot] = stack;
} else if let Some(slot_type) = self.equipment_slots.get(&slot) {
self.entity_equipment
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.put(slot_type, stack);
}
}
/// Gives a stack to the player or drops it if inventory is full. /// Gives a stack to the player or drops it if inventory is full.
pub async fn offer_or_drop_stack(&self, stack: ItemStack, player: &dyn InventoryPlayer) { pub async fn offer_or_drop_stack(&self, stack: ItemStack, player: &dyn InventoryPlayer) {
self.offer(stack, true, player).await; self.offer(stack, true, player).await;

View File

@ -233,13 +233,11 @@ impl BedBlock {
world.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS); world.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS);
world.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS); world.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS);
world world.explode(
.explode(
bed_head_pos.to_centered_f64(), bed_head_pos.to_centered_f64(),
5.0, 5.0,
crate::world::ExplosionInteraction::Block, crate::world::ExplosionInteraction::Block,
) );
.await;
return BlockActionResult::SuccessServer; return BlockActionResult::SuccessServer;
} }

View File

@ -47,6 +47,18 @@ fn fire_cauldron_change(
!event.cancelled !event.cancelled
} }
fn give_item_or_drop(
player: &crate::entity::player::Player,
world: &std::sync::Arc<crate::world::World>,
item: &'static Item,
) {
let mut stack = ItemStack::new(1, item);
let was_added = player.inventory.insert_stack_anywhere(&mut stack);
if !was_added && !stack.is_empty() {
world.drop_stack(&player.position().to_block_pos(), stack);
}
}
impl BlockBehaviour for CauldronBlock { impl BlockBehaviour for CauldronBlock {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
@ -78,13 +90,7 @@ impl BlockBehaviour for CauldronBlock {
&args.position.to_f64(), &args.position.to_f64(),
); );
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, &Item::BUCKET);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref())
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} else if item_id == Item::LAVA_BUCKET.id { } else if item_id == Item::LAVA_BUCKET.id {
args.world.set_block_state( args.world.set_block_state(
@ -98,13 +104,7 @@ impl BlockBehaviour for CauldronBlock {
&args.position.to_f64(), &args.position.to_f64(),
); );
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, &Item::BUCKET);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref())
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} else if item_id == Item::POWDER_SNOW_BUCKET.id { } else if item_id == Item::POWDER_SNOW_BUCKET.id {
let state_id = Block::POWDER_SNOW_CAULDRON let state_id = Block::POWDER_SNOW_CAULDRON
@ -118,13 +118,7 @@ impl BlockBehaviour for CauldronBlock {
&args.position.to_f64(), &args.position.to_f64(),
); );
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, &Item::BUCKET);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref())
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} else if item_id == Item::POTION.id { } else if item_id == Item::POTION.id {
let state_id = Block::WATER_CAULDRON let state_id = Block::WATER_CAULDRON
@ -138,16 +132,7 @@ impl BlockBehaviour for CauldronBlock {
&args.position.to_f64(), &args.position.to_f64(),
); );
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, &Item::GLASS_BOTTLE);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(
ItemStack::new(1, &Item::GLASS_BOTTLE),
player.as_ref(),
)
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} }
} }
@ -187,13 +172,7 @@ impl BlockBehaviour for CauldronBlock {
args.world args.world
.play_sound(sound, SoundCategory::Blocks, &args.position.to_f64()); .play_sound(sound, SoundCategory::Blocks, &args.position.to_f64());
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, result_item);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(ItemStack::new(1, result_item), player.as_ref())
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} }
} }
@ -218,16 +197,7 @@ impl BlockBehaviour for CauldronBlock {
&args.position.to_f64(), &args.position.to_f64(),
); );
args.item_stack.decrement_unless_creative(gamemode, 1); args.item_stack.decrement_unless_creative(gamemode, 1);
let player = Arc::clone(args.player); give_item_or_drop(args.player, args.world, &Item::GLASS_BOTTLE);
tokio::spawn(async move {
player
.inventory
.offer_or_drop_stack(
ItemStack::new(1, &Item::GLASS_BOTTLE),
player.as_ref(),
)
.await;
});
return BlockActionResult::Success; return BlockActionResult::Success;
} }
} }

View File

@ -35,7 +35,7 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
) -> BoxFuture<'a, Option<SharedScreenHandler>> { ) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move { Box::pin(async move {
if let Some(tracker) = &self.tracker { if let Some(tracker) = &self.tracker {
self.inventory.set_tracker(tracker.clone()).await; self.inventory.set_tracker(tracker.clone());
} }
let handler = let handler =
create_generic_9x3(sync_id, player_inventory, self.inventory.clone()).await; create_generic_9x3(sync_id, player_inventory, self.inventory.clone()).await;

View File

@ -41,11 +41,7 @@ impl crate::block::BlockBehaviour for PumpkinBlock {
ItemStack::new(4, &Item::PUMPKIN_SEEDS), ItemStack::new(4, &Item::PUMPKIN_SEEDS),
)); ));
args.world.spawn_entity(item_entity); args.world.spawn_entity(item_entity);
let player = Arc::clone(args.player); args.player.damage_item_in_slot(args.equipment_slot, 1);
let slot = args.equipment_slot.clone();
tokio::spawn(async move {
player.damage_item_in_slot(&slot, 1).await;
});
BlockActionResult::Consume BlockActionResult::Consume
} }
} }

View File

@ -28,7 +28,11 @@ impl BlockBehaviour for DaylightDetectorBlock {
} }
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let player_abilities = args.player.abilities.blocking_lock(); let player_abilities = args
.player
.abilities
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !player_abilities.allow_modify_world { if !player_abilities.allow_modify_world {
return BlockActionResult::Pass; return BlockActionResult::Pass;
} }

View File

@ -669,7 +669,7 @@ impl DispenserBlock {
item: &mut ItemStack, item: &mut ItemStack,
) { ) {
let front = Self::target_position(ctx); let front = Self::target_position(ctx);
let Some(filled) = try_pickup_fluid_at(ctx.world, front).await else { let Some(filled) = try_pickup_fluid_at(ctx.world, front) else {
Self::drop_item(ctx, item); Self::drop_item(ctx, item);
return; return;
}; };
@ -717,7 +717,6 @@ impl DispenserBlock {
*ctx.position, *ctx.position,
ctx.facing.to_block_direction(), ctx.facing.to_block_direction(),
) )
.await
}; };
if emptied { if emptied {
@ -737,7 +736,7 @@ impl DispenserBlock {
true true
} else { } else {
Ignition::ignite_block( Ignition::ignite_block(
|world: Arc<World>, pos: BlockPos, new_state_id: BlockStateId| async move { |world: Arc<World>, pos: BlockPos, new_state_id: BlockStateId| {
world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL);
}, },
ctx.world, ctx.world,
@ -745,7 +744,6 @@ impl DispenserBlock {
front, front,
front_block, front_block,
) )
.await
}; };
if ignited { if ignited {
@ -761,7 +759,7 @@ impl DispenserBlock {
let front = Self::target_position(ctx); let front = Self::target_position(ctx);
let front_block = ctx.world.get_block(&front); let front_block = ctx.world.get_block(&front);
if try_wax_block(ctx.world, front, front_block).await { if try_wax_block(ctx.world, front, front_block) {
item.decrement(1); item.decrement(1);
Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense); Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense);
} else { } else {

View File

@ -54,13 +54,9 @@ impl BlockBehaviour for RespawnAnchorBlock {
if args.world.dimension != Dimension::THE_NETHER { if args.world.dimension != Dimension::THE_NETHER {
args.world args.world
.break_block(args.position, None, BlockFlags::SKIP_DROPS); .break_block(args.position, None, BlockFlags::SKIP_DROPS);
let world = Arc::clone(args.world);
let center_pos = args.position.to_centered_f64(); let center_pos = args.position.to_centered_f64();
tokio::spawn(async move { args.world
world .explode(center_pos, 5.0, crate::world::ExplosionInteraction::Block);
.explode(center_pos, 5.0, crate::world::ExplosionInteraction::Block)
.await;
});
return BlockActionResult::SuccessServer; return BlockActionResult::SuccessServer;
} }

View File

@ -72,12 +72,7 @@ pub trait FlowingFluid: Send + Sync {
/// 3. Triggering fluid spread to adjacent positions /// 3. Triggering fluid spread to adjacent positions
/// ///
/// Sources (level 8, non-falling) always spread without state changes. /// Sources (level 8, non-falling) always spread without state changes.
async fn on_scheduled_tick_internal( fn on_scheduled_tick_internal(&self, world: &Arc<World>, fluid: &Fluid, block_pos: &BlockPos) {
&self,
world: &Arc<World>,
fluid: &Fluid,
block_pos: &BlockPos,
) {
let current_block_state_id = world.get_block_state_id(block_pos); let current_block_state_id = world.get_block_state_id(block_pos);
let block = Block::from_state_id(current_block_state_id); let block = Block::from_state_id(current_block_state_id);
@ -96,7 +91,7 @@ pub trait FlowingFluid: Send + Sync {
// Update state if non-source // Update state if non-source
if !is_source && !waterlogged { if !is_source && !waterlogged {
let new_fluid_state = self.get_new_liquid(world, fluid, block_pos).await; let new_fluid_state = self.get_new_liquid(world, fluid, block_pos);
if let Some(new_state) = new_fluid_state { if let Some(new_state) = new_fluid_state {
let new_state_id = new_state.to_state_id(fluid); let new_state_id = new_state.to_state_id(fluid);
@ -127,8 +122,7 @@ pub trait FlowingFluid: Send + Sync {
} }
// Then, spread using the appropriate state // Then, spread using the appropriate state
self.try_flow(world, fluid, block_pos, &state_for_spreading) self.try_flow(world, fluid, block_pos, &state_for_spreading);
.await;
} }
/// Attempts to flow fluid from a position, prioritizing downward flow. /// Attempts to flow fluid from a position, prioritizing downward flow.
@ -138,7 +132,7 @@ pub trait FlowingFluid: Send + Sync {
/// 2. Sides - spread horizontally using pathfinding /// 2. Sides - spread horizontally using pathfinding
/// ///
/// Sources with 3+ adjacent sources also spread to sides when flowing down. /// Sources with 3+ adjacent sources also spread to sides when flowing down.
async fn try_flow( fn try_flow(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -153,24 +147,23 @@ pub trait FlowingFluid: Send + Sync {
// Try to flow down first // Try to flow down first
if is_hole { if is_hole {
let falling_props = self.get_flowing(fluid, Level::L8, true); let falling_props = self.get_flowing(fluid, Level::L8, true);
self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid)) self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid));
.await;
// Check if we should also spread to sides // Check if we should also spread to sides
if props.level == Level::L8 && props.falling == Falling::False { if props.level == Level::L8 && props.falling == Falling::False {
let source_count = self.count_source_neighbors(world, fluid, block_pos).await; let source_count = self.count_source_neighbors(world, fluid, block_pos);
if source_count >= 3 { if source_count >= 3 {
self.flow_to_sides(world, fluid, block_pos, props).await; self.flow_to_sides(world, fluid, block_pos, props);
} }
} }
return; return;
} }
// Check if fluid should flow to the side(s) // Check if fluid should flow to the side(s)
self.flow_to_sides(world, fluid, block_pos, props).await; self.flow_to_sides(world, fluid, block_pos, props);
} }
async fn count_source_neighbors( fn count_source_neighbors(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -205,7 +198,7 @@ pub trait FlowingFluid: Send + Sync {
/// ///
/// # Returns /// # Returns
/// New fluid properties, or None if fluid should drain /// New fluid properties, or None if fluid should drain
async fn get_new_liquid( fn get_new_liquid(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -295,7 +288,7 @@ pub trait FlowingFluid: Send + Sync {
/// - Fluid tick scheduling for non-source blocks /// - Fluid tick scheduling for non-source blocks
/// ///
/// Called by `spread_to` implementations after fluid-specific pre-checks. /// Called by `spread_to` implementations after fluid-specific pre-checks.
async fn apply_spread( fn apply_spread(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -318,9 +311,7 @@ pub trait FlowingFluid: Send + Sync {
// Check for infinite source formation before quiescence checks // Check for infinite source formation before quiescence checks
if !current_is_source && self.can_convert_to_source(world) { if !current_is_source && self.can_convert_to_source(world) {
let should_convert = self let should_convert = self.check_infinite_source_formation(world, fluid, pos);
.check_infinite_source_formation(world, fluid, pos)
.await;
if should_convert { if should_convert {
let source_props = self.get_source(fluid, false); let source_props = self.get_source(fluid, false);
@ -355,7 +346,7 @@ pub trait FlowingFluid: Send + Sync {
&pumpkin_data::Block::WATER, &pumpkin_data::Block::WATER,
); );
if let Some(server) = world.server.upgrade() { 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 { if event.cancelled {
return; return;
@ -365,9 +356,7 @@ pub trait FlowingFluid: Send + Sync {
// Check for infinite source formation after placing new fluid // Check for infinite source formation after placing new fluid
if self.can_convert_to_source(world) { if self.can_convert_to_source(world) {
let should_convert = self let should_convert = self.check_infinite_source_formation(world, fluid, pos);
.check_infinite_source_formation(world, fluid, pos)
.await;
if should_convert { if should_convert {
let source_props = self.get_source(fluid, false); let source_props = self.get_source(fluid, false);
@ -396,7 +385,7 @@ pub trait FlowingFluid: Send + Sync {
/// ///
/// # Returns /// # Returns
/// `true` if position should convert to a source block /// `true` if position should convert to a source block
async fn check_infinite_source_formation( fn check_infinite_source_formation(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -444,23 +433,16 @@ pub trait FlowingFluid: Send + Sync {
/// ///
/// Default implementation delegates to `apply_spread`. Implementations like /// Default implementation delegates to `apply_spread`. Implementations like
/// lava can override to add fluid-specific logic (e.g., water -> stone conversion). /// lava can override to add fluid-specific logic (e.g., water -> stone conversion).
async fn spread_to( fn spread_to(&self, world: &Arc<World>, fluid: &Fluid, pos: &BlockPos, state_id: BlockStateId) {
&self,
world: &Arc<World>,
fluid: &Fluid,
pos: &BlockPos,
state_id: BlockStateId,
) {
let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); let new_props = FlowingFluidProperties::from_state_id(state_id, fluid);
self.apply_spread(world, fluid, pos, state_id, new_props) self.apply_spread(world, fluid, pos, state_id, new_props);
.await;
} }
/// Spreads fluid horizontally to adjacent positions using pathfinding. /// Spreads fluid horizontally to adjacent positions using pathfinding.
/// ///
/// Uses `get_spread` to find optimal flow directions (shortest distance to holes) /// Uses `get_spread` to find optimal flow directions (shortest distance to holes)
/// and the computed fluid state for each target position. /// and the computed fluid state for each target position.
async fn flow_to_sides( fn flow_to_sides(
&self, &self,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -479,12 +461,12 @@ pub trait FlowingFluid: Send + Sync {
return; 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);
for &(direction, state_id) in spread_dirs.iter().take(count) { for &(direction, state_id) in spread_dirs.iter().take(count) {
let side_pos = block_pos.offset(direction.to_offset()); 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);
} }
} }
} }

View File

@ -6,7 +6,6 @@ use crate::{
}; };
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, BlockState, BlockStateId, Block, BlockDirection, BlockState, BlockStateId,
block_properties::blocks_movement,
damage::DamageType, damage::DamageType,
dimension::Dimension, dimension::Dimension,
fluid::{Falling, Fluid, FluidProperties, Level}, fluid::{Falling, Fluid, FluidProperties, Level},
@ -160,12 +159,7 @@ impl FluidBehaviour for FlowingLava {
} }
fn on_scheduled_tick(&self, world: &Arc<World>, _fluid: &Fluid, block_pos: &BlockPos) { fn on_scheduled_tick(&self, world: &Arc<World>, _fluid: &Fluid, block_pos: &BlockPos) {
let world = world.clone(); Self.on_scheduled_tick_internal(world, &Fluid::FLOWING_LAVA, block_pos);
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( fn on_neighbor_update(
@ -193,16 +187,13 @@ impl FluidBehaviour for FlowingLava {
} }
fn random_tick(&self, _fluid: &Fluid, world: &Arc<World>, block_pos: &BlockPos) { fn random_tick(&self, _fluid: &Fluid, world: &Arc<World>, block_pos: &BlockPos) {
let world = world.clone(); if !Self::can_spread_fire_around(world, block_pos) {
let block_pos = *block_pos;
tokio::spawn(async move {
if !Self::can_spread_fire_around(&world, &block_pos) {
return; return;
} }
let passes = rand::random_range(0..3); let passes = rand::random_range(0..3);
if passes > 0 { if passes > 0 {
let mut test_pos = block_pos; let mut test_pos = *block_pos;
for _ in 0..passes { for _ in 0..passes {
test_pos = test_pos.offset(Vector3::new( test_pos = test_pos.offset(Vector3::new(
@ -211,20 +202,18 @@ impl FluidBehaviour for FlowingLava {
rand::random_range(-1..=1), rand::random_range(-1..=1),
)); ));
if !world.is_loaded(&test_pos) { let (block, _) = world.get_block_and_state_id(&test_pos);
if block.id == Block::AIR.id {
if Self::has_flammable_neighbours(world, &test_pos) {
world.set_block_state(
&test_pos,
Block::FIRE.default_state.id,
BlockFlags::NOTIFY_ALL,
);
return; return;
} }
} else if block.is_solid() {
let Some(block_state) = world.get_block_state_if_loaded(&test_pos) else {
return;
};
if block_state.is_air() {
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()) {
return; return;
} }
} }
@ -241,20 +230,15 @@ impl FluidBehaviour for FlowingLava {
} }
let above_pos = test_pos.up(); let above_pos = test_pos.up();
if !world.is_loaded(&above_pos) {
return;
}
if world if world
.get_block_state_if_loaded(&above_pos) .get_block_state_if_loaded(&above_pos)
.is_some_and(BlockState::is_air) .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); Self::ignite_fire_if_possible(world, &above_pos);
} }
} }
} }
});
} }
} }
@ -291,13 +275,7 @@ impl FlowingFluid for FlowingLava {
world.level_info.load().game_rules.lava_source_conversion world.level_info.load().game_rules.lava_source_conversion
} }
async fn spread_to( fn spread_to(&self, world: &Arc<World>, fluid: &Fluid, pos: &BlockPos, state_id: BlockStateId) {
&self,
world: &Arc<World>,
fluid: &Fluid,
pos: &BlockPos,
state_id: BlockStateId,
) {
let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); let new_props = FlowingFluidProperties::from_state_id(state_id, fluid);
let current_state_id = world.get_block_state_id(pos); let current_state_id = world.get_block_state_id(pos);
let block = Block::from_state_id(current_state_id); let block = Block::from_state_id(current_state_id);
@ -317,7 +295,6 @@ impl FlowingFluid for FlowingLava {
} }
// Delegate quiescence, replacement and scheduling to the shared helper // Delegate quiescence, replacement and scheduling to the shared helper
self.apply_spread(world, fluid, pos, state_id, new_props) self.apply_spread(world, fluid, pos, state_id, new_props);
.await;
} }
} }

View File

@ -30,7 +30,7 @@ fn is_hole(world: &Arc<World>, fluid: &Fluid, pos: &BlockPos) -> bool {
/// - Holes (downward flow opportunities) get distance 0 priority /// - Holes (downward flow opportunities) get distance 0 priority
/// - All directions with equal minimum distance are returned /// - All directions with equal minimum distance are returned
/// - Returns up to 4 directions with their computed fluid states /// - Returns up to 4 directions with their computed fluid states
pub async fn get_spread<T: FlowingFluid + Sync + ?Sized>( pub fn get_spread<T: FlowingFluid + Sync + ?Sized>(
fluid_impl: &T, fluid_impl: &T,
world: &Arc<World>, world: &Arc<World>,
fluid: &Fluid, fluid: &Fluid,
@ -63,7 +63,7 @@ pub async fn get_spread<T: FlowingFluid + Sync + ?Sized>(
} }
// Skip if no valid fluid state for this position // Skip if no valid fluid state for this position
let Some(new_fluid_props) = fluid_impl.get_new_liquid(world, fluid, &side_pos).await else { let Some(new_fluid_props) = fluid_impl.get_new_liquid(world, fluid, &side_pos) else {
continue; continue;
}; };

View File

@ -36,12 +36,7 @@ impl FluidBehaviour for FlowingWater {
} }
fn on_scheduled_tick(&self, world: &Arc<World>, _fluid: &Fluid, block_pos: &BlockPos) { fn on_scheduled_tick(&self, world: &Arc<World>, _fluid: &Fluid, block_pos: &BlockPos) {
let world = world.clone(); Self.on_scheduled_tick_internal(world, &Fluid::FLOWING_WATER, block_pos);
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( fn on_neighbor_update(

View File

@ -401,7 +401,7 @@ pub struct BlockEvent {
pub data: u8, pub data: u8,
} }
pub async fn drop_loot( pub fn drop_loot(
world: &Arc<World>, world: &Arc<World>,
block: &Block, block: &Block,
pos: &BlockPos, pos: &BlockPos,
@ -419,7 +419,7 @@ pub async fn drop_loot(
cancelled: false, cancelled: false,
}; };
if let Some(server) = world.server.upgrade() { 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 { if !event.cancelled {
for stack in event.items { for stack in event.items {
@ -440,7 +440,7 @@ pub async fn drop_loot(
exp: amount, exp: amount,
}; };
if let Some(server) = world.server.upgrade() { 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.exp > 0 { if event.exp > 0 {
ExperienceOrbEntity::spawn(world, pos.to_f64(), event.exp as u32); ExperienceOrbEntity::spawn(world, pos.to_f64(), event.exp as u32);

View File

@ -521,7 +521,8 @@ impl EntitySelectorPredicate {
let has_tag = entity let has_tag = entity
.get_entity() .get_entity()
.scoreboard_tags .scoreboard_tags
.blocking_lock() .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.contains(expected_tag); .contains(expected_tag);
has_tag ^ invert has_tag ^ invert
} }
@ -567,8 +568,7 @@ impl EntitySelectorPredicate {
} }
Self::Nbt(expected_nbt, invert) => { Self::Nbt(expected_nbt, invert) => {
let mut actual_nbt = NbtCompound::default(); let mut actual_nbt = NbtCompound::default();
// write_nbt is asynchronous, so we can poll it synchronously because it does not do IO. entity.write_nbt(&mut actual_nbt);
futures::executor::block_on(entity.write_nbt(&mut actual_nbt));
matches_nbt_compound(expected_nbt, &actual_nbt) ^ invert matches_nbt_compound(expected_nbt, &actual_nbt) ^ invert
} }
Self::Predicate(_predicate_id, invert) => { Self::Predicate(_predicate_id, invert) => {

View File

@ -314,7 +314,7 @@ async fn perform(
} }
.create_without_context_args_slice(&[ .create_without_context_args_slice(&[
first_advancement.name(), first_advancement.name(),
first_player.get_display_name().await, first_player.get_display_name(),
])) ]))
} else { } else {
Err(match action { Err(match action {
@ -333,7 +333,7 @@ async fn perform(
} }
.create_without_context_args_slice(&[ .create_without_context_args_slice(&[
TextComponent::text(advancements.len().to_string()), TextComponent::text(advancements.len().to_string()),
first_player.get_display_name().await, first_player.get_display_name(),
])) ]))
} else { } else {
Err(match action { Err(match action {
@ -350,10 +350,7 @@ async fn perform(
if let [first_player] = targets { if let [first_player] = targets {
TextComponent::translate( TextComponent::translate(
format!("{}.one.to.one.success", action.get_key()), format!("{}.one.to.one.success", action.get_key()),
[ [first_advancement.name(), first_player.get_display_name()],
first_advancement.name(),
first_player.get_display_name().await,
],
) )
} else { } else {
TextComponent::translate( TextComponent::translate(
@ -369,7 +366,7 @@ async fn perform(
format!("{}.many.to.one.success", action.get_key()), format!("{}.many.to.one.success", action.get_key()),
[ [
TextComponent::text(advancements.len().to_string()), TextComponent::text(advancements.len().to_string()),
first.get_display_name().await, first.get_display_name(),
], ],
) )
} else { } else {
@ -431,7 +428,7 @@ pub async fn perform_criterion(
.create_without_context_args_slice(&[ .create_without_context_args_slice(&[
TextComponent::text(criterion.to_owned()), TextComponent::text(criterion.to_owned()),
advancement.name(), advancement.name(),
first_player.get_display_name().await, first_player.get_display_name(),
])) ]))
} else { } else {
Err(match action { Err(match action {
@ -451,7 +448,7 @@ pub async fn perform_criterion(
[ [
TextComponent::text(criterion.to_owned()), TextComponent::text(criterion.to_owned()),
advancement.name(), advancement.name(),
first_player.get_display_name().await, first_player.get_display_name(),
], ],
) )
} else { } else {

View File

@ -130,7 +130,7 @@ async fn clear_command_text_output(
([target], true, _) => Err(TextComponent::translate_cross( ([target], true, _) => Err(TextComponent::translate_cross(
translation::java::CLEAR_FAILED_SINGLE, translation::java::CLEAR_FAILED_SINGLE,
translation::bedrock::COMMANDS_CLEAR_FAILURE, translation::bedrock::COMMANDS_CLEAR_FAILURE,
[target.get_display_name().await], [target.get_display_name()],
)), )),
(targets, true, _) => Err(TextComponent::translate_cross( (targets, true, _) => Err(TextComponent::translate_cross(
translation::java::CLEAR_FAILED_MULTIPLE, translation::java::CLEAR_FAILED_MULTIPLE,
@ -142,7 +142,7 @@ async fn clear_command_text_output(
translation::java::COMMANDS_CLEAR_SUCCESS_SINGLE, translation::java::COMMANDS_CLEAR_SUCCESS_SINGLE,
[ [
TextComponent::text(item_count.to_string()), TextComponent::text(item_count.to_string()),
target.get_display_name().await, target.get_display_name(),
], ],
)), )),
(targets, false, false) => Ok(TextComponent::translate_cross( (targets, false, false) => Ok(TextComponent::translate_cross(
@ -158,7 +158,7 @@ async fn clear_command_text_output(
translation::java::COMMANDS_CLEAR_TEST_SINGLE, translation::java::COMMANDS_CLEAR_TEST_SINGLE,
[ [
TextComponent::text(item_count.to_string()), TextComponent::text(item_count.to_string()),
target.get_display_name().await, target.get_display_name(),
], ],
)), )),
(targets, false, true) => Ok(TextComponent::translate_cross( (targets, false, true) => Ok(TextComponent::translate_cross(

View File

@ -87,7 +87,7 @@ impl CommandExecutor for LocationExecutor {
None, None,
); );
send_damage_result(sender, success, amount, target.get_display_name().await).await send_damage_result(sender, success, amount, target.get_display_name()).await
}) })
} }
} }
@ -127,7 +127,7 @@ impl CommandExecutor for EntityExecutor {
cause.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 send_damage_result(sender, success, amount, target.get_display_name()).await
}) })
} }
} }

View File

@ -32,7 +32,7 @@ impl CommandExecutor for GetEntityDataExecutor {
let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else { let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else {
return Err(InvalidConsumption(Some(ARG_ENTITY.into()))); return Err(InvalidConsumption(Some(ARG_ENTITY.into())));
}; };
display_data(entity.as_ref(), entity.get_display_name().await, sender).await display_data(entity.as_ref(), entity.get_display_name(), sender).await
}) })
} }
} }
@ -222,7 +222,7 @@ async fn display_data(
sender: &CommandSender, sender: &CommandSender,
) -> Result<i32, CommandError> { ) -> Result<i32, CommandError> {
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
entity.write_nbt(&mut nbt).await; entity.write_nbt(&mut nbt);
let tag = NbtTag::Compound(nbt); let tag = NbtTag::Compound(nbt);
let result = get_i32_result(&tag)?; let result = get_i32_result(&tag)?;

View File

@ -124,7 +124,7 @@ impl CommandExecutor for GiveExecutor {
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
"commands.effect.give.success.single", "commands.effect.give.success.single",
"commands.effect.give.success.single", "commands.effect.give.success.single",
[translation_name, targets[0].get_display_name().await], [translation_name, targets[0].get_display_name()],
)) ))
.await; .await;
} else { } else {
@ -183,7 +183,7 @@ impl CommandExecutor for ClearExecutor {
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
"commands.effect.clear.everything.success.single", "commands.effect.clear.everything.success.single",
"commands.effect.clear.everything.success.single", "commands.effect.clear.everything.success.single",
[targets[0].get_display_name().await], [targets[0].get_display_name()],
)) ))
.await; .await;
} else { } else {
@ -228,7 +228,7 @@ impl CommandExecutor for ClearExecutor {
effect.translation_key, effect.translation_key,
[], [],
), ),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
)) ))
.await; .await;

View File

@ -76,7 +76,7 @@ impl CommandExecutor for Executor {
translation::bedrock::COMMANDS_ENCHANT_SUCCESS, translation::bedrock::COMMANDS_ENCHANT_SUCCESS,
[ [
enchantment.get_fullname(level), enchantment.get_fullname(level),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
); );
sender.send_message(msg).await; sender.send_message(msg).await;
@ -141,7 +141,7 @@ async fn enchant_target(
let msg = TextComponent::translate_cross( let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_ITEMLESS, translation::java::COMMANDS_ENCHANT_FAILED_ITEMLESS,
translation::bedrock::COMMANDS_ENCHANT_NOITEM, translation::bedrock::COMMANDS_ENCHANT_NOITEM,
[target.get_display_name().await], [target.get_display_name()],
); );
return Err(CommandError::CommandFailed(msg)); return Err(CommandError::CommandFailed(msg));
} }
@ -170,9 +170,7 @@ async fn enchant_target(
let inventory = player.inventory(); let inventory = player.inventory();
inventory.set_held_item(item.clone()); inventory.set_held_item(item.clone());
player player.sync_hand_slot(inventory.get_selected_slot() as usize, item);
.sync_hand_slot(inventory.get_selected_slot() as usize, item)
.await;
Ok(()) Ok(())
} }

View File

@ -39,7 +39,7 @@ fn execute_as_modifier<'a>(
let mut sources = Vec::new(); let mut sources = Vec::new();
for target in targets { for target in targets {
let mut source = context.source.as_ref().clone(); let mut source = context.source.as_ref().clone();
let display_name = target.get_display_name().await; let display_name = target.get_display_name();
let name = target.get_name().get_text(); let name = target.get_name().get_text();
source.entity = Some(target.clone()); source.entity = Some(target.clone());
source.name = name; source.name = name;

View File

@ -62,7 +62,7 @@ impl Executor {
translation_key, translation_key,
translation_key, translation_key,
[ [
target.get_display_name().await, target.get_display_name(),
TextComponent::text(val.to_string()), TextComponent::text(val.to_string()),
], ],
)) ))
@ -126,20 +126,20 @@ impl Executor {
match self.exp_type { match self.exp_type {
ExpType::Levels => { ExpType::Levels => {
if self.mode == Mode::Add { if self.mode == Mode::Add {
target.add_experience_levels(amount).await; target.add_experience_levels(amount);
} else { } else {
target.set_experience_level(amount, true).await; target.set_experience_level(amount, true);
} }
} }
ExpType::Points => { ExpType::Points => {
if self.mode == Mode::Add { if self.mode == Mode::Add {
target.add_experience_points(amount).await; target.add_experience_points(amount);
} else { } else {
let current_lvl = target.experience_level.load(Ordering::Relaxed); let current_lvl = target.experience_level.load(Ordering::Relaxed);
if amount > experience::points_in_level(current_lvl) { if amount > experience::points_in_level(current_lvl) {
return false; return false;
} }
target.set_experience_points(amount).await; target.set_experience_points(amount);
} }
} }
} }
@ -195,7 +195,7 @@ impl CommandExecutor for Executor {
} }
// Safe to access first() because successes > 0 // Safe to access first() because successes > 0
let first_name = targets[0].get_display_name().await; let first_name = targets[0].get_display_name();
let msg = self.get_success_message(amount, targets, first_name); let msg = self.get_success_message(amount, targets, first_name);
sender.send_message(msg).await; sender.send_message(msg).await;

View File

@ -353,12 +353,12 @@ impl CommandExecutor for PrintForEntityExecutor {
&context.source, &context.source,
&player.gameprofile, &player.gameprofile,
translation::java::COMMANDS_FETCHPROFILE_ENTITY_SUCCESS, translation::java::COMMANDS_FETCHPROFILE_ENTITY_SUCCESS,
player.get_display_name().await, player.get_display_name(),
) )
.await; .await;
Ok(1) Ok(1)
} else { } else {
Err(NO_PROFILE_ERROR_TYPE.create_without_context(entity.get_display_name().await)) Err(NO_PROFILE_ERROR_TYPE.create_without_context(entity.get_display_name()))
} }
}) })
} }

View File

@ -82,7 +82,7 @@ impl CommandExecutor for TargetExecutor {
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
translation::java::COMMANDS_GAMEMODE_SUCCESS_OTHER, translation::java::COMMANDS_GAMEMODE_SUCCESS_OTHER,
translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_OTHER, translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_OTHER,
[target.get_display_name().await, gamemode_comp], [target.get_display_name(), gamemode_comp],
)) ))
.await; .await;
} }

View File

@ -73,7 +73,7 @@ impl CommandExecutor for Executor {
id: item_name.to_string().into(), id: item_name.to_string().into(),
count: Some(item_count.min(99)), count: Some(item_count.min(99)),
}), }),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
) )
} else { } else {

View File

@ -259,7 +259,7 @@ impl CommandExecutor for EntityReplaceExecutor {
translation::java::COMMANDS_ITEM_ENTITY_SET_SUCCESS_SINGLE, translation::java::COMMANDS_ITEM_ENTITY_SET_SUCCESS_SINGLE,
translation::java::COMMANDS_ITEM_ENTITY_SET_SUCCESS_SINGLE, translation::java::COMMANDS_ITEM_ENTITY_SET_SUCCESS_SINGLE,
[ [
targets[0].get_display_name().await, targets[0].get_display_name(),
TextComponent::text("[") TextComponent::text("[")
.add_child(item.translated_name()) .add_child(item.translated_name())
.add_child(TextComponent::text("]")) .add_child(TextComponent::text("]"))

View File

@ -51,13 +51,13 @@ impl CommandExecutor for Executor {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::COMMANDS_KICK_SUCCESS, translation::java::COMMANDS_KICK_SUCCESS,
translation::bedrock::COMMANDS_KICK_SUCCESS_REASON, translation::bedrock::COMMANDS_KICK_SUCCESS_REASON,
[target.get_display_name().await, reason.clone()], [target.get_display_name(), reason.clone()],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::COMMANDS_KICK_SUCCESS, translation::java::COMMANDS_KICK_SUCCESS,
translation::bedrock::COMMANDS_KICK_SUCCESS, translation::bedrock::COMMANDS_KICK_SUCCESS,
[target.get_display_name().await, reason.clone()], [target.get_display_name(), reason.clone()],
) )
}; };

View File

@ -23,14 +23,14 @@ impl CommandExecutor for TargetsExecutor {
let target_count = targets.len(); let target_count = targets.len();
for target in &targets { for target in &targets {
target.kill(target.as_ref()).await; target.kill(target.as_ref());
} }
let msg = if target_count == 1 { let msg = if target_count == 1 {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::COMMANDS_KILL_SUCCESS_SINGLE, translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
translation::bedrock::COMMANDS_KILL_SUCCESSFUL, translation::bedrock::COMMANDS_KILL_SUCCESSFUL,
[targets[0].get_display_name().await], [targets[0].get_display_name()],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(
@ -53,7 +53,7 @@ impl CommandExecutor for SelfExecutor {
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> { fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
Box::pin(async move { Box::pin(async move {
let target = context.source.entity_or_err()?; let target = context.source.entity_or_err()?;
target.kill(&*target).await; target.kill(&*target);
context context
.source .source
@ -61,7 +61,7 @@ impl CommandExecutor for SelfExecutor {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::COMMANDS_KILL_SUCCESS_SINGLE, translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
translation::bedrock::COMMANDS_KILL_SUCCESSFUL, translation::bedrock::COMMANDS_KILL_SUCCESSFUL,
[target.get_display_name().await], [target.get_display_name()],
), ),
true, true,
) )

View File

@ -12,7 +12,7 @@ use crate::{
context::command_context::CommandContext, context::command_context::CommandContext,
node::{CommandExecutor, CommandExecutorResult, dispatcher::CommandDispatcher}, node::{CommandExecutor, CommandExecutorResult, dispatcher::CommandDispatcher},
}, },
entity::{EntityBase, EntityBaseFuture, player::Player}, entity::{EntityBase, player::Player},
}; };
const DESCRIPTION: &str = "Print the list of online players."; const DESCRIPTION: &str = "Print the list of online players.";
@ -33,7 +33,7 @@ impl CommandExecutor for ListCommandExecutor {
let players_len = players.len(); let players_len = players.len();
let list = match self.0 { let list = match self.0 {
ListMode::Names => get_player_names(&players).await, ListMode::Names => get_player_names(&players),
ListMode::Uuids => get_player_names_and_ids(&players), ListMode::Uuids => get_player_names_and_ids(&players),
}; };
@ -75,10 +75,8 @@ impl CommandExecutor for ListCommandExecutor {
} }
} }
async fn get_player_names(players: &[Arc<Player>]) -> TextComponent { fn get_player_names(players: &[Arc<Player>]) -> TextComponent {
let display_name_futures: Vec<EntityBaseFuture<'_, TextComponent>> = let display_names = players.iter().map(|p| p.get_display_name()).collect();
players.iter().map(|p| p.get_display_name()).collect();
let display_names = futures::future::join_all(display_name_futures).await;
TextComponent::join_with_comma(display_names) TextComponent::join_with_comma(display_names)
} }

View File

@ -42,8 +42,8 @@ impl CommandExecutor for Executor {
.send_message( .send_message(
&TextComponent::text(msg.clone()), &TextComponent::text(msg.clone()),
MSG_COMMAND_OUTGOING, MSG_COMMAND_OUTGOING,
&player.get_display_name().await, &player.get_display_name(),
Some(&target.get_display_name().await), Some(&target.get_display_name()),
) )
.await; .await;
} }
@ -52,8 +52,8 @@ impl CommandExecutor for Executor {
.send_message( .send_message(
&TextComponent::text(msg.clone()), &TextComponent::text(msg.clone()),
MSG_COMMAND_INCOMING, MSG_COMMAND_INCOMING,
&player.get_display_name().await, &player.get_display_name(),
Some(&target.get_display_name().await), Some(&target.get_display_name()),
) )
.await; .await;
} }

View File

@ -155,7 +155,7 @@ impl CommandExecutor for Executor {
translation::bedrock::COMMANDS_PLAYSOUND_SUCCESS, translation::bedrock::COMMANDS_PLAYSOUND_SUCCESS,
[ [
TextComponent::text(sound_name), TextComponent::text(sound_name),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
)) ))
.await; .await;

View File

@ -121,7 +121,7 @@ impl CommandExecutor for RecipeGiveExecutor {
translation::java::COMMANDS_RECIPE_GIVE_SUCCESS_SINGLE, translation::java::COMMANDS_RECIPE_GIVE_SUCCESS_SINGLE,
[ [
TextComponent::text(recipe_count_str), TextComponent::text(recipe_count_str),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
); );
context.source.send_feedback(msg, true).await; context.source.send_feedback(msg, true).await;
@ -202,7 +202,7 @@ impl CommandExecutor for RecipeTakeExecutor {
translation::java::COMMANDS_RECIPE_TAKE_SUCCESS_SINGLE, translation::java::COMMANDS_RECIPE_TAKE_SUCCESS_SINGLE,
[ [
TextComponent::text(taken_count_str), TextComponent::text(taken_count_str),
targets[0].get_display_name().await, targets[0].get_display_name(),
], ],
); );
context.source.send_feedback(msg, true).await; context.source.send_feedback(msg, true).await;

View File

@ -39,13 +39,23 @@ static ERROR_GENERIC: CommandErrorType<2> = CommandErrorType::new(
); );
#[allow(clippy::assigning_clones)] #[allow(clippy::assigning_clones)]
async fn is_riding_recursive(entity: &dyn EntityBase, possible_vehicle: &dyn EntityBase) -> bool { fn is_riding_recursive(entity: &dyn EntityBase, possible_vehicle: &dyn EntityBase) -> bool {
let mut current = possible_vehicle.get_entity().vehicle.lock().await.clone(); let mut current = possible_vehicle
.get_entity()
.vehicle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
while let Some(vehicle) = current { while let Some(vehicle) = current {
if vehicle.get_entity().entity_id == entity.get_entity().entity_id { if vehicle.get_entity().entity_id == entity.get_entity().entity_id {
return true; return true;
} }
current = vehicle.get_entity().vehicle.lock().await.clone(); current = vehicle
.get_entity()
.vehicle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
} }
false false
} }
@ -79,17 +89,22 @@ impl CommandExecutor for RideMountExecutor {
continue; continue;
} }
if is_riding_recursive(target.as_ref(), vehicle.as_ref()).await { if is_riding_recursive(target.as_ref(), vehicle.as_ref()) {
last_error = Some(ERROR_LOOP.create_without_context()); last_error = Some(ERROR_LOOP.create_without_context());
continue; continue;
} }
let current_vehicle = target.get_entity().vehicle.lock().await.clone(); let current_vehicle = target
.get_entity()
.vehicle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
if let Some(ref curr_veh) = current_vehicle { if let Some(ref curr_veh) = current_vehicle {
if curr_veh.get_entity().entity_id == vehicle.get_entity().entity_id { if curr_veh.get_entity().entity_id == vehicle.get_entity().entity_id {
last_error = Some(ERROR_ALREADY_RIDING.create_without_context( last_error = Some(ERROR_ALREADY_RIDING.create_without_context(
target.get_display_name().await, target.get_display_name(),
vehicle.get_display_name().await, vehicle.get_display_name(),
)); ));
continue; continue;
} }
@ -102,17 +117,13 @@ impl CommandExecutor for RideMountExecutor {
vehicle vehicle
.get_entity() .get_entity()
.add_passenger(vehicle.clone(), target.clone()) .add_passenger(vehicle.clone(), target.clone());
.await;
success_count += 1; success_count += 1;
let msg = TextComponent::translate_cross( let msg = TextComponent::translate_cross(
translation::java::COMMANDS_RIDE_MOUNT_SUCCESS, translation::java::COMMANDS_RIDE_MOUNT_SUCCESS,
translation::java::COMMANDS_RIDE_MOUNT_SUCCESS, translation::java::COMMANDS_RIDE_MOUNT_SUCCESS,
[ [target.get_display_name(), vehicle.get_display_name()],
target.get_display_name().await,
vehicle.get_display_name().await,
],
); );
context.source.send_feedback(msg, true).await; context.source.send_feedback(msg, true).await;
} }
@ -122,8 +133,8 @@ impl CommandExecutor for RideMountExecutor {
return Err(err); return Err(err);
} }
return Err(ERROR_GENERIC.create_without_context( return Err(ERROR_GENERIC.create_without_context(
targets[0].get_display_name().await, targets[0].get_display_name(),
vehicle.get_display_name().await, vehicle.get_display_name(),
)); ));
} }
@ -143,7 +154,12 @@ impl CommandExecutor for RideDismountExecutor {
let mut last_error = None; let mut last_error = None;
for target in &targets { for target in &targets {
let current_vehicle = target.get_entity().vehicle.lock().await.clone(); let current_vehicle = target
.get_entity()
.vehicle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
if let Some(vehicle) = current_vehicle { if let Some(vehicle) = current_vehicle {
vehicle vehicle
.get_entity() .get_entity()
@ -154,13 +170,12 @@ impl CommandExecutor for RideDismountExecutor {
let msg = TextComponent::translate_cross( let msg = TextComponent::translate_cross(
translation::java::COMMANDS_RIDE_DISMOUNT_SUCCESS, translation::java::COMMANDS_RIDE_DISMOUNT_SUCCESS,
translation::java::COMMANDS_RIDE_DISMOUNT_SUCCESS, translation::java::COMMANDS_RIDE_DISMOUNT_SUCCESS,
[target.get_display_name().await], [target.get_display_name()],
); );
context.source.send_feedback(msg, true).await; context.source.send_feedback(msg, true).await;
} else { } else {
last_error = Some( last_error =
ERROR_NOT_RIDING.create_without_context(target.get_display_name().await), Some(ERROR_NOT_RIDING.create_without_context(target.get_display_name()));
);
} }
} }
@ -168,9 +183,7 @@ impl CommandExecutor for RideDismountExecutor {
if let Some(err) = last_error { if let Some(err) = last_error {
return Err(err); return Err(err);
} }
return Err( return Err(ERROR_NOT_RIDING.create_without_context(targets[0].get_display_name()));
ERROR_NOT_RIDING.create_without_context(targets[0].get_display_name().await)
);
} }
Ok(success_count) Ok(success_count)

View File

@ -87,7 +87,7 @@ async fn rotate_entity(
/// Sends success message for the rotate command. /// Sends success message for the rotate command.
async fn send_success_message(sender: &CommandSender, target: &dyn crate::entity::EntityBase) { async fn send_success_message(sender: &CommandSender, target: &dyn crate::entity::EntityBase) {
let target_name = target.get_display_name().await; let target_name = target.get_display_name();
sender sender
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
translation::java::COMMANDS_ROTATE_SUCCESS, translation::java::COMMANDS_ROTATE_SUCCESS,

View File

@ -35,7 +35,7 @@ impl CommandExecutor for StopSpectateExecutor {
}; };
if player.gamemode.load() != GameMode::Spectator { if player.gamemode.load() != GameMode::Spectator {
let display_name = player.get_display_name().await; let display_name = player.get_display_name();
return Err(CommandError::CommandFailed(TextComponent::translate_cross( return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
@ -76,7 +76,7 @@ impl CommandExecutor for SpectateTargetSelfExecutor {
}; };
if player.gamemode.load() != GameMode::Spectator { if player.gamemode.load() != GameMode::Spectator {
let display_name = player.get_display_name().await; let display_name = player.get_display_name();
return Err(CommandError::CommandFailed(TextComponent::translate_cross( return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
@ -98,7 +98,7 @@ impl CommandExecutor for SpectateTargetSelfExecutor {
let target_world = target_entity.world.load_full(); let target_world = target_entity.world.load_full();
let player_world = player.world(); let player_world = player.world();
if !Arc::ptr_eq(&target_world, &player_world) { if !Arc::ptr_eq(&target_world, &player_world) {
let target_name = target.get_display_name().await; let target_name = target.get_display_name();
return Err(CommandError::CommandFailed(TextComponent::translate_cross( return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE,
translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE,
@ -120,7 +120,7 @@ impl CommandExecutor for SpectateTargetSelfExecutor {
.teleport(pos, Some(yaw), Some(pitch), player_world) .teleport(pos, Some(yaw), Some(pitch), player_world)
.await; .await;
let target_name = target.get_display_name().await; let target_name = target.get_display_name();
sender sender
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED, translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED,
@ -155,7 +155,7 @@ impl CommandExecutor for SpectateTargetOtherExecutor {
// First validate all players // First validate all players
for player in players { for player in players {
if player.gamemode.load() != GameMode::Spectator { if player.gamemode.load() != GameMode::Spectator {
let display_name = player.get_display_name().await; let display_name = player.get_display_name();
return Err(CommandError::CommandFailed(TextComponent::translate_cross( return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR, translation::java::COMMANDS_SPECTATE_NOT_SPECTATOR,
@ -173,7 +173,7 @@ impl CommandExecutor for SpectateTargetOtherExecutor {
let player_world = player.world(); let player_world = player.world();
if !Arc::ptr_eq(&target_world, &player_world) { if !Arc::ptr_eq(&target_world, &player_world) {
let target_name = target.get_display_name().await; let target_name = target.get_display_name();
return Err(CommandError::CommandFailed(TextComponent::translate_cross( return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE,
translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE,
@ -201,7 +201,7 @@ impl CommandExecutor for SpectateTargetOtherExecutor {
succeeded += 1; succeeded += 1;
} }
let target_name = target.get_display_name().await; let target_name = target.get_display_name();
sender sender
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED, translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED,

View File

@ -66,7 +66,7 @@ impl CommandExecutor for Executor {
} }
}; };
let entity = from_type(entity_type, pos, &world, Uuid::new_v4()); let entity = from_type(entity_type, pos, &world, Uuid::new_v4());
let name = entity.get_display_name().await; let name = entity.get_display_name();
world.spawn_entity(entity); world.spawn_entity(entity);
sender sender
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(

View File

@ -48,8 +48,8 @@ impl CommandExecutor for ChangeExecutor {
for target in &targets { for target in &targets {
let entity = target.get_entity(); let entity = target.get_entity();
let success = match self.0 { let success = match self.0 {
Action::Add => entity.add_scoreboard_tag(&tag).await, Action::Add => entity.add_scoreboard_tag(&tag),
Action::Remove => entity.remove_scoreboard_tag(&tag).await, Action::Remove => entity.remove_scoreboard_tag(&tag),
}; };
if success { if success {
changed += 1; changed += 1;
@ -90,10 +90,7 @@ impl CommandExecutor for ChangeExecutor {
TextComponent::translate_cross( TextComponent::translate_cross(
single_key.0, single_key.0,
single_key.1, single_key.1,
[ [TextComponent::text(tag), targets[0].get_display_name()],
TextComponent::text(tag),
targets[0].get_display_name().await,
],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(
@ -123,7 +120,11 @@ impl CommandExecutor for ListExecutor {
// BTreeSet keeps the output deterministic. // BTreeSet keeps the output deterministic.
let mut all_tags = BTreeSet::new(); let mut all_tags = BTreeSet::new();
for target in &targets { for target in &targets {
let tags = target.get_entity().scoreboard_tags.lock().await; let tags = target
.get_entity()
.scoreboard_tags
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
all_tags.extend(tags.iter().cloned()); all_tags.extend(tags.iter().cloned());
} }
@ -131,7 +132,7 @@ impl CommandExecutor for ListExecutor {
TextComponent::text(all_tags.iter().cloned().collect::<Vec<String>>().join(", ")); TextComponent::text(all_tags.iter().cloned().collect::<Vec<String>>().join(", "));
let msg = if targets.len() == 1 { let msg = if targets.len() == 1 {
let name = targets[0].get_display_name().await; let name = targets[0].get_display_name();
if all_tags.is_empty() { if all_tags.is_empty() {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::COMMANDS_TAG_LIST_SINGLE_EMPTY, translation::java::COMMANDS_TAG_LIST_SINGLE_EMPTY,

View File

@ -70,7 +70,7 @@ async fn success_key_and_arg(
multiple_key: &'static str, multiple_key: &'static str,
) -> (&'static str, TextComponent) { ) -> (&'static str, TextComponent) {
if targets.len() == 1 { if targets.len() == 1 {
(single_key, targets[0].get_display_name().await) (single_key, targets[0].get_display_name())
} else { } else {
(multiple_key, TextComponent::text(targets.len().to_string())) (multiple_key, TextComponent::text(targets.len().to_string()))
} }
@ -118,7 +118,7 @@ impl CommandExecutor for EntitiesToEntityExecutor {
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
key, key,
translation::bedrock::COMMANDS_TP_SUCCESSVICTIM, translation::bedrock::COMMANDS_TP_SUCCESSVICTIM,
[target_arg, destination.get_display_name().await], [target_arg, destination.get_display_name()],
)) ))
.await; .await;
@ -383,10 +383,7 @@ impl CommandExecutor for SelfToEntityExecutor {
.send_message(TextComponent::translate_cross( .send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_SUCCESS_ENTITY_SINGLE, translation::java::COMMANDS_TELEPORT_SUCCESS_ENTITY_SINGLE,
translation::bedrock::COMMANDS_TP_SUCCESSVICTIM, translation::bedrock::COMMANDS_TP_SUCCESSVICTIM,
[ [player.get_display_name(), destination.get_display_name()],
player.get_display_name().await,
destination.get_display_name().await,
],
)) ))
.await; .await;
@ -433,7 +430,7 @@ impl CommandExecutor for SelfToPosExecutor {
translation::java::COMMANDS_TELEPORT_SUCCESS_LOCATION_SINGLE, translation::java::COMMANDS_TELEPORT_SUCCESS_LOCATION_SINGLE,
translation::bedrock::COMMANDS_TP_SUCCESS_COORDINATES, translation::bedrock::COMMANDS_TP_SUCCESS_COORDINATES,
[ [
player.get_display_name().await, player.get_display_name(),
TextComponent::text(pos.x.to_string()), TextComponent::text(pos.x.to_string()),
TextComponent::text(pos.y.to_string()), TextComponent::text(pos.y.to_string()),
TextComponent::text(pos.z.to_string()), TextComponent::text(pos.z.to_string()),

View File

@ -53,11 +53,7 @@ impl CommandExecutor for ClearOrResetExecutor {
} else { } else {
"commands.title.cleared.single" "commands.title.cleared.single"
}; };
TextComponent::translate_cross( TextComponent::translate_cross(text, text, [targets[0].get_display_name()])
text,
text,
[targets[0].get_display_name().await],
)
} else { } else {
let text = if reset { let text = if reset {
"commands.title.reset.multiple" "commands.title.reset.multiple"
@ -105,7 +101,7 @@ impl CommandExecutor for TitleExecutor {
TextComponent::translate_cross( TextComponent::translate_cross(
format!("commands.title.show.{mode_name}.single").clone(), format!("commands.title.show.{mode_name}.single").clone(),
format!("commands.title.show.{mode_name}.single"), format!("commands.title.show.{mode_name}.single"),
[targets[0].get_display_name().await], [targets[0].get_display_name()],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(
@ -148,7 +144,7 @@ impl CommandExecutor for TimesTitleExecutor {
TextComponent::translate_cross( TextComponent::translate_cross(
"commands.title.times.single", "commands.title.times.single",
"commands.title.times.single", "commands.title.times.single",
[targets[0].get_display_name().await], [targets[0].get_display_name()],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(

View File

@ -128,7 +128,7 @@ impl CommandExecutor for TargetPlayerExecutor {
"commands.transfer.success.single", "commands.transfer.success.single",
"commands.transfer.success.single", "commands.transfer.success.single",
[ [
players[0].get_display_name().await, players[0].get_display_name(),
TextComponent::text(hostname.to_owned()), TextComponent::text(hostname.to_owned()),
TextComponent::text(port.to_string()), TextComponent::text(port.to_string()),
], ],

View File

@ -52,9 +52,13 @@ impl CommandExecutor for GetExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let border = world.worldborder.lock().await; let diameter = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.new_diameter
.round() as i32;
let diameter = border.new_diameter.round() as i32;
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_GET, translation::java::COMMANDS_WORLDBORDER_GET,
@ -81,10 +85,14 @@ impl CommandExecutor for SetExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let distance = distance_consumer().find_arg_default_name(args)??; let distance = distance_consumer().find_arg_default_name(args)??;
let (d, diff) = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if (distance - border.new_diameter).abs() < f64::EPSILON { if (distance - border.new_diameter).abs() < f64::EPSILON {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
@ -95,6 +103,10 @@ impl CommandExecutor for SetExecutor {
} }
let d = border.new_diameter; let d = border.new_diameter;
border.set_diameter(world, distance, None);
(d, (distance - d) as i32)
};
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE, translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE,
@ -104,9 +116,7 @@ impl CommandExecutor for SetExecutor {
)) ))
.await; .await;
border.set_diameter(world, distance, None); Ok(diff)
Ok((distance - d) as i32)
}) })
} }
} }
@ -124,14 +134,18 @@ impl CommandExecutor for SetTimeExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let distance = distance_consumer().find_arg_default_name(args)??; let distance = distance_consumer().find_arg_default_name(args)??;
let time = time_consumer().find_arg_default_name(args)??; let time = time_consumer().find_arg_default_name(args)??;
let (old_dist, ordering, diff) = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let old_dist = format!("{:.1}", border.new_diameter); let old_dist = format!("{:.1}", border.new_diameter);
match distance.total_cmp(&border.new_diameter) { let ordering = distance.total_cmp(&border.new_diameter);
std::cmp::Ordering::Equal => { if ordering == std::cmp::Ordering::Equal {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE,
@ -139,8 +153,14 @@ impl CommandExecutor for SetTimeExecutor {
), ),
)); ));
} }
std::cmp::Ordering::Less => { let d = border.new_diameter;
border.set_diameter(world, distance, Some(i64::from(time) * 1000));
(old_dist, ordering, (distance - d) as i32)
};
let dist = format!("{distance:.1}"); let dist = format!("{distance:.1}");
match ordering {
std::cmp::Ordering::Less => {
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_SHRINK, translation::java::COMMANDS_WORLDBORDER_SET_SHRINK,
@ -152,7 +172,6 @@ impl CommandExecutor for SetTimeExecutor {
.await; .await;
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
let dist = format!("{distance:.1}");
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_GROW, translation::java::COMMANDS_WORLDBORDER_SET_GROW,
@ -163,12 +182,10 @@ impl CommandExecutor for SetTimeExecutor {
)) ))
.await; .await;
} }
std::cmp::Ordering::Equal => unreachable!(),
} }
let d = border.new_diameter; Ok(diff)
border.set_diameter(world, distance, Some(i64::from(time) * 1000));
Ok((distance - d) as i32)
}) })
} }
} }
@ -186,8 +203,6 @@ impl CommandExecutor for AddExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let distance_add = distance_consumer().find_arg_default_name(args)??; let distance_add = distance_consumer().find_arg_default_name(args)??;
if distance_add == 0.0 { if distance_add == 0.0 {
@ -199,10 +214,19 @@ impl CommandExecutor for AddExecutor {
)); ));
} }
let distance = border.new_diameter + distance_add; let (dist, old_dist) = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let distance = border.new_diameter + distance_add;
let dist = format!("{distance:.1}"); let dist = format!("{distance:.1}");
let old_dist = format!("{:.1}", border.new_diameter); let old_dist = format!("{:.1}", border.new_diameter);
border.set_diameter(world, distance, None);
(dist, old_dist)
};
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE, translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE,
@ -211,7 +235,6 @@ impl CommandExecutor for AddExecutor {
TextComponent::text(old_dist) TextComponent::text(old_dist)
)) ))
.await; .await;
border.set_diameter(world, distance, None);
Ok(distance_add as i32) Ok(distance_add as i32)
}) })
} }
@ -230,16 +253,19 @@ impl CommandExecutor for AddTimeExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let distance_add = distance_consumer().find_arg_default_name(args)??; let distance_add = distance_consumer().find_arg_default_name(args)??;
let time = time_consumer().find_arg_default_name(args)??; let time = time_consumer().find_arg_default_name(args)??;
let distance = distance_add + border.new_diameter; let (distance, old_dist, ordering) = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let distance = distance_add + border.new_diameter;
let old_dist = format!("{:.1}", border.new_diameter); let old_dist = format!("{:.1}", border.new_diameter);
match distance.total_cmp(&border.new_diameter) { let ordering = distance.total_cmp(&border.new_diameter);
std::cmp::Ordering::Equal => { if ordering == std::cmp::Ordering::Equal {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE,
@ -247,8 +273,13 @@ impl CommandExecutor for AddTimeExecutor {
), ),
)); ));
} }
std::cmp::Ordering::Less => { border.set_diameter(world, distance, Some(i64::from(time) * 1000));
(distance, old_dist, ordering)
};
let dist = format!("{distance:.1}"); let dist = format!("{distance:.1}");
match ordering {
std::cmp::Ordering::Less => {
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_SHRINK, translation::java::COMMANDS_WORLDBORDER_SET_SHRINK,
@ -260,7 +291,6 @@ impl CommandExecutor for AddTimeExecutor {
.await; .await;
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
let dist = format!("{distance:.1}");
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_SET_GROW, translation::java::COMMANDS_WORLDBORDER_SET_GROW,
@ -271,10 +301,9 @@ impl CommandExecutor for AddTimeExecutor {
)) ))
.await; .await;
} }
std::cmp::Ordering::Equal => unreachable!(),
} }
border.set_diameter(world, distance, Some(i64::from(time) * 1000));
Ok(distance_add as i32) Ok(distance_add as i32)
}) })
} }
@ -293,10 +322,14 @@ impl CommandExecutor for CenterExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let Vector2 { x, y } = Position2DArgumentConsumer.find_arg_default_name(args)?; let Vector2 { x, y } = Position2DArgumentConsumer.find_arg_default_name(args)?;
world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.set_center(world, x, y);
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_CENTER_SUCCESS, translation::java::COMMANDS_WORLDBORDER_CENTER_SUCCESS,
@ -305,7 +338,7 @@ impl CommandExecutor for CenterExecutor {
TextComponent::text(format!("{y:.2}")) TextComponent::text(format!("{y:.2}"))
)) ))
.await; .await;
border.set_center(world, x, y);
Ok(0) Ok(0)
}) })
} }
@ -324,10 +357,14 @@ impl CommandExecutor for DamageAmountExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let damage_per_block = damage_per_block_consumer().find_arg_default_name(args)??; let damage_per_block = damage_per_block_consumer().find_arg_default_name(args)??;
let old_damage = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON { if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
@ -337,8 +374,12 @@ impl CommandExecutor for DamageAmountExecutor {
)); ));
} }
let damage = format!("{damage_per_block:.2}");
let old_damage = format!("{:.2}", border.damage_per_block); let old_damage = format!("{:.2}", border.damage_per_block);
border.damage_per_block = damage_per_block;
old_damage
};
let damage = format!("{damage_per_block:.2}");
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_SUCCESS, translation::java::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_SUCCESS,
@ -347,7 +388,7 @@ impl CommandExecutor for DamageAmountExecutor {
TextComponent::text(old_damage) TextComponent::text(old_damage)
)) ))
.await; .await;
border.damage_per_block = damage_per_block;
Ok(damage_per_block as i32) Ok(damage_per_block as i32)
}) })
} }
@ -366,10 +407,14 @@ impl CommandExecutor for DamageBufferExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let buffer = damage_buffer_consumer().find_arg_default_name(args)??; let buffer = damage_buffer_consumer().find_arg_default_name(args)??;
let old_buf = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if (buffer - border.buffer).abs() < f32::EPSILON { if (buffer - border.buffer).abs() < f32::EPSILON {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
@ -379,8 +424,12 @@ impl CommandExecutor for DamageBufferExecutor {
)); ));
} }
let buf = format!("{buffer:.2}");
let old_buf = format!("{:.2}", border.buffer); let old_buf = format!("{:.2}", border.buffer);
border.buffer = buffer;
old_buf
};
let buf = format!("{buffer:.2}");
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_SUCCESS, translation::java::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_SUCCESS,
@ -389,7 +438,7 @@ impl CommandExecutor for DamageBufferExecutor {
TextComponent::text(old_buf) TextComponent::text(old_buf)
)) ))
.await; .await;
border.buffer = buffer;
Ok(buffer as i32) Ok(buffer as i32)
}) })
} }
@ -408,10 +457,14 @@ impl CommandExecutor for WarningDistanceExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let distance = warning_distance_consumer().find_arg_default_name(args)??; let distance = warning_distance_consumer().find_arg_default_name(args)??;
let old_warning = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if distance == border.warning_blocks { if distance == border.warning_blocks {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
@ -421,15 +474,20 @@ impl CommandExecutor for WarningDistanceExecutor {
)); ));
} }
let old_warning = border.warning_blocks;
border.set_warning_distance(world, distance);
old_warning
};
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS, translation::java::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS,
translation::bedrock::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS, translation::bedrock::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS,
TextComponent::text(distance.to_string()), TextComponent::text(distance.to_string()),
TextComponent::text(border.warning_blocks.to_string()) TextComponent::text(old_warning.to_string())
)) ))
.await; .await;
border.set_warning_distance(world, distance);
Ok(distance) Ok(distance)
}) })
} }
@ -448,10 +506,14 @@ impl CommandExecutor for WarningTimeExecutor {
// TODO: Maybe ask player for world, or get the current world // TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load(); let worlds = server.worlds.load();
let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let world = worlds.first().ok_or(CommandError::InvalidRequirement)?;
let mut border = world.worldborder.lock().await;
let time = time_consumer().find_arg_default_name(args)??; let time = time_consumer().find_arg_default_name(args)??;
let old_time = {
let mut border = world
.worldborder
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if time == border.warning_time { if time == border.warning_time {
return Err(CommandError::CommandFailed( return Err(CommandError::CommandFailed(
pumpkin_macros::translate_cross!( pumpkin_macros::translate_cross!(
@ -461,15 +523,20 @@ impl CommandExecutor for WarningTimeExecutor {
)); ));
} }
let old_time = border.warning_time;
border.set_warning_delay(world, time);
old_time
};
sender sender
.send_message(pumpkin_macros::translate_cross!( .send_message(pumpkin_macros::translate_cross!(
translation::java::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS, translation::java::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS,
translation::bedrock::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS, translation::bedrock::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS,
TextComponent::text(time.to_string()), TextComponent::text(time.to_string()),
TextComponent::text(border.warning_time.to_string()) TextComponent::text(old_time.to_string())
)) ))
.await; .await;
border.set_warning_delay(world, time);
Ok(time) Ok(time)
}) })
} }

View File

@ -203,7 +203,7 @@ impl CommandSource {
#[must_use] #[must_use]
pub async fn with_entity(self, entity: Arc<dyn EntityBase>) -> Self { pub async fn with_entity(self, entity: Arc<dyn EntityBase>) -> Self {
let name = entity.get_name().get_text(); let name = entity.get_name().get_text();
let display_name = entity.get_display_name().await; let display_name = entity.get_display_name();
Self { Self {
output: self.output, output: self.output,
world: self.world, world: self.world,

View File

@ -331,8 +331,8 @@ impl CommandSender {
Some(player.clone()), Some(player.clone()),
player.position(), player.position(),
player.rotation().into(), player.rotation().into(),
player.get_display_name().await.get_text(), player.get_display_name().get_text(),
player.get_display_name().await, player.get_display_name(),
server.clone(), server.clone(),
), ),
Self::CommandBlock(command_entity, world) => { Self::CommandBlock(command_entity, world) => {

View File

@ -54,7 +54,7 @@ impl ServerPlayerData {
player.on_handled_screen_closed().await; player.on_handled_screen_closed().await;
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
player.write_nbt(&mut nbt).await; player.write_nbt(&mut nbt);
let storage = self.storage.clone(); let storage = self.storage.clone();
let uuid = player.gameprofile.id; let uuid = player.gameprofile.id;
@ -84,7 +84,7 @@ impl ServerPlayerData {
for world in server.worlds.load().iter() { for world in server.worlds.load().iter() {
for player in world.players.load().iter() { for player in world.players.load().iter() {
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
player.write_nbt(&mut nbt).await; player.write_nbt(&mut nbt);
let storage = self.storage.clone(); let storage = self.storage.clone();
let uuid = player.gameprofile.id; let uuid = player.gameprofile.id;
@ -194,7 +194,7 @@ impl ServerPlayerData {
let uuid = player.gameprofile.id; let uuid = player.gameprofile.id;
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
player.write_nbt(&mut nbt).await; player.write_nbt(&mut nbt);
let storage = self.storage.clone(); let storage = self.storage.clone();
tokio::task::spawn_blocking(move || storage.save_player_data(&uuid, nbt)) tokio::task::spawn_blocking(move || storage.save_player_data(&uuid, nbt))

View File

@ -4,9 +4,8 @@ use pumpkin_data::{Block, BlockStateId};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::{chunk::ChunkHeightmapType, world::BlockFlags}; use pumpkin_world::{chunk::ChunkHeightmapType, world::BlockFlags};
use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use tokio::sync::Mutex; use std::sync::{Arc, Mutex};
use crate::entity::{ use crate::entity::{
Entity, EntityBase, Entity, EntityBase,
@ -284,31 +283,43 @@ impl EnderDragonEntity {
} }
} }
pub async fn set_phase(&self, phase_type: EnderDragonPhase) { pub fn set_phase(&self, phase_type: EnderDragonPhase) {
let mut phase_lock = self.phase.lock().await; let mut phase_lock = self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *phase_lock == phase_type { if *phase_lock == phase_type {
return; return;
} }
let old_phase = self.phase_manager.get_phase(*phase_lock); let old_phase = self.phase_manager.get_phase(*phase_lock);
old_phase.end(self).await; old_phase.end(self);
*phase_lock = phase_type; *phase_lock = phase_type;
let new_phase = self.phase_manager.get_phase(phase_type); let new_phase = self.phase_manager.get_phase(phase_type);
new_phase.begin(self).await; new_phase.begin(self);
} }
async fn ensure_nodes_initialized(&self) { fn ensure_nodes_initialized(&self) {
let mut initialized = self.nodes_initialized.lock().await; let mut initialized = self
.nodes_initialized
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *initialized { if *initialized {
return; return;
} }
let world = self.mob_entity.living_entity.entity.world.load(); let world = self.mob_entity.living_entity.entity.world.load();
let fight_origin = self.fight_origin.lock().await; let fight_origin = self
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut nodes = self.nodes.lock().await; let mut nodes = self
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for i in 0..NODE_COUNT { for i in 0..NODE_COUNT {
let mut y_adjustment = 5; let mut y_adjustment = 5;
let node_x; let node_x;
@ -346,19 +357,28 @@ impl EnderDragonEntity {
let new_path = find_path(&nodes, nearest, dest, None); let new_path = find_path(&nodes, nearest, dest, None);
drop(nodes); drop(nodes);
*self.target_node.lock().await = dest; *self
*self.path.lock().await = new_path; .target_node
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = dest;
*self
.path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = new_path;
*initialized = true; *initialized = true;
} }
pub async fn find_closest_node(&self) -> usize { pub fn find_closest_node(&self) -> usize {
let pos = self.mob_entity.living_entity.entity.pos.load(); let pos = self.mob_entity.living_entity.entity.pos.load();
self.find_closest_node_to(pos).await self.find_closest_node_to(pos)
} }
pub async fn find_closest_node_to(&self, pos: Vector3<f64>) -> usize { pub fn find_closest_node_to(&self, pos: Vector3<f64>) -> usize {
self.ensure_nodes_initialized().await; self.ensure_nodes_initialized();
let nodes = self.nodes.lock().await; let nodes = self
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
Self::nearest_node_in(&nodes, pos) Self::nearest_node_in(&nodes, pos)
} }
@ -408,7 +428,7 @@ impl EnderDragonEntity {
Vector3::new(vec.x * cos - vec.z * sin, vec.y, vec.z * cos + vec.x * sin) Vector3::new(vec.x * cos - vec.z * sin, vec.y, vec.z * cos + vec.x * sin)
} }
pub async fn steer_toward( pub fn steer_toward(
&self, &self,
pos: Vector3<f64>, pos: Vector3<f64>,
target: Vector3<f64>, target: Vector3<f64>,
@ -459,7 +479,10 @@ impl EnderDragonEntity {
} }
y_rot_d = y_rot_d.clamp(-50.0, 50.0); y_rot_d = y_rot_d.clamp(-50.0, 50.0);
let mut y_rot_a = self.yaw_rot_accel.lock().await; let mut y_rot_a = self
.yaw_rot_accel
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*y_rot_a *= 0.8; *y_rot_a *= 0.8;
*y_rot_a += y_rot_d * turn_speed; *y_rot_a += y_rot_d * turn_speed;
entity.yaw.store(yaw + *y_rot_a * 0.1); entity.yaw.store(yaw + *y_rot_a * 0.1);
@ -485,11 +508,24 @@ impl EnderDragonEntity {
)); ));
} }
async fn update_flap_time(&self) { fn update_flap_time(&self) {
let sitting = self.phase.lock().await.is_sitting(); let sitting = self
let in_wall = *self.in_wall.lock().await; .phase
let mut flap = self.flap_time.lock().await; .lock()
let mut o_flap = self.o_flap_time.lock().await; .unwrap_or_else(std::sync::PoisonError::into_inner)
.is_sitting();
let in_wall = *self
.in_wall
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut flap = self
.flap_time
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut o_flap = self
.o_flap_time
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*o_flap = *flap; *o_flap = *flap;
*flap += if sitting { *flap += if sitting {
@ -504,11 +540,19 @@ impl EnderDragonEntity {
} }
} }
async fn tick_growl(&self) { fn tick_growl(&self) {
if self.phase.lock().await.is_sitting() { if self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_sitting()
{
return; return;
} }
let mut t = self.growl_time.lock().await; let mut t = self
.growl_time
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *t > 0 { if *t > 0 {
*t -= 1; *t -= 1;
} else { } else {
@ -537,7 +581,7 @@ impl EnderDragonEntity {
.cloned() .cloned()
} }
async fn handle_player_collisions(&self) { fn handle_player_collisions(&self) {
let world = self.mob_entity.living_entity.entity.world.load(); let world = self.mob_entity.living_entity.entity.world.load();
let dragon_bbox = self.mob_entity.living_entity.entity.bounding_box.load(); let dragon_bbox = self.mob_entity.living_entity.entity.bounding_box.load();
@ -563,7 +607,12 @@ impl EnderDragonEntity {
.apply_knockback(4.0, xd / dd, zd / dd); .apply_knockback(4.0, xd / dd, zd / dd);
player.get_entity().send_velocity(); player.get_entity().send_velocity();
if !self.phase.lock().await.is_sitting() { if !self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_sitting()
{
player.damage(self, 5.0, DamageType::MOB_ATTACK); player.damage(self, 5.0, DamageType::MOB_ATTACK);
} }
} }
@ -596,8 +645,11 @@ impl EnderDragonEntity {
} }
} }
async fn tick_block_breaking(&self) { fn tick_block_breaking(&self) {
let phase_type = *self.phase.lock().await; let phase_type = *self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if phase_type.is_sitting() || phase_type == EnderDragonPhase::Dying { if phase_type.is_sitting() || phase_type == EnderDragonPhase::Dying {
return; return;
} }
@ -622,9 +674,11 @@ impl EnderDragonEntity {
} }
} }
async fn tick_parts(&self) { fn tick_parts(&self) {
let history: tokio::sync::MutexGuard<'_, DragonFlightHistory> = let history = self
self.flight_history.lock().await; .flight_history
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let p5 = history.get(5); let p5 = history.get(5);
let p10 = history.get(10); let p10 = history.get(10);
let p0 = history.get(0); let p0 = history.get(0);
@ -657,13 +711,21 @@ impl EnderDragonEntity {
pos.z - ss1 * 4.5, pos.z - ss1 * 4.5,
)); ));
let head_y_offset = if self.phase.lock().await.is_sitting() { let head_y_offset = if self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_sitting()
{
-1.0 -1.0
} else { } else {
(p5.y - p0.y) as f64 (p5.y - p0.y) as f64
}; };
let yaw_accel = *self.yaw_rot_accel.lock().await; let yaw_accel = *self
.yaw_rot_accel
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let rot2 = (yaw - yaw_accel * 0.01) * (std::f32::consts::PI / 180.0); let rot2 = (yaw - yaw_accel * 0.01) * (std::f32::consts::PI / 180.0);
let ss2 = rot2.sin() as f64; let ss2 = rot2.sin() as f64;
let cc2 = rot2.cos() as f64; let cc2 = rot2.cos() as f64;
@ -701,18 +763,21 @@ impl EnderDragonEntity {
} }
} }
pub async fn ai_step(&self) { pub fn ai_step(&self) {
self.mob_entity.living_entity.entity.update_last_pos(); self.mob_entity.living_entity.entity.update_last_pos();
self.ensure_nodes_initialized().await; self.ensure_nodes_initialized();
self.update_flap_time().await; self.update_flap_time();
{ {
let y = self.mob_entity.living_entity.entity.pos.load().y; let y = self.mob_entity.living_entity.entity.pos.load().y;
let yaw = self.mob_entity.living_entity.entity.yaw.load(); let yaw = self.mob_entity.living_entity.entity.yaw.load();
self.flight_history.lock().await.record(y, yaw); self.flight_history
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.record(y, yaw);
}; };
self.tick_growl().await; self.tick_growl();
self.tick_crystal_healing(); self.tick_crystal_healing();
{ {
@ -725,23 +790,32 @@ impl EnderDragonEntity {
} }
} }
let phase_type: EnderDragonPhase = *self.phase.lock().await; let phase_type: EnderDragonPhase = *self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let phase = self.phase_manager.get_phase(phase_type); let phase = self.phase_manager.get_phase(phase_type);
if phase_type.is_sitting() { if phase_type.is_sitting() {
*self.ticks_sitting.lock().await += 1; *self
.ticks_sitting
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) += 1;
} else { } else {
self.handle_player_collisions().await; self.handle_player_collisions();
self.tick_block_breaking().await; self.tick_block_breaking();
} }
phase.tick(self).await; phase.tick(self);
if phase_type == EnderDragonPhase::Dying { if phase_type == EnderDragonPhase::Dying {
return; return;
} }
let target_location = *self.target_location.lock().await; let target_location = *self
.target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(target) = target_location { if let Some(target) = target_location {
let pos = self.mob_entity.living_entity.entity.pos.load(); let pos = self.mob_entity.living_entity.entity.pos.load();
self.steer_toward( self.steer_toward(
@ -750,18 +824,23 @@ impl EnderDragonEntity {
phase.get_fly_speed(), phase.get_fly_speed(),
phase.get_turn_speed(), phase.get_turn_speed(),
0.5, 0.5,
) );
.await;
} }
self.mob_entity.living_entity.entity.send_pos_rot(); self.mob_entity.living_entity.entity.send_pos_rot();
self.tick_parts().await; self.tick_parts();
} }
pub async fn hurt(&self, damage: f32) { pub fn hurt(&self, damage: f32) {
let phase_type: EnderDragonPhase = *self.phase.lock().await; let phase_type: EnderDragonPhase = *self
.phase
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if phase_type.is_sitting() { if phase_type.is_sitting() {
*self.sitting_damage_received.lock().await += damage; *self
.sitting_damage_received
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) += damage;
} }
} }
} }
@ -772,31 +851,13 @@ impl Mob for EnderDragonEntity {
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) {
let entity_id = self.mob_entity.living_entity.entity.entity_id; self.ai_step();
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::<Self>() else {
return;
};
dragon.ai_step().await;
});
} }
fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) {
let living = &self.mob_entity.living_entity; let living = &self.mob_entity.living_entity;
if living.health.load() <= 0.0 { if living.health.load() <= 0.0 {
let world = self.mob_entity.living_entity.entity.world.load_full(); self.set_phase(EnderDragonPhase::Dying);
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::<Self>()
{
dragon.set_phase(EnderDragonPhase::Dying).await;
}
});
} }
} }

View File

@ -1,7 +1,6 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::EntityBase; use crate::entity::EntityBase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
pub struct ChargingPhase; pub struct ChargingPhase;
@ -11,10 +10,12 @@ impl super::Phase for ChargingPhase {
EnderDragonPhase::Charging EnderDragonPhase::Charging
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let target_id = { let target_id = {
let guard = dragon.target_player.lock().await; let guard = dragon
.target_player
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*guard *guard
}; };
let world = dragon.mob_entity.living_entity.entity.world.load(); let world = dragon.mob_entity.living_entity.entity.world.load();
@ -26,19 +27,24 @@ impl super::Phase for ChargingPhase {
player.get_entity().pos.load() player.get_entity().pos.load()
} else { } else {
let origin = { let origin = {
let guard = dragon.fight_origin.lock().await; let guard = dragon
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.0 guard.0
}; };
Vector3::new(origin.x as f64, NODE_Y as f64 - 20.0, origin.z as f64) Vector3::new(origin.x as f64, NODE_Y as f64 - 20.0, origin.z as f64)
}; };
if pos.distance_squared(target_pos) < 25.0 { if pos.distance_squared(target_pos) < 25.0 {
dragon.set_phase(EnderDragonPhase::Hovering).await; dragon.set_phase(EnderDragonPhase::Hovering);
return; return;
} }
*dragon.target_location.lock().await = Some(target_pos); *dragon
}) .target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target_pos);
} }
fn get_fly_speed(&self) -> f32 { fn get_fly_speed(&self) -> f32 {

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@ -11,10 +10,12 @@ impl super::Phase for CirclingPhase {
EnderDragonPhase::Circling EnderDragonPhase::Circling
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let pos = dragon.mob_entity.living_entity.entity.pos.load(); let pos = dragon.mob_entity.living_entity.entity.pos.load();
let mut target_location = dragon.target_location.lock().await; let mut target_location = dragon
.target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let d0 = target_location.map_or(0.0, |loc| pos.distance_squared(loc)); let d0 = target_location.map_or(0.0, |loc| pos.distance_squared(loc));
@ -27,13 +28,19 @@ impl super::Phase for CirclingPhase {
.horizontal_collision .horizontal_collision
.load(Ordering::Relaxed) .load(Ordering::Relaxed)
{ {
let mut path = dragon.path.lock().await; let mut path = dragon
.path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if path.is_empty() { if path.is_empty() {
drop(path); drop(path);
let i = dragon.find_closest_node().await; let i = dragon.find_closest_node();
let mut j = i as i32; let mut j = i as i32;
let mut clockwise = dragon.holding_pattern_clockwise.lock().await; let mut clockwise = dragon
.holding_pattern_clockwise
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if rand::random_range(0..8) == 0 { if rand::random_range(0..8) == 0 {
*clockwise = !*clockwise; *clockwise = !*clockwise;
j = i as i32 + 6; j = i as i32 + 6;
@ -60,8 +67,14 @@ impl super::Phase for CirclingPhase {
(j - 12).rem_euclid(8) as usize + 12 (j - 12).rem_euclid(8) as usize + 12
}; };
let mut path_lock = dragon.path.lock().await; let mut path_lock = dragon
let nodes = dragon.nodes.lock().await; .path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let nodes = dragon
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*path_lock = find_path(&nodes, i, j, None); *path_lock = find_path(&nodes, i, j, None);
drop(nodes); drop(nodes);
path = path_lock; path = path_lock;
@ -69,7 +82,10 @@ impl super::Phase for CirclingPhase {
if let Some(next_node_idx) = path.first().copied() { if let Some(next_node_idx) = path.first().copied() {
path.remove(0); path.remove(0);
let nodes = dragon.nodes.lock().await; let nodes = dragon
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(node) = nodes[next_node_idx] { if let Some(node) = nodes[next_node_idx] {
let mut y_target = node.y + rand::random_range(0.0..20.0); let mut y_target = node.y + rand::random_range(0.0..20.0);
while y_target < node.y { while y_target < node.y {
@ -83,12 +99,15 @@ impl super::Phase for CirclingPhase {
if rand::random_range(0..64) == 0 { if rand::random_range(0..64) == 0 {
if rand::random_bool(0.5) { if rand::random_bool(0.5) {
dragon.set_phase(EnderDragonPhase::FlyToPortal).await; dragon.set_phase(EnderDragonPhase::FlyToPortal);
} else if let Some(player) = dragon.find_nearest_player() { } else if let Some(player) = dragon.find_nearest_player() {
*dragon.target_player.lock().await = Some(player.gameprofile.id); *dragon
dragon.set_phase(EnderDragonPhase::Strafing).await; .target_player
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) =
Some(player.gameprofile.id);
dragon.set_phase(EnderDragonPhase::Strafing);
} }
} }
})
} }
} }

View File

@ -1,7 +1,6 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{DEATH_TIMER_MAX, EnderDragonEntity}; use crate::entity::boss::ender_dragon::{DEATH_TIMER_MAX, EnderDragonEntity};
use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::experience_orb::ExperienceOrbEntity;
use futures::future::BoxFuture;
use pumpkin_data::particle::Particle; use pumpkin_data::particle::Particle;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
@ -13,15 +12,18 @@ impl super::Phase for DyingPhase {
EnderDragonPhase::Dying EnderDragonPhase::Dying
} }
fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { *dragon
*dragon.target_location.lock().await = None; .target_location
}) .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { let mut t = dragon
let mut t = dragon.dragon_death_time.lock().await; .dragon_death_time
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*t += 1; *t += 1;
let entity = &dragon.mob_entity.living_entity.entity; let entity = &dragon.mob_entity.living_entity.entity;
@ -65,21 +67,13 @@ impl super::Phase for DyingPhase {
}; };
if *t > 150 && *t % 5 == 0 { if *t > 150 && *t % 5 == 0 {
ExperienceOrbEntity::spawn( ExperienceOrbEntity::spawn(&world, entity.pos.load(), (xp_count as f32 * 0.08) as u32);
&world,
entity.pos.load(),
(xp_count as f32 * 0.08) as u32,
);
} }
entity.velocity.store(Vector3::new(0.0, 0.1, 0.0)); entity.velocity.store(Vector3::new(0.0, 0.1, 0.0));
if *t >= DEATH_TIMER_MAX { if *t >= DEATH_TIMER_MAX {
ExperienceOrbEntity::spawn( ExperienceOrbEntity::spawn(&world, entity.pos.load(), (xp_count as f32 * 0.2) as u32);
&world,
entity.pos.load(),
(xp_count as f32 * 0.2) as u32,
);
if let Some(ref fight_mutex) = world.dragon_fight { if let Some(ref fight_mutex) = world.dragon_fight {
fight_mutex fight_mutex
@ -92,6 +86,5 @@ impl super::Phase for DyingPhase {
} }
entity.remove(); entity.remove();
} }
})
} }
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
pub struct FlyToPortalPhase; pub struct FlyToPortalPhase;
@ -10,21 +9,25 @@ impl super::Phase for FlyToPortalPhase {
EnderDragonPhase::FlyToPortal EnderDragonPhase::FlyToPortal
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let origin = { let origin = {
let guard = dragon.fight_origin.lock().await; let guard = dragon
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.0 guard.0
}; };
let target = Vector3::new(origin.x as f64, origin.y as f64 + 10.0, origin.z as f64); let target = Vector3::new(origin.x as f64, origin.y as f64 + 10.0, origin.z as f64);
let pos = dragon.mob_entity.living_entity.entity.pos.load(); let pos = dragon.mob_entity.living_entity.entity.pos.load();
if pos.distance_squared(target) < 4.0 { if pos.distance_squared(target) < 4.0 {
dragon.set_phase(EnderDragonPhase::LandingApproach).await; dragon.set_phase(EnderDragonPhase::LandingApproach);
return; return;
} }
*dragon.target_location.lock().await = Some(target); *dragon
}) .target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target);
} }
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
pub struct HoveringPhase; pub struct HoveringPhase;
@ -10,20 +9,24 @@ impl super::Phase for HoveringPhase {
EnderDragonPhase::Hovering EnderDragonPhase::Hovering
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let origin = { let origin = {
let guard = dragon.fight_origin.lock().await; let guard = dragon
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.0 guard.0
}; };
let target = Vector3::new(origin.x as f64, NODE_Y as f64 + 10.0, origin.z as f64); let target = Vector3::new(origin.x as f64, NODE_Y as f64 + 10.0, origin.z as f64);
if rand::random_bool(0.01) { if rand::random_bool(0.01) {
dragon.set_phase(EnderDragonPhase::TakingOff).await; dragon.set_phase(EnderDragonPhase::TakingOff);
return; return;
} }
*dragon.target_location.lock().await = Some(target); *dragon
}) .target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target);
} }
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::EnderDragonEntity; use crate::entity::boss::ender_dragon::EnderDragonEntity;
use futures::future::BoxFuture;
pub struct LandingPhase; pub struct LandingPhase;
@ -9,17 +8,22 @@ impl super::Phase for LandingPhase {
EnderDragonPhase::Landing EnderDragonPhase::Landing
} }
fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { *dragon
*dragon.target_location.lock().await = None; .target_location
}) .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { dragon.set_phase(EnderDragonPhase::SitAttacking);
dragon.set_phase(EnderDragonPhase::SitAttacking).await; *dragon
*dragon.ticks_sitting.lock().await = 0; .ticks_sitting
*dragon.sit_attack_timer.lock().await = 0; .lock()
}) .unwrap_or_else(std::sync::PoisonError::into_inner) = 0;
*dragon
.sit_attack_timer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = 0;
} }
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
pub struct LandingApproachPhase; pub struct LandingApproachPhase;
@ -10,21 +9,25 @@ impl super::Phase for LandingApproachPhase {
EnderDragonPhase::LandingApproach EnderDragonPhase::LandingApproach
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let origin = { let origin = {
let guard = dragon.fight_origin.lock().await; let guard = dragon
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.0 guard.0
}; };
let target = Vector3::new(origin.x as f64, origin.y as f64, origin.z as f64); let target = Vector3::new(origin.x as f64, origin.y as f64, origin.z as f64);
let pos = dragon.mob_entity.living_entity.entity.pos.load(); let pos = dragon.mob_entity.living_entity.entity.pos.load();
if pos.distance_squared(target) < 1.0 { if pos.distance_squared(target) < 1.0 {
dragon.set_phase(EnderDragonPhase::Landing).await; dragon.set_phase(EnderDragonPhase::Landing);
return; return;
} }
*dragon.target_location.lock().await = Some(target); *dragon
}) .target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target);
} }
} }

View File

@ -1,5 +1,4 @@
use crate::entity::boss::ender_dragon::EnderDragonEntity; use crate::entity::boss::ender_dragon::EnderDragonEntity;
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use std::sync::Arc; use std::sync::Arc;
@ -29,13 +28,9 @@ pub use taking_off::TakingOffPhase;
pub trait Phase: Send + Sync { pub trait Phase: Send + Sync {
fn get_type(&self) -> EnderDragonPhase; fn get_type(&self) -> EnderDragonPhase;
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()>; fn tick(&self, dragon: &EnderDragonEntity);
fn begin<'a>(&'a self, _dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, _dragon: &EnderDragonEntity) {}
Box::pin(async {}) fn end(&self, _dragon: &EnderDragonEntity) {}
}
fn end<'a>(&'a self, _dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> {
Box::pin(async {})
}
fn is_sitting(&self) -> bool { fn is_sitting(&self) -> bool {
false false
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::EnderDragonEntity; use crate::entity::boss::ender_dragon::EnderDragonEntity;
use futures::future::BoxFuture;
pub struct SitAttackingPhase; pub struct SitAttackingPhase;
@ -9,39 +8,51 @@ impl super::Phase for SitAttackingPhase {
EnderDragonPhase::SitAttacking EnderDragonPhase::SitAttacking
} }
fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { *dragon
*dragon.target_location.lock().await = None; .target_location
}) .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { let mut timer = dragon
let mut timer = dragon.sit_attack_timer.lock().await; .sit_attack_timer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*timer += 1; *timer += 1;
if *timer > 40 { if *timer > 40 {
*timer = 0; *timer = 0;
let should_breathe = rand::random_bool(0.5); let should_breathe = rand::random_bool(0.5);
let should_take_off = *dragon.ticks_sitting.lock().await > 200; let should_take_off = *dragon
.ticks_sitting
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
> 200;
drop(timer); drop(timer);
if should_breathe { if should_breathe {
dragon.set_phase(EnderDragonPhase::SitBreathing).await; dragon.set_phase(EnderDragonPhase::SitBreathing);
*dragon.breathing_timer.lock().await = 0; *dragon
.breathing_timer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = 0;
} else if should_take_off { } else if should_take_off {
dragon.set_phase(EnderDragonPhase::TakingOff).await; dragon.set_phase(EnderDragonPhase::TakingOff);
} }
} else { } else {
drop(timer); drop(timer);
} }
let mut dmg = dragon.sitting_damage_received.lock().await; let mut dmg = dragon
.sitting_damage_received
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *dmg > 150.0 { if *dmg > 150.0 {
*dmg = 0.0; *dmg = 0.0;
drop(dmg); drop(dmg);
dragon.set_phase(EnderDragonPhase::TakingOff).await; dragon.set_phase(EnderDragonPhase::TakingOff);
} }
})
} }
} }

View File

@ -2,7 +2,6 @@ use super::EnderDragonPhase;
use crate::entity::{ use crate::entity::{
Entity, area_effect_cloud::AreaEffectCloudEntity, boss::ender_dragon::EnderDragonEntity, Entity, area_effect_cloud::AreaEffectCloudEntity, boss::ender_dragon::EnderDragonEntity,
}; };
use futures::future::BoxFuture;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
@ -13,26 +12,32 @@ impl super::Phase for SitBreathingPhase {
EnderDragonPhase::SitBreathing EnderDragonPhase::SitBreathing
} }
fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { *dragon
*dragon.target_location.lock().await = None; .target_location
}) .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { let mut timer = dragon
let mut timer = dragon.breathing_timer.lock().await; .breathing_timer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*timer += 1; *timer += 1;
if *timer > 100 { if *timer > 100 {
*timer = 0; *timer = 0;
drop(timer); drop(timer);
dragon.set_phase(EnderDragonPhase::SitAttacking).await; dragon.set_phase(EnderDragonPhase::SitAttacking);
return; return;
} }
drop(timer); drop(timer);
let timer_val = *dragon.breathing_timer.lock().await; let timer_val = *dragon
.breathing_timer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if timer_val == 1 { if timer_val == 1 {
let entity = &dragon.mob_entity.living_entity.entity; let entity = &dragon.mob_entity.living_entity.entity;
let pos = entity.pos.load(); let pos = entity.pos.load();
@ -68,6 +73,5 @@ impl super::Phase for SitBreathingPhase {
); );
world.spawn_entity(cloud); world.spawn_entity(cloud);
} }
})
} }
} }

View File

@ -5,7 +5,6 @@ use crate::entity::{
area_effect_cloud::AreaEffectCloudEntity, area_effect_cloud::AreaEffectCloudEntity,
boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path}, boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path},
}; };
use futures::future::BoxFuture;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@ -17,17 +16,23 @@ impl super::Phase for StrafingPhase {
EnderDragonPhase::Strafing EnderDragonPhase::Strafing
} }
fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn begin(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { *dragon
*dragon.fireball_charge.lock().await = 0; .fireball_charge
*dragon.target_location.lock().await = None; .lock()
}) .unwrap_or_else(std::sync::PoisonError::into_inner) = 0;
*dragon
.target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move { let target_id = *dragon
let target_id = *dragon.target_player.lock().await; .target_player
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let world = dragon.mob_entity.living_entity.entity.world.load(); let world = dragon.mob_entity.living_entity.entity.world.load();
let pos = dragon.mob_entity.living_entity.entity.pos.load(); let pos = dragon.mob_entity.living_entity.entity.pos.load();
@ -43,21 +48,26 @@ impl super::Phase for StrafingPhase {
}; };
let Some(player) = player_target else { let Some(player) = player_target else {
dragon.set_phase(EnderDragonPhase::Circling).await; dragon.set_phase(EnderDragonPhase::Circling);
return; return;
}; };
let player_pos = player.get_entity().pos.load(); let player_pos = player.get_entity().pos.load();
let mut path = dragon.path.lock().await; let mut path = dragon
let mut target_location = dragon.target_location.lock().await; .path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut target_location = dragon
.target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if path.is_empty() { if path.is_empty() {
let d2 = player_pos.x - pos.x; let d2 = player_pos.x - pos.x;
let d3 = player_pos.z - pos.z; let d3 = player_pos.z - pos.z;
let d4 = d2.hypot(d3); let d4 = d2.hypot(d3);
let d5 = (0.4 + d4 / 80.0 - 1.0).clamp(0.0, 10.0); let d5 = (0.4 + d4 / 80.0 - 1.0).clamp(0.0, 10.0);
*target_location = *target_location = Some(Vector3::new(player_pos.x, player_pos.y + d5, player_pos.z));
Some(Vector3::new(player_pos.x, player_pos.y + d5, player_pos.z));
} }
let d11 = target_location let d11 = target_location
@ -73,11 +83,17 @@ impl super::Phase for StrafingPhase {
{ {
if path.is_empty() { if path.is_empty() {
drop(path); drop(path);
let i = dragon.find_closest_node().await; let i = dragon.find_closest_node();
let j = dragon.find_closest_node_to(player_pos).await; let j = dragon.find_closest_node_to(player_pos);
let mut path_lock = dragon.path.lock().await; let mut path_lock = dragon
let nodes = dragon.nodes.lock().await; .path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let nodes = dragon
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*path_lock = find_path(&nodes, i, j, None); *path_lock = find_path(&nodes, i, j, None);
drop(nodes); drop(nodes);
path = path_lock; path = path_lock;
@ -85,7 +101,10 @@ impl super::Phase for StrafingPhase {
if let Some(next_node_idx) = path.first().copied() { if let Some(next_node_idx) = path.first().copied() {
path.remove(0); path.remove(0);
let nodes = dragon.nodes.lock().await; let nodes = dragon
.nodes
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(node) = nodes[next_node_idx] { if let Some(node) = nodes[next_node_idx] {
let mut y_target = node.y + rand::random_range(0.0..20.0); let mut y_target = node.y + rand::random_range(0.0..20.0);
while y_target < node.y { while y_target < node.y {
@ -99,7 +118,10 @@ impl super::Phase for StrafingPhase {
drop(target_location); drop(target_location);
if player_pos.distance_squared(pos) < 4096.0 { if player_pos.distance_squared(pos) < 4096.0 {
let mut charge = dragon.fireball_charge.lock().await; let mut charge = dragon
.fireball_charge
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let aim_diff = player_pos - pos; let aim_diff = player_pos - pos;
let aim = if aim_diff.length_squared() > 1e-6 { let aim = if aim_diff.length_squared() > 1e-6 {
@ -155,15 +177,21 @@ impl super::Phase for StrafingPhase {
); );
world.spawn_entity(cloud); world.spawn_entity(cloud);
dragon.path.lock().await.clear(); dragon
dragon.set_phase(EnderDragonPhase::Circling).await; .path
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clear();
dragon.set_phase(EnderDragonPhase::Circling);
} }
} else { } else {
let mut charge = dragon.fireball_charge.lock().await; let mut charge = dragon
.fireball_charge
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *charge > 0 { if *charge > 0 {
*charge -= 1; *charge -= 1;
} }
} }
})
} }
} }

View File

@ -1,6 +1,5 @@
use super::EnderDragonPhase; use super::EnderDragonPhase;
use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext}; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext};
use futures::future::BoxFuture;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
pub struct TakingOffPhase; pub struct TakingOffPhase;
@ -10,21 +9,25 @@ impl super::Phase for TakingOffPhase {
EnderDragonPhase::TakingOff EnderDragonPhase::TakingOff
} }
fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { fn tick(&self, dragon: &EnderDragonEntity) {
Box::pin(async move {
let origin = { let origin = {
let guard = dragon.fight_origin.lock().await; let guard = dragon
.fight_origin
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.0 guard.0
}; };
let target = Vector3::new(origin.x as f64, NODE_Y as f64, origin.z as f64); let target = Vector3::new(origin.x as f64, NODE_Y as f64, origin.z as f64);
let pos = dragon.mob_entity.living_entity.entity.pos.load(); let pos = dragon.mob_entity.living_entity.entity.pos.load();
if pos.distance_squared(target) < 16.0 { if pos.distance_squared(target) < 16.0 {
dragon.set_phase(EnderDragonPhase::Circling).await; dragon.set_phase(EnderDragonPhase::Circling);
return; return;
} }
*dragon.target_location.lock().await = Some(target); *dragon
}) .target_location
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target);
} }
} }

View File

@ -1,8 +1,7 @@
use std::sync::{ use std::sync::{
Arc, Weak, Arc, Mutex, Weak,
atomic::{AtomicBool, AtomicI32, Ordering}, atomic::{AtomicBool, AtomicI32, Ordering},
}; };
use tokio::sync::Mutex;
use uuid::Uuid; use uuid::Uuid;
use pumpkin_data::{ use pumpkin_data::{
@ -26,7 +25,7 @@ use pumpkin_world::world::BlockFlags;
use crate::{ use crate::{
entity::{ entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal,
revenge::RevengeGoal, revenge::RevengeGoal,
@ -259,7 +258,7 @@ impl WitherEntity {
} }
} }
async fn update_bossbar(&self, world: &Arc<crate::world::World>, progress: f32) { fn update_bossbar(&self, world: &Arc<crate::world::World>, progress: f32) {
let pos = self.mob_entity.living_entity.entity.pos.load(); let pos = self.mob_entity.living_entity.entity.pos.load();
let tracking_radius_sq = 50.0 * 50.0; let tracking_radius_sq = 50.0 * 50.0;
let players = world.players.load(); let players = world.players.load();
@ -273,7 +272,10 @@ impl WitherEntity {
.map(|p| p.gameprofile.id) .map(|p| p.gameprofile.id)
.collect(); .collect();
let mut bossbar_players = self.bossbar_players.lock().await; let mut bossbar_players = self
.bossbar_players
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for &uid in &current { for &uid in &current {
if !bossbar_players.contains(&uid) { if !bossbar_players.contains(&uid) {
@ -306,8 +308,11 @@ impl WitherEntity {
} }
} }
async fn remove_all_bossbar(&self, world: &Arc<crate::world::World>) { fn remove_all_bossbar(&self, world: &Arc<crate::world::World>) {
let mut bossbar_players = self.bossbar_players.lock().await; let mut bossbar_players = self
.bossbar_players
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let players = world.players.load(); let players = world.players.load();
for player in players.iter() { for player in players.iter() {
if bossbar_players.contains(&player.gameprofile.id) { if bossbar_players.contains(&player.gameprofile.id) {
@ -318,18 +323,18 @@ impl WitherEntity {
} }
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
async fn async_mob_tick(&self) { fn tick_wither(&self) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
let world = entity.world.load(); let world = entity.world.load();
if world.level_info.load().difficulty == Difficulty::Peaceful { if world.level_info.load().difficulty == Difficulty::Peaceful {
self.remove_all_bossbar(&world).await; self.remove_all_bossbar(&world);
entity.remove(); entity.remove();
return; return;
} }
if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 {
self.remove_all_bossbar(&world).await; self.remove_all_bossbar(&world);
if !self.dropped_loot.swap(true, Ordering::SeqCst) { if !self.dropped_loot.swap(true, Ordering::SeqCst) {
let pos = entity.block_pos.load(); let pos = entity.block_pos.load();
world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR));
@ -343,18 +348,16 @@ impl WitherEntity {
if invul > 0 { if invul > 0 {
let new_count = invul - 1; let new_count = invul - 1;
let progress = (1.0 - (new_count as f32) / 220.0).clamp(0.0, 1.0); let progress = (1.0 - (new_count as f32) / 220.0).clamp(0.0, 1.0);
self.update_bossbar(&world, progress).await; self.update_bossbar(&world, progress);
if new_count <= 0 { if new_count <= 0 {
let pos = entity.pos.load(); let pos = entity.pos.load();
let eye_y = pos.y + entity.get_eye_height(); let eye_y = pos.y + entity.get_eye_height();
world world.explode(
.explode(
Vector3::new(pos.x, eye_y, pos.z), Vector3::new(pos.x, eye_y, pos.z),
7.0, 7.0,
ExplosionInteraction::Mob, ExplosionInteraction::Mob,
) );
.await;
if !entity.silent.load(Ordering::Relaxed) { if !entity.silent.load(Ordering::Relaxed) {
world.sync_world_event( world.sync_world_event(
@ -378,7 +381,7 @@ impl WitherEntity {
} else { } else {
0.0 0.0
}; };
self.update_bossbar(&world, progress).await; self.update_bossbar(&world, progress);
if tick_count % 20 == 0 { if tick_count % 20 == 0 {
living.heal(1.0); living.heal(1.0);
@ -604,17 +607,7 @@ impl Mob for WitherEntity {
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) {
let entity_id = self.mob_entity.living_entity.entity.entity_id; self.tick_wither();
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(wither) = entity.cast_any().downcast_ref::<Self>() else {
return;
};
wither.async_mob_tick().await;
});
} }
fn pre_damage(&self, damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool { fn pre_damage(&self, damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool {
@ -662,35 +655,22 @@ impl Mob for WitherEntity {
fn post_tick(&self) { fn post_tick(&self) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { 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(); let world = entity.world.load_full();
tokio::spawn(async move { self.remove_all_bossbar(&world);
let Some(entity) = world.get_entity_by_id(entity_id) else { if !self.dropped_loot.swap(true, Ordering::SeqCst) {
return;
};
let Some(wither) = entity.cast_any().downcast_ref::<Self>() else {
return;
};
wither.remove_all_bossbar(&world).await;
if !wither.dropped_loot.swap(true, Ordering::SeqCst) {
let pos = entity.get_entity().block_pos.load(); let pos = entity.get_entity().block_pos.load();
world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR));
} }
});
} }
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
nbt.put_int("Invul", self.get_invulnerable_ticks()); nbt.put_int("Invul", self.get_invulnerable_ticks());
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(invul) = nbt.get_int("Invul") { if let Some(invul) = nbt.get_int("Invul") {
self.set_invulnerable_ticks(invul); self.set_invulnerable_ticks(invul);
} }
})
} }
} }

View File

@ -74,9 +74,7 @@ impl BreathManager {
player.entity_id(), player.entity_id(),
new_air, new_air,
); );
tokio::spawn(async move { server.plugin_manager.fire_blocking(&server, &mut event);
server.plugin_manager.fire(&server, &mut event).await;
});
} }
self.send_air_supply(player); self.send_air_supply(player);
} }

View File

@ -1,6 +1,6 @@
use std::sync::atomic::{AtomicI32, AtomicI64, AtomicU8, Ordering}; use std::sync::atomic::{AtomicI32, AtomicI64, AtomicU8, Ordering};
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}; use crate::entity::{Entity, EntityBase, living::LivingEntity};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::{ use pumpkin_data::{
@ -230,8 +230,7 @@ impl ArmorStandEntity {
} }
impl EntityBase for ArmorStandEntity { impl EntityBase for ArmorStandEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
let disabled_slots = self.disabled_slots.load(Ordering::Relaxed); let disabled_slots = self.disabled_slots.load(Ordering::Relaxed);
// ... // ...
@ -245,11 +244,9 @@ impl EntityBase for ArmorStandEntity {
} }
nbt.put("Pose", self.pack_rotation()); nbt.put("Pose", self.pack_rotation());
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
let mut flags = 0u8; let mut flags = 0u8;
// ... // ...
@ -295,7 +292,6 @@ impl EntityBase for ArmorStandEntity {
let packed: PackedRotation = pose_tag.clone().into(); let packed: PackedRotation = pose_tag.clone().into();
self.unpack_rotation(&packed); self.unpack_rotation(&packed);
} }
})
} }
fn get_entity(&self) -> &Entity { fn get_entity(&self) -> &Entity {
@ -306,11 +302,9 @@ impl EntityBase for ArmorStandEntity {
Some(&self.living_entity) Some(&self.living_entity)
} }
fn kill<'a>(&'a self, _caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { fn kill(&self, _caller: &dyn EntityBase) {
Box::pin(async move {
self.get_entity().remove(); self.get_entity().remove();
// TODO: emit GameEvent::ENTITY_DIE // TODO: emit GameEvent::ENTITY_DIE
})
} }
fn damage_with_context( fn damage_with_context(

View File

@ -13,7 +13,7 @@ use pumpkin_protocol::{
use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
use crate::{ use crate::{
entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}, entity::{Entity, EntityBase, living::LivingEntity},
server::Server, server::Server,
}; };
@ -796,20 +796,16 @@ impl BlockDisplayEntity {
} }
impl EntityBase for BlockDisplayEntity { impl EntityBase for BlockDisplayEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.display.write_display_nbt(nbt); self.display.write_display_nbt(nbt);
nbt.put_int("block_state", self.block_state.load(Ordering::Relaxed)); nbt.put_int("block_state", self.block_state.load(Ordering::Relaxed));
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.display.read_display_nbt(nbt); self.display.read_display_nbt(nbt);
if let Some(state) = nbt.get_int("block_state") { if let Some(state) = nbt.get_int("block_state") {
self.block_state.store(state, Ordering::Relaxed); self.block_state.store(state, Ordering::Relaxed);
} }
})
} }
fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {} fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {}
@ -919,8 +915,7 @@ impl ItemDisplayEntity {
} }
impl EntityBase for ItemDisplayEntity { impl EntityBase for ItemDisplayEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.display.write_display_nbt(nbt); self.display.write_display_nbt(nbt);
let display_mode_str = match self.item_display.load(Ordering::Relaxed) { let display_mode_str = match self.item_display.load(Ordering::Relaxed) {
1 => "thirdperson_lefthand", 1 => "thirdperson_lefthand",
@ -934,11 +929,9 @@ impl EntityBase for ItemDisplayEntity {
_ => "none", _ => "none",
}; };
nbt.put_string("item_display", display_mode_str.to_string()); nbt.put_string("item_display", display_mode_str.to_string());
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.display.read_display_nbt(nbt); self.display.read_display_nbt(nbt);
if let Some(mode_str) = nbt.get_string("item_display") { if let Some(mode_str) = nbt.get_string("item_display") {
let mode = match mode_str { let mode = match mode_str {
@ -954,7 +947,6 @@ impl EntityBase for ItemDisplayEntity {
}; };
self.item_display.store(mode, Ordering::Relaxed); self.item_display.store(mode, Ordering::Relaxed);
} }
})
} }
fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {} fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {}
@ -1203,8 +1195,7 @@ impl TextDisplayEntity {
} }
impl EntityBase for TextDisplayEntity { impl EntityBase for TextDisplayEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.display.write_display_nbt(nbt); self.display.write_display_nbt(nbt);
let text_json_res = pumpkin_util::serde_json::to_string( let text_json_res = pumpkin_util::serde_json::to_string(
&*self &*self
@ -1231,11 +1222,9 @@ impl EntityBase for TextDisplayEntity {
"center" "center"
}; };
nbt.put_string("alignment", align_str.to_string()); nbt.put_string("alignment", align_str.to_string());
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.display.read_display_nbt(nbt); self.display.read_display_nbt(nbt);
if let Some(text_json) = nbt.get_string("text") if let Some(text_json) = nbt.get_string("text")
&& let Ok(component) = pumpkin_util::serde_json::from_str(text_json) && let Ok(component) = pumpkin_util::serde_json::from_str(text_json)
@ -1273,7 +1262,6 @@ impl EntityBase for TextDisplayEntity {
} }
} }
self.flags.store(flags, Ordering::Relaxed); self.flags.store(flags, Ordering::Relaxed);
})
} }
fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {} fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {}

View File

@ -52,11 +52,7 @@ impl EntityBase for EndCrystalEntity {
if !damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) { if !damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) {
let world = self.entity.world.load(); let world = self.entity.world.load();
let pos = self.entity.pos.load(); let pos = self.entity.pos.load();
tokio::spawn(async move { world.explode(pos, 6.0, crate::world::ExplosionInteraction::Block);
world
.explode(pos, 6.0, crate::world::ExplosionInteraction::Block)
.await;
});
} }
// TODO // TODO

View File

@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}; use crate::entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
@ -282,8 +282,7 @@ impl ItemFrameEntity {
} }
impl EntityBase for ItemFrameEntity { impl EntityBase for ItemFrameEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
let item = self let item = self
.item_stack .item_stack
.lock() .lock()
@ -298,11 +297,9 @@ impl EntityBase for ItemFrameEntity {
nbt.put_byte("Facing", self.facing.load(Ordering::Relaxed) as i8); nbt.put_byte("Facing", self.facing.load(Ordering::Relaxed) as i8);
nbt.put_bool("Invisible", self.invisible.load(Ordering::Relaxed)); nbt.put_bool("Invisible", self.invisible.load(Ordering::Relaxed));
nbt.put_bool("Fixed", self.fixed.load(Ordering::Relaxed)); nbt.put_bool("Fixed", self.fixed.load(Ordering::Relaxed));
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
if let Some(item_compound) = nbt.get_compound("Item") if let Some(item_compound) = nbt.get_compound("Item")
&& let Some(stack) = ItemStack::read_item_stack(item_compound) && let Some(stack) = ItemStack::read_item_stack(item_compound)
{ {
@ -327,7 +324,6 @@ impl EntityBase for ItemFrameEntity {
); );
self.fixed self.fixed
.store(nbt.get_bool("Fixed").unwrap_or(false), Ordering::Relaxed); .store(nbt.get_bool("Fixed").unwrap_or(false), Ordering::Relaxed);
})
} }
fn get_entity(&self) -> &Entity { fn get_entity(&self) -> &Entity {
@ -405,12 +401,7 @@ impl EntityBase for ItemFrameEntity {
}) })
} }
fn interact<'a>( fn interact(&self, player: &Arc<Player>, item_stack: &mut ItemStack) -> bool {
&'a self,
player: &'a Arc<Player>,
item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
if self.is_fixed() { if self.is_fixed() {
return false; return false;
} }
@ -435,7 +426,6 @@ impl EntityBase for ItemFrameEntity {
} else { } else {
false false
} }
})
} }
fn damage_with_context( fn damage_with_context(

View File

@ -1,5 +1,5 @@
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}; use crate::entity::{Entity, EntityBase, living::LivingEntity};
use crate::world::World; use crate::world::World;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
@ -135,12 +135,7 @@ impl EntityBase for LeashKnotEntity {
} }
} }
fn interact<'a>( fn interact(&self, player: &Arc<Player>, _item_stack: &mut ItemStack) -> bool {
&'a self,
player: &'a Arc<Player>,
_item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
let world = player.world(); let world = player.world();
let knot_id = self.entity.entity_id; let knot_id = self.entity.entity_id;
let player_id = player.entity_id(); let player_id = player.entity_id();
@ -194,7 +189,6 @@ impl EntityBase for LeashKnotEntity {
} else { } else {
false false
} }
})
} }
fn cast_any(&self) -> &dyn std::any::Any { fn cast_any(&self) -> &dyn std::any::Any {
self self

View File

@ -1,7 +1,7 @@
use core::f32; use core::f32;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use crate::entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}; use crate::entity::{Entity, EntityBase, living::LivingEntity};
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
@ -17,17 +17,13 @@ impl PaintingEntity {
} }
impl EntityBase for PaintingEntity { impl EntityBase for PaintingEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8); nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8);
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
let facing = nbt.get_byte("facing").unwrap_or(3); let facing = nbt.get_byte("facing").unwrap_or(3);
self.entity.data.store(facing as i32, Ordering::Relaxed); self.entity.data.store(facing as i32, Ordering::Relaxed);
})
} }
fn get_entity(&self) -> &Entity { fn get_entity(&self) -> &Entity {

View File

@ -1,4 +1,4 @@
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct HungerMobEffect; pub struct HungerMobEffect;
@ -11,12 +11,7 @@ impl MobEffect for HungerMobEffect {
(duration as u32).is_multiple_of(20) (duration as u32).is_multiple_of(20)
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, amplifier: u8) {
&'a self,
living: &'a LivingEntity,
amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let world = living.entity.world.load(); let world = living.entity.world.load();
if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) if let Some(entity) = world.get_entity_by_id(living.entity.entity_id)
&& let Some(player) = entity.get_player() && let Some(player) = entity.get_player()
@ -24,6 +19,5 @@ impl MobEffect for HungerMobEffect {
let exhaustion = 0.1 * (f32::from(amplifier) + 1.0); let exhaustion = 0.1 * (f32::from(amplifier) + 1.0);
player.hunger_manager.add_exhaustion(exhaustion); player.hunger_manager.add_exhaustion(exhaustion);
} }
})
} }
} }

View File

@ -5,21 +5,20 @@ use pumpkin_data::entity::EntityType;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::r#type::from_type; use crate::entity::r#type::from_type;
pub struct InfestedMobEffect; pub struct InfestedMobEffect;
impl MobEffect for InfestedMobEffect { impl MobEffect for InfestedMobEffect {
fn on_mob_hurt<'a>( fn on_mob_hurt(
&'a self, &self,
living: &'a LivingEntity, living: &LivingEntity,
_amplifier: u8, _amplifier: u8,
_damage_type: &'a DamageType, _damage_type: &DamageType,
_damage_amount: f32, _damage_amount: f32,
) -> EffectFuture<'a, ()> { ) {
Box::pin(async move {
// Wither, ender dragon and silverfish are immune // Wither, ender dragon and silverfish are immune
if living.entity.entity_type == &EntityType::WITHER if living.entity.entity_type == &EntityType::WITHER
|| living.entity.entity_type == &EntityType::ENDER_DRAGON || living.entity.entity_type == &EntityType::ENDER_DRAGON
@ -53,21 +52,18 @@ impl MobEffect for InfestedMobEffect {
let rz = -vx * sin_a + vz * cos_a; let rz = -vx * sin_a + vz * cos_a;
let silver = from_type(&EntityType::SILVERFISH, center, &world, Uuid::new_v4()); let silver = from_type(&EntityType::SILVERFISH, center, &world, Uuid::new_v4());
let silver_entity = silver.get_entity();
let entity = silver.get_entity(); silver_entity.set_pos(center);
entity.set_pos(center); silver_entity.velocity.store(Vector3::new(
entity.yaw.store(rand::random::<f32>() * 360.0);
entity.pitch.store(0.0);
entity.velocity.store(Vector3::new(
f64::from(rx), f64::from(rx),
f64::from(vy), f64::from(vy),
f64::from(rz), f64::from(rz),
)); ));
world.spawn_entity(silver); world.spawn_entity(silver);
}
world.play_sound(Sound::EntitySilverfishHurt, SoundCategory::Hostile, &center); world.play_sound(Sound::EntitySilverfishHurt, SoundCategory::Hostile, &center);
} }
} }
})
}
} }

View File

@ -9,9 +9,6 @@ pub mod weaving;
pub mod wind_charged; pub mod wind_charged;
pub mod wither; pub mod wither;
use std::future::Future;
use std::pin::Pin;
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use pumpkin_data::effect::StatusEffect; use pumpkin_data::effect::StatusEffect;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
@ -19,9 +16,7 @@ use pumpkin_nbt::tag::NbtTag;
use tracing::warn; use tracing::warn;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::{NBTInitFuture, NBTStorage, NBTStorageInit, NbtFuture}; use crate::entity::{NBTStorage, NBTStorageInit};
pub type EffectFuture<'a, T = ()> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait MobEffect: Send + Sync { pub trait MobEffect: Send + Sync {
/// Returns true if `apply_effect_tick` should be called for the current tick and duration. /// Returns true if `apply_effect_tick` should be called for the current tick and duration.
@ -30,34 +25,20 @@ pub trait MobEffect: Send + Sync {
} }
/// Applies periodic/tick-based effect logic on a living entity. /// Applies periodic/tick-based effect logic on a living entity.
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, _living: &LivingEntity, _amplifier: u8) {}
&'a self,
_living: &'a LivingEntity,
_amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {})
}
/// Called when an entity carrying this effect is hurt. /// Called when an entity carrying this effect is hurt.
fn on_mob_hurt<'a>( fn on_mob_hurt(
&'a self, &self,
_living: &'a LivingEntity, _living: &LivingEntity,
_amplifier: u8, _amplifier: u8,
_damage_type: &'a DamageType, _damage_type: &DamageType,
_damage_amount: f32, _damage_amount: f32,
) -> EffectFuture<'a, ()> { ) {
Box::pin(async move {})
} }
/// Called when an entity carrying this effect dies. /// Called when an entity carrying this effect dies.
fn on_mob_death<'a>( fn on_mob_death(&self, _living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) {}
&'a self,
_living: &'a LivingEntity,
_amplifier: u8,
_damage_type: &'a DamageType,
) -> EffectFuture<'a, ()> {
Box::pin(async move {})
}
} }
pub static REGENERATION: regeneration::RegenerationMobEffect = regeneration::RegenerationMobEffect; pub static REGENERATION: regeneration::RegenerationMobEffect = regeneration::RegenerationMobEffect;
@ -99,8 +80,7 @@ pub fn get_mob_effect(effect: &'static StatusEffect) -> Option<&'static dyn MobE
} }
impl NBTStorage for pumpkin_data::potion::Effect { impl NBTStorage for pumpkin_data::potion::Effect {
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
nbt.put("id", self.effect_type.minecraft_name); nbt.put("id", self.effect_type.minecraft_name);
if self.amplifier > 0 { if self.amplifier > 0 {
nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier))); nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier)));
@ -114,16 +94,11 @@ impl NBTStorage for pumpkin_data::potion::Effect {
} }
let show_icon: i8 = i8::from(self.show_icon); let show_icon: i8 = i8::from(self.show_icon);
nbt.put("show_icon", NbtTag::Byte(show_icon)); nbt.put("show_icon", NbtTag::Byte(show_icon));
})
} }
} }
impl NBTStorageInit for pumpkin_data::potion::Effect { impl NBTStorageInit for pumpkin_data::potion::Effect {
fn create_from_nbt<'a>(nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self> fn create_from_nbt(nbt: &mut NbtCompound) -> Option<Self> {
where
Self: 'a,
{
Box::pin(async move {
let Some(effect_id) = nbt.get_string("id") else { let Some(effect_id) = nbt.get_string("id") else {
warn!("Unable to read effect. Effect id is not present"); warn!("Unable to read effect. Effect id is not present");
return None; return None;
@ -150,6 +125,5 @@ impl NBTStorageInit for pumpkin_data::potion::Effect {
show_icon, show_icon,
blend: false, blend: false,
}) })
})
} }
} }

View File

@ -4,7 +4,7 @@ use pumpkin_data::damage::DamageType;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::mob::slime::SlimeEntity; use crate::entity::mob::slime::SlimeEntity;
use crate::entity::r#type::from_type; use crate::entity::r#type::from_type;
@ -12,13 +12,7 @@ use crate::entity::r#type::from_type;
pub struct OozingMobEffect; pub struct OozingMobEffect;
impl MobEffect for OozingMobEffect { impl MobEffect for OozingMobEffect {
fn on_mob_death<'a>( fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
_damage_type: &'a DamageType,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
// Slimes are immune // Slimes are immune
if living.entity.entity_type == &EntityType::SLIME { if living.entity.entity_type == &EntityType::SLIME {
return; return;
@ -42,6 +36,5 @@ impl MobEffect for OozingMobEffect {
world.spawn_entity(entity_arc); world.spawn_entity(entity_arc);
} }
})
} }
} }

View File

@ -1,6 +1,6 @@
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct PoisonMobEffect; pub struct PoisonMobEffect;
@ -18,12 +18,7 @@ impl MobEffect for PoisonMobEffect {
} }
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let current_health = living.health.load(); let current_health = living.health.load();
if current_health > 1.0 if current_health > 1.0
&& let Some(dyn_self) = living && let Some(dyn_self) = living
@ -37,6 +32,5 @@ impl MobEffect for PoisonMobEffect {
dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC); dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC);
} }
} }
})
} }
} }

View File

@ -1,4 +1,4 @@
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct RaidOmenMobEffect; pub struct RaidOmenMobEffect;
@ -8,12 +8,7 @@ impl MobEffect for RaidOmenMobEffect {
duration == 1 duration == 1
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let world = living.entity.world.load(); let world = living.entity.world.load();
if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) if let Some(entity) = world.get_entity_by_id(living.entity.entity_id)
&& let Some(player) = entity.get_player() && let Some(player) = entity.get_player()
@ -29,6 +24,5 @@ impl MobEffect for RaidOmenMobEffect {
raids.create_or_extend_raid(player, raid_pos, &world); raids.create_or_extend_raid(player, raid_pos, &world);
player.clear_raid_omen_position(); player.clear_raid_omen_position();
} }
})
} }
} }

View File

@ -1,4 +1,4 @@
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct RegenerationMobEffect; pub struct RegenerationMobEffect;
@ -16,17 +16,11 @@ impl MobEffect for RegenerationMobEffect {
} }
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let current_health = living.health.load(); let current_health = living.health.load();
let max_health = living.get_max_health(); let max_health = living.get_max_health();
if current_health < max_health && current_health > 0.0 { if current_health < max_health && current_health > 0.0 {
living.heal(1.0); living.heal(1.0);
} }
})
} }
} }

View File

@ -1,4 +1,4 @@
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct SaturationMobEffect; pub struct SaturationMobEffect;
@ -8,12 +8,7 @@ impl MobEffect for SaturationMobEffect {
true true
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, amplifier: u8) {
&'a self,
living: &'a LivingEntity,
amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let world = living.entity.world.load(); let world = living.entity.world.load();
if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) if let Some(entity) = world.get_entity_by_id(living.entity.entity_id)
&& let Some(player) = entity.get_player() && let Some(player) = entity.get_player()
@ -24,6 +19,5 @@ impl MobEffect for SaturationMobEffect {
.hunger_manager .hunger_manager
.add_saturation(f32::from(hunger) * 2.0); .add_saturation(f32::from(hunger) * 2.0);
} }
})
} }
} }

View File

@ -7,19 +7,13 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct WeavingMobEffect; pub struct WeavingMobEffect;
impl MobEffect for WeavingMobEffect { impl MobEffect for WeavingMobEffect {
fn on_mob_death<'a>( fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
_damage_type: &'a DamageType,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let world = living.entity.world.load(); let world = living.entity.world.load();
// Check if mob griefing is enabled or if player // Check if mob griefing is enabled or if player
@ -62,6 +56,5 @@ impl MobEffect for WeavingMobEffect {
); );
world.sync_world_event(WorldEvent::AnimationSpawnCobweb, target_pos, 0); world.sync_world_event(WorldEvent::AnimationSpawnCobweb, target_pos, 0);
} }
})
} }
} }

View File

@ -2,7 +2,7 @@ use pumpkin_data::damage::DamageType;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::projectile::wind_charge::BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR; use crate::entity::projectile::wind_charge::BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR;
use crate::world::explosion::ExplosionInteraction; use crate::world::explosion::ExplosionInteraction;
@ -10,13 +10,7 @@ use crate::world::explosion::ExplosionInteraction;
pub struct WindChargedMobEffect; pub struct WindChargedMobEffect;
impl MobEffect for WindChargedMobEffect { impl MobEffect for WindChargedMobEffect {
fn on_mob_death<'a>( fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
_damage_type: &'a DamageType,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let world = living.entity.world.load(); let world = living.entity.world.load();
let pos = living.entity.pos.load(); let pos = living.entity.pos.load();
let height = living.entity.height(); let height = living.entity.height();
@ -25,20 +19,17 @@ impl MobEffect for WindChargedMobEffect {
// gustStrength = 3.0 + random * 2.0 // gustStrength = 3.0 + random * 2.0
let gust_strength = 3.0 + rand::random::<f32>() * 2.0; let gust_strength = 3.0 + rand::random::<f32>() * 2.0;
world world.explode_with_calculator(
.explode_with_calculator(
center, center,
gust_strength, gust_strength,
ExplosionInteraction::Trigger, ExplosionInteraction::Trigger,
Some(BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone()), Some(BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone()),
) );
.await;
world.play_sound( world.play_sound(
Sound::EntityBreezeWindBurst, Sound::EntityBreezeWindBurst,
SoundCategory::Neutral, SoundCategory::Hostile,
&center, &center,
); );
})
} }
} }

View File

@ -1,6 +1,6 @@
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use crate::entity::effect::{EffectFuture, MobEffect}; use crate::entity::effect::MobEffect;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
pub struct WitherMobEffect; pub struct WitherMobEffect;
@ -18,12 +18,7 @@ impl MobEffect for WitherMobEffect {
} }
} }
fn apply_effect_tick<'a>( fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) {
&'a self,
living: &'a LivingEntity,
_amplifier: u8,
) -> EffectFuture<'a, ()> {
Box::pin(async move {
let dyn_self = living let dyn_self = living
.entity .entity
.world .world
@ -32,6 +27,5 @@ impl MobEffect for WitherMobEffect {
if let Some(dyn_self) = dyn_self { if let Some(dyn_self) = dyn_self {
dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER); dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER);
} }
})
} }
} }

View File

@ -115,14 +115,11 @@ impl EntityBase for ExperienceOrbEntity {
if can_pickup { if can_pickup {
player.living_entity.pickup(&self.entity, 1); player.living_entity.pickup(&self.entity, 1);
self.entity.remove(); self.entity.remove();
let player_clone = player.clone();
let amount = self.amount as i32; let amount = self.amount as i32;
tokio::spawn(async move { let remaining = player.apply_mending_from_xp(amount);
let remaining = player_clone.apply_mending_from_xp(amount).await;
if remaining > 0 { if remaining > 0 {
player_clone.add_experience_points(remaining).await; player.add_experience_points(remaining);
} }
});
} }
} }
} }

View File

@ -1,7 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use super::{NBTStorage, NBTStorageInit, player::Player}; use super::{NBTStorage, NBTStorageInit, player::Player};
use crate::entity::NbtFuture;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
@ -176,17 +175,14 @@ impl HungerManager {
} }
impl NBTStorage for HungerManager { impl NBTStorage for HungerManager {
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
nbt.put_int("foodLevel", self.level.load().into()); nbt.put_int("foodLevel", self.level.load().into());
nbt.put_float("foodSaturationLevel", self.saturation.load()); nbt.put_float("foodSaturationLevel", self.saturation.load());
nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); nbt.put_float("foodExhaustionLevel", self.exhaustion.load());
nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); nbt.put_int("foodTickTimer", self.tick_timer.load() as i32);
})
} }
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_nbt_non_mut(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.level self.level
.store(nbt.get_int("foodLevel").unwrap_or(20) as u8); .store(nbt.get_int("foodLevel").unwrap_or(20) as u8);
self.saturation self.saturation
@ -195,7 +191,6 @@ impl NBTStorage for HungerManager {
.store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0)); .store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0));
self.tick_timer self.tick_timer
.store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32); .store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32);
})
} }
} }

View File

@ -13,9 +13,7 @@ use pumpkin_util::math::{
}; };
use crate::{ use crate::{
entity::{ entity::{Entity, EntityBase, living::LivingEntity, player::Player},
Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity, player::Player,
},
server::Server, server::Server,
}; };
@ -161,8 +159,7 @@ impl InteractionEntity {
} }
impl EntityBase for InteractionEntity { impl EntityBase for InteractionEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
nbt.put_float( nbt.put_float(
"width", "width",
*self *self
@ -194,11 +191,9 @@ impl EntityBase for InteractionEntity {
if let Some(interaction) = interaction { if let Some(interaction) = interaction {
nbt.put("interaction", NbtTag::Compound(interaction.to_nbt())); nbt.put("interaction", NbtTag::Compound(interaction.to_nbt()));
} }
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
let width = nbt.get_float("width").unwrap_or(1.0); let width = nbt.get_float("width").unwrap_or(1.0);
let height = nbt.get_float("height").unwrap_or(1.0); let height = nbt.get_float("height").unwrap_or(1.0);
let response = nbt.get_bool("response").unwrap_or(false); let response = nbt.get_bool("response").unwrap_or(false);
@ -239,7 +234,6 @@ impl EntityBase for InteractionEntity {
.lock() .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None; .unwrap_or_else(std::sync::PoisonError::into_inner) = None;
} }
})
} }
fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {} fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {}
@ -331,12 +325,7 @@ impl EntityBase for InteractionEntity {
false false
} }
fn interact<'a>( fn interact(&self, player: &Arc<Player>, _item_stack: &mut ItemStack) -> bool {
&'a self,
player: &'a Arc<Player>,
_item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
let timestamp = self.entity.world.load().get_world_age(); let timestamp = self.entity.world.load().get_world_age();
*self *self
.interaction .interaction
@ -346,6 +335,5 @@ impl EntityBase for InteractionEntity {
timestamp, timestamp,
}); });
true true
})
} }
} }

View File

@ -25,7 +25,7 @@ use std::sync::{
}, },
}; };
use super::{Entity, EntityBase, NbtFuture, living::LivingEntity, player::Player}; use super::{Entity, EntityBase, living::LivingEntity, player::Player};
pub struct ItemEntity { pub struct ItemEntity {
entity: Entity, entity: Entity,
@ -381,23 +381,18 @@ impl ItemEntity {
if age >= 6000 { if age >= 6000 {
let entity_id = entity.entity_id; let entity_id = entity.entity_id;
let world = entity.world.load_full(); let world = entity.world.load_full();
tokio::spawn(async move {
let mut despawn_event = let mut despawn_event =
crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new( crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new(entity_id);
entity_id,
);
if let Some(server) = world.server.upgrade() { if let Some(server) = world.server.upgrade() {
server server
.plugin_manager .plugin_manager
.fire(&server, &mut despawn_event) .fire_blocking(&server, &mut despawn_event);
.await;
} }
if !despawn_event.cancelled if !despawn_event.cancelled
&& let Some(e) = world.get_entity_by_id(entity_id) && let Some(e) = world.get_entity_by_id(entity_id)
{ {
e.get_entity().remove(); e.get_entity().remove();
} }
});
return false; return false;
} }
@ -608,8 +603,7 @@ impl EntityBase for ItemEntity {
0.04 0.04
} }
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
let item = self let item = self
.item_stack .item_stack
.lock() .lock()
@ -624,11 +618,9 @@ impl EntityBase for ItemEntity {
self.pickup_delay.load(Ordering::Relaxed) as i16, self.pickup_delay.load(Ordering::Relaxed) as i16,
); );
nbt.put_short("Health", self.health.load(Relaxed) as i16); nbt.put_short("Health", self.health.load(Relaxed) as i16);
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
// Restore the item stack from the "Item" compound // Restore the item stack from the "Item" compound
if let Some(item_compound) = nbt.get_compound("Item") if let Some(item_compound) = nbt.get_compound("Item")
&& let Some(stack) = ItemStack::read_item_stack(item_compound) && let Some(stack) = ItemStack::read_item_stack(item_compound)
@ -652,7 +644,6 @@ impl EntityBase for ItemEntity {
if let Some(health) = nbt.get_short("Health") { if let Some(health) = nbt.get_short("Health") {
self.health.store(health as f32, Relaxed); self.health.store(health as f32, Relaxed);
} }
})
} }
fn cast_any(&self) -> &dyn std::any::Any { fn cast_any(&self) -> &dyn std::any::Any {

View File

@ -192,7 +192,7 @@ impl LightningBoltEntity {
} }
impl EntityBase for LightningBoltEntity { impl EntityBase for LightningBoltEntity {
fn tick(&self, caller: &Arc<dyn EntityBase>, _server: &Server) { fn tick(&self, _caller: &Arc<dyn EntityBase>, _server: &Server) {
let entity = &self.entity; let entity = &self.entity;
let life = self.life.load(Ordering::Relaxed); let life = self.life.load(Ordering::Relaxed);
@ -261,14 +261,7 @@ impl EntityBase for LightningBoltEntity {
} }
let hit_id = hit_entity.get_entity().entity_id; let hit_id = hit_entity.get_entity().entity_id;
if hit_guard.insert(hit_id) { if hit_guard.insert(hit_id) {
let caller_clone = caller.clone(); hit_entity.on_lightning_strike(hit_entity.as_ref(), self);
let target = hit_entity.clone();
tokio::spawn(async move {
if let Some(lightning) = caller_clone.cast_any().downcast_ref::<Self>()
{
target.on_lightning_strike(target.as_ref(), lightning).await;
}
});
} }
} }
} }

View File

@ -24,6 +24,7 @@ use tracing::warn;
use super::experience_orb::ExperienceOrbEntity; use super::experience_orb::ExperienceOrbEntity;
use super::{Entity, EntityBase, NBTStorageInit}; use super::{Entity, EntityBase, NBTStorageInit};
use crate::block::OnLandedUponArgs; use crate::block::OnLandedUponArgs;
use crate::entity::NBTStorage;
use crate::entity::attributes::AttributeInstance; use crate::entity::attributes::AttributeInstance;
use crate::entity::attributes::Modifier; use crate::entity::attributes::Modifier;
use crate::entity::attributes::ModifierOperation; use crate::entity::attributes::ModifierOperation;
@ -31,7 +32,6 @@ use crate::entity::combat::knockback_after_resistance;
use crate::entity::mob::equipment::DEFAULT_EQUIPMENT_DROP_CHANCE; use crate::entity::mob::equipment::DEFAULT_EQUIPMENT_DROP_CHANCE;
use crate::entity::mob::slime::SlimeEntity; use crate::entity::mob::slime::SlimeEntity;
use crate::entity::player::statistics::{CustomStatistic, StatisticCategory}; use crate::entity::player::statistics::{CustomStatistic, StatisticCategory};
use crate::entity::{NBTStorage, NbtFuture};
use crate::server::Server; use crate::server::Server;
use crate::world::loot::{LootContextParameters, LootTableExt}; use crate::world::loot::{LootContextParameters, LootTableExt};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
@ -1443,9 +1443,13 @@ impl LivingEntity {
fall_distance: f32, fall_distance: f32,
damage_per_distance: f32, damage_per_distance: f32,
) { ) {
let may_fly = caller let may_fly = caller.get_player().is_some_and(|player| {
.get_player() player
.is_some_and(|player| player.abilities.blocking_lock().allow_flying); .abilities
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.allow_flying
});
if may_fly || self.is_immune_to_fall_damage() { if may_fly || self.is_immune_to_fall_damage() {
return; return;
} }
@ -1472,7 +1476,7 @@ impl LivingEntity {
} }
} }
pub async fn get_death_message( pub fn get_death_message(
dyn_self: &dyn EntityBase, dyn_self: &dyn EntityBase,
damage_type: DamageType, damage_type: DamageType,
source: Option<&dyn EntityBase>, source: Option<&dyn EntityBase>,
@ -1486,16 +1490,13 @@ impl LivingEntity {
TextComponent::translate_cross( TextComponent::translate_cross(
format!("death.attack.{}.player", damage_type.message_id), format!("death.attack.{}.player", damage_type.message_id),
format!("death.attack.{}.player", damage_type.message_id), format!("death.attack.{}.player", damage_type.message_id),
[ [dyn_self.get_display_name(), cause.get_display_name()],
dyn_self.get_display_name().await,
cause.get_display_name().await,
],
) )
} else { } else {
TextComponent::translate_cross( TextComponent::translate_cross(
format!("death.attack.{}", damage_type.message_id), format!("death.attack.{}", damage_type.message_id),
format!("death.attack.{}", damage_type.message_id), format!("death.attack.{}", damage_type.message_id),
[dyn_self.get_display_name().await], [dyn_self.get_display_name()],
) )
} }
} }
@ -1504,14 +1505,14 @@ impl LivingEntity {
TextComponent::translate_cross( TextComponent::translate_cross(
translation::java::DEATH_FELL_ACCIDENT_GENERIC, translation::java::DEATH_FELL_ACCIDENT_GENERIC,
translation::bedrock::DEATH_FELL_ACCIDENT_GENERIC, translation::bedrock::DEATH_FELL_ACCIDENT_GENERIC,
[dyn_self.get_display_name().await], [dyn_self.get_display_name()],
) )
} }
DeathMessageType::IntentionalGameDesign => TextComponent::text("[") DeathMessageType::IntentionalGameDesign => TextComponent::text("[")
.add_child(TextComponent::translate_cross( .add_child(TextComponent::translate_cross(
format!("death.attack.{}.message", damage_type.message_id), format!("death.attack.{}.message", damage_type.message_id),
format!("death.attack.{}.message", damage_type.message_id), format!("death.attack.{}.message", damage_type.message_id),
[dyn_self.get_display_name().await], [dyn_self.get_display_name()],
)) ))
.add_child(TextComponent::text("]")), .add_child(TextComponent::text("]")),
} }
@ -1618,7 +1619,7 @@ impl LivingEntity {
}; };
for (effect_type, amplifier) in active_effects_vec { for (effect_type, amplifier) in active_effects_vec {
if let Some(mob_effect) = crate::entity::effect::get_mob_effect(effect_type) { if let Some(mob_effect) = crate::entity::effect::get_mob_effect(effect_type) {
mob_effect.on_mob_death(self, amplifier, &damage_type).await; mob_effect.on_mob_death(self, amplifier, &damage_type);
} }
} }
@ -1687,7 +1688,7 @@ impl LivingEntity {
let show_death_messages = { world.level_info.load().game_rules.show_death_messages }; let show_death_messages = { world.level_info.load().game_rules.show_death_messages };
if self.entity.entity_type == &EntityType::PLAYER && show_death_messages { if self.entity.entity_type == &EntityType::PLAYER && show_death_messages {
//TODO: KillCredit //TODO: KillCredit
let death_message = Self::get_death_message(dyn_self, damage_type, source, cause).await; let death_message = Self::get_death_message(dyn_self, damage_type, source, cause);
if let Some(server) = world.server.upgrade() { if let Some(server) = world.server.upgrade() {
for player in server.get_all_players() { for player in server.get_all_players() {
player.send_system_message(&death_message).await; player.send_system_message(&death_message).await;
@ -1816,15 +1817,7 @@ impl LivingEntity {
} }
for (mob_effect, amplifier) in effects_to_apply { for (mob_effect, amplifier) in effects_to_apply {
let entity_id = self.entity.entity_id; mob_effect.apply_effect_tick(self, amplifier);
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;
}
});
} }
} }
@ -2093,8 +2086,7 @@ impl LivingEntity {
} }
impl LivingEntity { impl LivingEntity {
pub fn write_living_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { pub fn write_living_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
nbt.put("Health", NbtTag::Float(self.health.load())); nbt.put("Health", NbtTag::Float(self.health.load()));
// Avoid persisting a lethal fall distance when the entity is dead to prevent death loops // Avoid persisting a lethal fall distance when the entity is dead to prevent death loops
let fall_distance = if self.dead.load(Relaxed) { let fall_distance = if self.dead.load(Relaxed) {
@ -2121,7 +2113,7 @@ impl LivingEntity {
let mut effects_list = Vec::with_capacity(effects_vec.len()); let mut effects_list = Vec::with_capacity(effects_vec.len());
for effect in effects_vec { for effect in effects_vec {
let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new();
effect.write_nbt(&mut effect_nbt).await; effect.write_nbt(&mut effect_nbt);
effects_list.push(NbtTag::Compound(effect_nbt)); effects_list.push(NbtTag::Compound(effect_nbt));
} }
nbt.put("active_effects", NbtTag::List(effects_list)); nbt.put("active_effects", NbtTag::List(effects_list));
@ -2129,11 +2121,9 @@ impl LivingEntity {
} }
//TODO: write equipment //TODO: write equipment
// todo more... // todo more...
})
} }
pub fn read_living_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { pub fn read_living_nbt_non_mut(&self, nbt: &NbtCompound) {
Box::pin(async {
self.health.store(nbt.get_float("Health").unwrap_or(20.0)); self.health.store(nbt.get_float("Health").unwrap_or(20.0));
// Clamp any persisted absorption to the entity's configured max // Clamp any persisted absorption to the entity's configured max
@ -2168,9 +2158,7 @@ impl LivingEntity {
let mut read_effects = Vec::new(); let mut read_effects = Vec::new();
for effect in nbt_effects { for effect in nbt_effects {
if let NbtTag::Compound(effect_nbt) = effect { if let NbtTag::Compound(effect_nbt) = effect {
if let Some(mut effect) = if let Some(mut effect) = Effect::create_from_nbt(&mut effect_nbt.clone()) {
Effect::create_from_nbt(&mut effect_nbt.clone()).await
{
effect.blend = true; // TODO: change, is taken from effect give command effect.blend = true; // TODO: change, is taken from effect give command
read_effects.push(effect); read_effects.push(effect);
} else { } else {
@ -2189,7 +2177,6 @@ impl LivingEntity {
} }
} }
} }
})
// todo more... // todo more...
} }
@ -2828,9 +2815,7 @@ impl EntityBase for LivingEntity {
.cooldown_group .cooldown_group
.clone() .clone()
.unwrap_or_else(|| item.item.registry_key.to_string()); .unwrap_or_else(|| item.item.registry_key.to_string());
player player.start_cooldown(group, (cooldown.seconds * 20.0) as i32);
.start_cooldown(group, (cooldown.seconds * 20.0) as i32)
.await;
} }
} }
@ -2932,7 +2917,12 @@ impl LivingEntity {
} }
ConsumeEffect::TeleportRandomly(diameter) => { ConsumeEffect::TeleportRandomly(diameter) => {
// Java Edition dismounts the consumer before random teleport attempts. // Java Edition dismounts the consumer before random teleport attempts.
let vehicle = caller.get_entity().vehicle.lock().await.clone(); let vehicle = caller
.get_entity()
.vehicle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
if let Some(vehicle) = vehicle { if let Some(vehicle) = vehicle {
vehicle vehicle
.get_entity() .get_entity()
@ -2959,7 +2949,7 @@ impl LivingEntity {
self.fall_distance.store(0.0); self.fall_distance.store(0.0);
// Vanilla broadcasts entity event 46 (teleport particles) on success. // Vanilla broadcasts entity event 46 (teleport particles) on success.
world.send_entity_status(&self.entity, EntityStatus::Teleport, None); world.send_entity_status(&self.entity, EntityStatus::Teleport, None);
world.emit_game_event("teleport", center).await; world.emit_game_event("teleport", center);
world.play_sound( world.play_sound(
Sound::ItemChorusFruitTeleport, Sound::ItemChorusFruitTeleport,
SoundCategory::Players, SoundCategory::Players,

View File

@ -1,8 +1,7 @@
use std::sync::{Arc, atomic::Ordering}; use std::sync::{Arc, Mutex, atomic::Ordering};
use tokio::sync::Mutex;
use crate::{ use crate::{
entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}, entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity},
net::{bedrock::BedrockClient, java::JavaClient}, net::{bedrock::BedrockClient, java::JavaClient},
server::Server, server::Server,
}; };
@ -26,21 +25,23 @@ impl MarkerEntity {
} }
impl EntityBase for MarkerEntity { impl EntityBase for MarkerEntity {
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move { let data = self
let data = self.data.lock().await; .data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !data.is_empty() { if !data.is_empty() {
nbt.put("data", NbtTag::Compound(data.clone())); nbt.put("data", NbtTag::Compound(data.clone()));
} }
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(data) = nbt.get_compound("data") { if let Some(data) = nbt.get_compound("data") {
*self.data.lock().await = data.clone(); *self
.data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = data.clone();
} }
})
} }
fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {} fn tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {}

View File

@ -14,7 +14,7 @@ use pumpkin_world::chunk::ChunkHeightmapType;
use rand::RngExt; use rand::RngExt;
use crate::entity::mob::{Mob, MobEntity}; use crate::entity::mob::{Mob, MobEntity};
use crate::entity::{Entity, EntityBase, NbtFuture}; use crate::entity::{Entity, EntityBase};
use crate::world::World; use crate::world::World;
const ROOSTING_FLAG: u8 = 1; const ROOSTING_FLAG: u8 = 1;
@ -217,19 +217,15 @@ impl Mob for BatEntity {
); );
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 };
nbt.put_byte("BatFlags", i8::try_from(flags).unwrap_or(0)); nbt.put_byte("BatFlags", i8::try_from(flags).unwrap_or(0));
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
let flags = u8::try_from(nbt.get_byte("BatFlags").unwrap_or(0)).unwrap_or(0); let flags = u8::try_from(nbt.get_byte("BatFlags").unwrap_or(0)).unwrap_or(0);
let roosting = (flags & ROOSTING_FLAG) != 0; let roosting = (flags & ROOSTING_FLAG) != 0;
self.set_roosting(roosting); self.set_roosting(roosting);
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {

View File

@ -18,7 +18,7 @@ use pumpkin_util::math::vector3::Vector3;
use crate::block::entities::creaking_heart::CreakingHeartBlockEntity; use crate::block::entities::creaking_heart::CreakingHeartBlockEntity;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal,
@ -397,8 +397,7 @@ impl CreakingEntity {
} }
impl Mob for CreakingEntity { impl Mob for CreakingEntity {
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
if let Some(pos) = self.get_home_pos() { if let Some(pos) = self.get_home_pos() {
let mut sub = NbtCompound::new(); let mut sub = NbtCompound::new();
sub.put_int("x", pos.0.x); sub.put_int("x", pos.0.x);
@ -406,11 +405,9 @@ impl Mob for CreakingEntity {
sub.put_int("z", pos.0.z); sub.put_int("z", pos.0.z);
nbt.put_compound("home_pos", sub); nbt.put_compound("home_pos", sub);
} }
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(sub) = nbt.get_compound("home_pos") if let Some(sub) = nbt.get_compound("home_pos")
&& let (Some(x), Some(y), Some(z)) = && let (Some(x), Some(y), Some(z)) =
(sub.get_int("x"), sub.get_int("y"), sub.get_int("z")) (sub.get_int("x"), sub.get_int("y"), sub.get_int("z"))
@ -418,7 +415,6 @@ impl Mob for CreakingEntity {
let pos = BlockPos::new(x, y, z); let pos = BlockPos::new(x, y, z);
self.set_transient(pos); self.set_transient(pos);
} }
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {

View File

@ -13,7 +13,7 @@ use pumpkin_nbt::compound::NbtCompound;
use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata};
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, creeper_ignite::CreeperIgniteGoal, active_target::ActiveTargetGoal, creeper_ignite::CreeperIgniteGoal,
look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal,
@ -101,7 +101,7 @@ impl CreeperEntity {
); );
} }
async fn explode(&self) { pub fn explode(&self) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
let radius = self.explosion_radius.load(Ordering::Relaxed) as f32; let radius = self.explosion_radius.load(Ordering::Relaxed) as f32;
let multiplier = if self.charged.load(Ordering::Relaxed) { let multiplier = if self.charged.load(Ordering::Relaxed) {
@ -115,21 +115,18 @@ impl CreeperEntity {
.store(true, Ordering::Relaxed); .store(true, Ordering::Relaxed);
let world = entity.world.load(); let world = entity.world.load();
let pos = entity.pos.load(); let pos = entity.pos.load();
world world.explode(
.explode(
pos, pos,
radius * multiplier, radius * multiplier,
crate::world::ExplosionInteraction::Mob, crate::world::ExplosionInteraction::Mob,
) );
.await;
// TODO: spawn area effect cloud with potion effects // TODO: spawn area effect cloud with potion effects
entity.remove(); entity.remove();
} }
} }
impl Mob for CreeperEntity { impl Mob for CreeperEntity {
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
nbt.put_bool("powered", self.charged.load(Ordering::Relaxed)); nbt.put_bool("powered", self.charged.load(Ordering::Relaxed));
nbt.put_short("Fuse", self.fuse_time.load(Ordering::Relaxed) as i16); nbt.put_short("Fuse", self.fuse_time.load(Ordering::Relaxed) as i16);
nbt.put_byte( nbt.put_byte(
@ -137,11 +134,9 @@ impl Mob for CreeperEntity {
self.explosion_radius.load(Ordering::Relaxed) as i8, self.explosion_radius.load(Ordering::Relaxed) as i8,
); );
nbt.put_bool("ignited", self.ignited.load(Ordering::Relaxed)); nbt.put_bool("ignited", self.ignited.load(Ordering::Relaxed));
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
if let Some(powered) = nbt.get_bool("powered") { if let Some(powered) = nbt.get_bool("powered") {
self.charged.store(powered, Ordering::Relaxed); self.charged.store(powered, Ordering::Relaxed);
} }
@ -155,19 +150,17 @@ impl Mob for CreeperEntity {
if let Some(ignited) = nbt.get_bool("ignited") { if let Some(ignited) = nbt.get_bool("ignited") {
self.ignited.store(ignited, Ordering::Relaxed); self.ignited.store(ignited, Ordering::Relaxed);
} }
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.mob_entity &self.mob_entity
} }
fn mob_on_lightning_strike<'a>( fn mob_on_lightning_strike(
&'a self, &self,
caller: &'a dyn EntityBase, caller: &dyn EntityBase,
lightning: &'a crate::entity::lightning::LightningBoltEntity, lightning: &crate::entity::lightning::LightningBoltEntity,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
self.charged.store(true, Ordering::Relaxed); self.charged.store(true, Ordering::Relaxed);
self.mob_entity.living_entity.entity.send_meta_data( self.mob_entity.living_entity.entity.send_meta_data(
&[Metadata::new( &[Metadata::new(
@ -178,12 +171,10 @@ impl Mob for CreeperEntity {
); );
self.mob_entity self.mob_entity
.living_entity .living_entity
.on_lightning_strike(caller, lightning) .on_lightning_strike(caller, lightning);
.await;
})
} }
fn mob_tick<'a>(&'a self, caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
if !entity.is_alive() { if !entity.is_alive() {
return; return;
@ -218,21 +209,11 @@ impl Mob for CreeperEntity {
if new_fuse >= fuse_time { if new_fuse >= fuse_time {
self.current_fuse_time.store(fuse_time, Ordering::Relaxed); self.current_fuse_time.store(fuse_time, Ordering::Relaxed);
let caller_clone = caller.clone(); self.explode();
tokio::spawn(async move {
if let Some(creeper) = caller_clone.cast_any().downcast_ref::<Self>() {
creeper.explode().await;
}
});
} }
} }
fn mob_interact<'a>( fn mob_interact(&self, player: &Arc<Player>, item_stack: &mut ItemStack) -> bool {
&'a self,
player: &'a Arc<Player>,
item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
if item_stack.item.id != Item::FLINT_AND_STEEL.id { if item_stack.item.id != Item::FLINT_AND_STEEL.id {
return self.mob_entity.mob_interact(player, item_stack); return self.mob_entity.mob_interact(player, item_stack);
} }
@ -264,6 +245,5 @@ impl Mob for CreeperEntity {
} }
true true
})
} }
} }

View File

@ -27,7 +27,7 @@ use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::
use rand::RngExt; use rand::RngExt;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::{ ai::{
goal::{ goal::{
active_target::ActiveTargetGoal, chase_player::ChasePlayerGoal, active_target::ActiveTargetGoal, chase_player::ChasePlayerGoal,
@ -412,20 +412,16 @@ impl EndermanEntity {
} }
impl Mob for EndermanEntity { impl Mob for EndermanEntity {
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async {
if let Some(block_state) = self.carried_block.load() { if let Some(block_state) = self.carried_block.load() {
nbt.put_int("carriedBlockState", block_state.as_u16() as i32); nbt.put_int("carriedBlockState", block_state.as_u16() as i32);
} }
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async {
if let Some(block_state) = nbt.get_int("carriedBlockState") { if let Some(block_state) = nbt.get_int("carriedBlockState") {
self.set_carried_block(BlockStateId::new(block_state as u16)); self.set_carried_block(BlockStateId::new(block_state as u16));
} }
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
@ -437,7 +433,7 @@ impl Mob for EndermanEntity {
} }
// TODO: sunlight avoidance, carried block drop on death, angerable system, ambient sound override // TODO: sunlight avoidance, carried block drop on death, angerable system, ambient sound override
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, caller: &'a Arc<dyn EntityBase>) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
if !entity.is_alive() { if !entity.is_alive() {
return; return;
@ -447,13 +443,7 @@ impl Mob for EndermanEntity {
let raining_at_feet = world.is_raining_at(&entity.block_pos.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()); 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 { if entity.touching_water.load(Ordering::SeqCst) || raining_at_feet || raining_at_head {
let entity_id = entity.entity_id; caller.damage(caller.as_ref(), 1.0, DamageType::DROWN);
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);
}
});
} }
} }

View File

@ -10,7 +10,7 @@ use pumpkin_protocol::java::client::play::Metadata;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
Controls, Goal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, Controls, Goal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal,
@ -161,20 +161,16 @@ impl Mob for EvokerEntity {
Some(self) Some(self)
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.write_raider_nbt(nbt); self.write_raider_nbt(nbt);
nbt.put_int("SpellTicks", self.get_spell_casting_time()); nbt.put_int("SpellTicks", self.get_spell_casting_time());
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.read_raider_nbt(nbt); self.read_raider_nbt(nbt);
if let Some(ticks) = nbt.get_int("SpellTicks") { if let Some(ticks) = nbt.get_int("SpellTicks") {
self.set_spell_casting_time(ticks); self.set_spell_casting_time(ticks);
} }
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {

View File

@ -15,7 +15,7 @@ use crate::entity::ai::goal::active_target::ActiveTargetGoal;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::projectile::fireball::FireballEntity; use crate::entity::projectile::fireball::FireballEntity;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{Controls, Goal}, ai::goal::{Controls, Goal},
mob::{Mob, MobEntity}, mob::{Mob, MobEntity},
}; };
@ -151,21 +151,15 @@ impl Mob for GhastEntity {
} }
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
let power = self.get_explosion_power(); let power = self.get_explosion_power();
nbt.put_byte("ExplosionPower", i8::try_from(power).unwrap_or(1)); nbt.put_byte("ExplosionPower", i8::try_from(power).unwrap_or(1));
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(power) = nbt.get_byte("ExplosionPower") { if let Some(power) = nbt.get_byte("ExplosionPower") {
self.set_explosion_power( self.set_explosion_power(u8::try_from(power).unwrap_or(Self::DEFAULT_EXPLOSION_POWER));
u8::try_from(power).unwrap_or(Self::DEFAULT_EXPLOSION_POWER),
);
} }
})
} }
fn modify_incoming_damage(&self, amount: f32, damage_type: DamageType) -> f32 { fn modify_incoming_damage(&self, amount: f32, damage_type: DamageType) -> f32 {

View File

@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use crate::entity::{ use crate::entity::{
Entity, NbtFuture, Entity,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal,
@ -96,16 +96,12 @@ impl Mob for IllusionerEntity {
Some(self) Some(self)
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.write_raider_nbt(nbt); self.write_raider_nbt(nbt);
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.read_raider_nbt(nbt); self.read_raider_nbt(nbt);
})
} }
} }

View File

@ -1,5 +1,4 @@
use super::{Entity, EntityBase, NbtFuture, ai::pathfinder::Navigator, living::LivingEntity}; use super::{Entity, EntityBase, ai::pathfinder::Navigator, living::LivingEntity};
use crate::entity::EntityBaseFuture;
use crate::entity::ai::control::MoveControlTrait; use crate::entity::ai::control::MoveControlTrait;
use crate::entity::ai::control::look_control::LookControl; use crate::entity::ai::control::look_control::LookControl;
use crate::entity::ai::control::move_control::MoveControl; use crate::entity::ai::control::move_control::MoveControl;
@ -587,21 +586,15 @@ pub trait Mob: EntityBase + Send + Sync {
} }
/// Metadata which must accompany this mob whenever it is spawned for a Java client. /// Metadata which must accompany this mob whenever it is spawned for a Java client.
fn mob_java_spawn_metadata( fn mob_java_spawn_metadata(&self, _version: JavaMinecraftVersion) -> Option<Box<[u8]>> {
&self, None
_version: JavaMinecraftVersion,
) -> EntityBaseFuture<'_, Option<Box<[u8]>>> {
Box::pin(async { None })
} }
/// Metadata which must accompany this mob whenever it is spawned for a Bedrock client. /// Metadata which must accompany this mob whenever it is spawned for a Bedrock client.
fn mob_bedrock_spawn_metadata( fn mob_bedrock_spawn_metadata(
&self, &self,
) -> EntityBaseFuture< ) -> Option<pumpkin_protocol::bedrock::client::set_actor_data::SyncedActorDataList> {
'_, None
Option<pumpkin_protocol::bedrock::client::set_actor_data::SyncedActorDataList>,
> {
Box::pin(async { None })
} }
fn get_job_site(&self) -> Option<BlockPos> { fn get_job_site(&self) -> Option<BlockPos> {
@ -699,12 +692,11 @@ pub trait Mob: EntityBase + Send + Sync {
None None
} }
fn populate_default_equipment_slots<'a>( fn populate_default_equipment_slots(
&'a self, &self,
_world: &'a Arc<World>, _world: &Arc<World>,
difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, difficulty: &crate::entity::mob::equipment::RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
if rand::random::<f32>() if rand::random::<f32>()
< MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier
{ {
@ -741,47 +733,43 @@ pub trait Mob: EntityBase + Send + Sync {
} }
} }
} }
})
} }
fn populate_default_equipment_enchantments<'a>( fn populate_default_equipment_enchantments(
&'a self, &self,
difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, difficulty: &crate::entity::mob::equipment::RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move { self.enchant_spawned_weapon(difficulty);
self.enchant_spawned_weapon(difficulty).await;
for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER {
self.enchant_spawned_armor(slot, difficulty).await; self.enchant_spawned_armor(slot, difficulty);
} }
})
} }
fn enchant_spawned_weapon<'a>( fn enchant_spawned_weapon(
&'a self, &self,
difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, difficulty: &crate::entity::mob::equipment::RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
self.enchant_spawned_equipment( self.enchant_spawned_equipment(
&EquipmentSlot::MAIN_HAND, &EquipmentSlot::MAIN_HAND,
MobEntity::MAX_ENCHANTED_WEAPON_CHANCE, MobEntity::MAX_ENCHANTED_WEAPON_CHANCE,
difficulty, difficulty,
) );
} }
fn enchant_spawned_armor<'a>( fn enchant_spawned_armor(
&'a self, &self,
slot: &'a EquipmentSlot, slot: &EquipmentSlot,
difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, difficulty: &crate::entity::mob::equipment::RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
self.enchant_spawned_equipment(slot, MobEntity::MAX_ENCHANTED_ARMOR_CHANCE, difficulty) self.enchant_spawned_equipment(slot, MobEntity::MAX_ENCHANTED_ARMOR_CHANCE, difficulty);
} }
fn enchant_spawned_equipment<'a>( fn enchant_spawned_equipment(
&'a self, &self,
slot: &'a EquipmentSlot, slot: &EquipmentSlot,
chance: f32, chance: f32,
difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, difficulty: &crate::entity::mob::equipment::RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
let living = &self.get_mob_entity().living_entity; let living = &self.get_mob_entity().living_entity;
let mut equipment = living let mut equipment = living
.entity_equipment .entity_equipment
@ -797,16 +785,11 @@ pub trait Mob: EntityBase + Send + Sync {
difficulty.special_multiplier, difficulty.special_multiplier,
); );
} }
})
} }
fn mob_write_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, _nbt: &mut NbtCompound) {}
Box::pin(async {})
}
fn mob_read_nbt<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, _nbt: &NbtCompound) {}
Box::pin(async {})
}
/// Set or clear the mob's target. Override to add side effects when targeting changes. /// Set or clear the mob's target. Override to add side effects when targeting changes.
fn set_mob_target(&self, target: Option<Arc<dyn EntityBase>>) { fn set_mob_target(&self, target: Option<Arc<dyn EntityBase>>) {
@ -817,56 +800,45 @@ pub trait Mob: EntityBase + Send + Sync {
.unwrap_or_else(std::sync::PoisonError::into_inner) = target; .unwrap_or_else(std::sync::PoisonError::into_inner) = target;
let world = mob.living_entity.entity.world.load_full(); let world = mob.living_entity.entity.world.load_full();
let entity_id = mob.living_entity.entity.entity_id; let entity_id = mob.living_entity.entity.entity_id;
tokio::spawn(async move { if let Some(server) = world.server.upgrade() {
let mut event = let mut event =
crate::plugin::api::events::entity::entity_target::EntityTargetEvent::new( crate::plugin::api::events::entity::entity_target::EntityTargetEvent::new(
entity_id, target_id, entity_id, target_id,
); );
if let Some(server) = world.server.upgrade() { server.plugin_manager.fire_blocking(&server, &mut event);
server.plugin_manager.fire(&server, &mut event).await;
} }
});
} }
fn mob_interact<'a>( fn mob_interact(&self, player: &Arc<Player>, item_stack: &mut ItemStack) -> bool {
&'a self, self.get_mob_entity().mob_interact(player, item_stack)
player: &'a Arc<Player>,
item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move { self.get_mob_entity().mob_interact(player, item_stack) })
} }
fn tame<'a>(&'a self, player: &'a Arc<Player>) -> EntityBaseFuture<'a, ()> { fn tame(&self, player: &Arc<Player>) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = crate::plugin::api::events::entity::entity_tame::EntityTameEvent::new( let mut event = crate::plugin::api::events::entity::entity_tame::EntityTameEvent::new(
mob.living_entity.entity.entity_id, mob.living_entity.entity.entity_id,
player.clone(), player.clone(),
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn breed(&self, father_id: i32, mother_id: i32, child_id: i32) -> EntityBaseFuture<'_, ()> { fn breed(&self, father_id: i32, mother_id: i32, child_id: i32) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = crate::plugin::api::events::entity::entity_breed::EntityBreedEvent::new( let mut event = crate::plugin::api::events::entity::entity_breed::EntityBreedEvent::new(
father_id, mother_id, child_id, father_id, mother_id, child_id,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn dye<'a>( fn dye(
&'a self, &self,
color: crate::plugin::api::events::entity::entity_dye::DyeColor, color: crate::plugin::api::events::entity::entity_dye::DyeColor,
player: Option<&'a Arc<Player>>, player: Option<&Arc<Player>>,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = crate::plugin::api::events::entity::entity_dye::EntityDyeEvent::new( let mut event = crate::plugin::api::events::entity::entity_dye::EntityDyeEvent::new(
mob.living_entity.entity.entity_id, mob.living_entity.entity.entity_id,
@ -874,17 +846,11 @@ pub trait Mob: EntityBase + Send + Sync {
player.cloned(), player.cloned(),
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn enter_love_mode( fn enter_love_mode(&self, human_entity_id: Option<i32>, ticks_in_love: i32) {
&self,
human_entity_id: Option<i32>,
ticks_in_love: i32,
) -> EntityBaseFuture<'_, ()> {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent::new( let mut event = crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent::new(
mob.living_entity.entity.entity_id, mob.living_entity.entity.entity_id,
@ -892,13 +858,11 @@ pub trait Mob: EntityBase + Send + Sync {
ticks_in_love, ticks_in_love,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn transform(&self, new_entity_id: i32, transform_reason: String) -> EntityBaseFuture<'_, ()> { fn transform(&self, new_entity_id: i32, transform_reason: String) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = let mut event =
crate::plugin::api::events::entity::entity_transform::EntityTransformEvent::new( crate::plugin::api::events::entity::entity_transform::EntityTransformEvent::new(
@ -907,13 +871,11 @@ pub trait Mob: EntityBase + Send + Sync {
transform_reason, transform_reason,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn break_door(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { fn break_door(&self, block_pos: BlockPos) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = let mut event =
crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new( crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new(
@ -921,13 +883,11 @@ pub trait Mob: EntityBase + Send + Sync {
block_pos, block_pos,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn enter_block(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { fn enter_block(&self, block_pos: BlockPos) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = let mut event =
crate::plugin::api::events::entity::entity_enter_block::EntityEnterBlockEvent::new( crate::plugin::api::events::entity::entity_enter_block::EntityEnterBlockEvent::new(
@ -935,13 +895,11 @@ pub trait Mob: EntityBase + Send + Sync {
block_pos, block_pos,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn interact(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { fn interact(&self, block_pos: BlockPos) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = let mut event =
crate::plugin::api::events::entity::entity_interact::EntityInteractEvent::new( crate::plugin::api::events::entity::entity_interact::EntityInteractEvent::new(
@ -949,13 +907,11 @@ pub trait Mob: EntityBase + Send + Sync {
block_pos, block_pos,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn place_block(&self, block_pos: BlockPos, block_name: String) -> EntityBaseFuture<'_, ()> { fn place_block(&self, block_pos: BlockPos, block_name: String) {
Box::pin(async move {
let mob = self.get_mob_entity(); let mob = self.get_mob_entity();
let mut event = crate::plugin::api::events::entity::entity_place::EntityPlaceEvent::new( let mut event = crate::plugin::api::events::entity::entity_place::EntityPlaceEvent::new(
mob.living_entity.entity.entity_id, mob.living_entity.entity.entity_id,
@ -963,9 +919,8 @@ pub trait Mob: EntityBase + Send + Sync {
block_name, block_name,
); );
if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
})
} }
fn mob_player_collision(&self, _player: &Arc<Player>) {} fn mob_player_collision(&self, _player: &Arc<Player>) {}
@ -1006,17 +961,14 @@ pub trait Mob: EntityBase + Send + Sync {
None None
} }
fn mob_on_lightning_strike<'a>( fn mob_on_lightning_strike(
&'a self, &self,
caller: &'a dyn EntityBase, caller: &dyn EntityBase,
lightning: &'a crate::entity::lightning::LightningBoltEntity, lightning: &crate::entity::lightning::LightningBoltEntity,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
self.get_mob_entity() self.get_mob_entity()
.living_entity .living_entity
.on_lightning_strike(caller, lightning) .on_lightning_strike(caller, lightning);
.await;
})
} }
} }
impl<T: Mob + Send + 'static> EntityBase for T { impl<T: Mob + Send + 'static> EntityBase for T {
@ -1024,14 +976,12 @@ impl<T: Mob + Send + 'static> EntityBase for T {
Some(self) Some(self)
} }
fn on_lightning_strike<'a>( fn on_lightning_strike(
&'a self, &self,
caller: &'a dyn EntityBase, caller: &dyn EntityBase,
lightning: &'a crate::entity::lightning::LightningBoltEntity, lightning: &crate::entity::lightning::LightningBoltEntity,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move { self.mob_on_lightning_strike(caller, lightning);
self.mob_on_lightning_strike(caller, lightning).await;
})
} }
fn get_item_steerable(&self) -> Option<&dyn crate::entity::item_steerable::ItemSteerable> { fn get_item_steerable(&self) -> Option<&dyn crate::entity::item_steerable::ItemSteerable> {
@ -1238,12 +1188,8 @@ impl<T: Mob + Send + 'static> EntityBase for T {
damaged damaged
} }
fn interact<'a>( fn interact(&self, player: &Arc<Player>, item_stack: &mut ItemStack) -> bool {
&'a self, self.mob_interact(player, item_stack)
player: &'a Arc<Player>,
item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move { self.mob_interact(player, item_stack).await })
} }
fn on_player_collision(&self, player: &Arc<Player>) { fn on_player_collision(&self, player: &Arc<Player>) {
@ -1293,8 +1239,7 @@ impl<T: Mob + Send + 'static> EntityBase for T {
<T as Mob>::get_home(self) <T as Mob>::get_home(self)
} }
fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn write_custom_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.get_mob_entity().write_mob_nbt(nbt); self.get_mob_entity().write_mob_nbt(nbt);
if let Some(ageable) = self.as_ageable() { if let Some(ageable) = self.as_ageable() {
ageable.write_ageable_nbt(nbt); ageable.write_ageable_nbt(nbt);
@ -1305,12 +1250,10 @@ impl<T: Mob + Send + 'static> EntityBase for T {
if let Some(tamable) = self.as_tamable() { if let Some(tamable) = self.as_tamable() {
tamable.write_tamable_nbt(nbt); tamable.write_tamable_nbt(nbt);
} }
self.mob_write_nbt(nbt).await; self.mob_write_nbt(nbt);
})
} }
fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn read_custom_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.get_mob_entity().read_mob_nbt(nbt); self.get_mob_entity().read_mob_nbt(nbt);
if let Some(ageable) = self.as_ageable() { if let Some(ageable) = self.as_ageable() {
ageable.read_ageable_nbt(nbt); ageable.read_ageable_nbt(nbt);
@ -1321,8 +1264,7 @@ impl<T: Mob + Send + 'static> EntityBase for T {
if let Some(tamable) = self.as_tamable() { if let Some(tamable) = self.as_tamable() {
tamable.read_tamable_nbt(nbt); tamable.read_tamable_nbt(nbt);
} }
self.mob_read_nbt(nbt).await; self.mob_read_nbt(nbt);
})
} }
fn get_gravity(&self) -> f64 { fn get_gravity(&self) -> f64 {

View File

@ -1,5 +1,5 @@
use std::sync::{ use std::sync::{
Arc, Weak, Arc, Mutex, Weak,
atomic::{AtomicBool, AtomicI32, Ordering}, atomic::{AtomicBool, AtomicI32, Ordering},
}; };
@ -16,12 +16,11 @@ use pumpkin_nbt::tag::NbtTag;
use pumpkin_protocol::java::client::play::Metadata; use pumpkin_protocol::java::client::play::Metadata;
use pumpkin_util::math::boundingbox::EntityDimensions; use pumpkin_util::math::boundingbox::EntityDimensions;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex;
use crate::entity::living::LivingEntity; use crate::entity::living::LivingEntity;
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal,
@ -274,10 +273,13 @@ impl PiglinEntity {
self.eat_cooldown_timer.load(Ordering::Relaxed) > 0 self.eat_cooldown_timer.load(Ordering::Relaxed) > 0
} }
pub async fn start_admiring(&self, item: ItemStack) { pub fn start_admiring(&self, item: ItemStack) {
self.admire_timer self.admire_timer
.store(PiglinAi::ADMIRE_DURATION, Ordering::Relaxed); .store(PiglinAi::ADMIRE_DURATION, Ordering::Relaxed);
*self.admiring_item.lock().await = Some(item.clone()); *self
.admiring_item
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(item.clone());
let mut equip = self let mut equip = self
.mob_entity .mob_entity
@ -296,9 +298,12 @@ impl PiglinEntity {
); );
} }
pub async fn stop_holding_off_hand_item(&self, bartering_enabled: bool) { pub fn stop_holding_off_hand_item(&self, bartering_enabled: bool) {
let admired_item = { let admired_item = {
let mut guard = self.admiring_item.lock().await; let mut guard = self
.admiring_item
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.take() guard.take()
}; };
@ -329,31 +334,34 @@ impl PiglinEntity {
outcomes, outcomes,
); );
if let Some(server) = entity.world.load().server.upgrade() { if let Some(server) = entity.world.load().server.upgrade() {
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire_blocking(&server, &mut event);
} }
if !event.cancelled { if !event.cancelled {
PiglinAi::throw_items(self, event.outcome, None); PiglinAi::throw_items(self, event.outcome, None);
} }
} else if !is_barter { } else if !is_barter {
let remainder = self.add_to_inventory(item).await; let remainder = self.add_to_inventory(item);
if let Some(rem) = remainder { if let Some(rem) = remainder {
PiglinAi::throw_items(self, vec![rem], None); PiglinAi::throw_items(self, vec![rem], None);
} }
} }
} else { } else {
let remainder = self.add_to_inventory(item).await; let remainder = self.add_to_inventory(item);
if let Some(rem) = remainder { if let Some(rem) = remainder {
PiglinAi::throw_items(self, vec![rem], None); PiglinAi::throw_items(self, vec![rem], None);
} }
} }
} }
pub async fn cancel_admiring(&self) { pub fn cancel_admiring(&self) {
if self.is_admiring() { if self.is_admiring() {
self.admire_timer.store(0, Ordering::Relaxed); self.admire_timer.store(0, Ordering::Relaxed);
let item = { let item = {
let mut guard = self.admiring_item.lock().await; let mut guard = self
.admiring_item
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.take() guard.take()
}; };
if let Some(item) = item { if let Some(item) = item {
@ -371,8 +379,8 @@ impl PiglinEntity {
} }
} }
pub async fn was_hurt_by(&self, attacker: Option<&dyn EntityBase>) { pub fn was_hurt_by(&self, attacker: Option<&dyn EntityBase>) {
self.cancel_admiring().await; self.cancel_admiring();
self.set_dancing(false); self.set_dancing(false);
self.celebration_timer.store(0, Ordering::Relaxed); self.celebration_timer.store(0, Ordering::Relaxed);
@ -384,8 +392,11 @@ impl PiglinEntity {
} }
} }
pub async fn add_to_inventory(&self, item: ItemStack) -> Option<ItemStack> { pub fn add_to_inventory(&self, item: ItemStack) -> Option<ItemStack> {
let mut inv = self.inventory.lock().await; let mut inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if inv.len() < Self::INVENTORY_SIZE { if inv.len() < Self::INVENTORY_SIZE {
inv.push(item); inv.push(item);
None None
@ -483,12 +494,11 @@ impl Mob for PiglinEntity {
&self.mob_entity &self.mob_entity
} }
fn populate_default_equipment_slots<'a>( fn populate_default_equipment_slots(
&'a self, &self,
_world: &'a Arc<World>, _world: &Arc<World>,
_difficulty: &'a RegionalDifficulty, _difficulty: &RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
if !self.is_baby.load(Ordering::Relaxed) { if !self.is_baby.load(Ordering::Relaxed) {
let living = &self.mob_entity.living_entity; let living = &self.mob_entity.living_entity;
let mut equipment = living let mut equipment = living
@ -529,7 +539,6 @@ impl Mob for PiglinEntity {
equipment.put(&EquipmentSlot::FEET, ItemStack::new(1, &Item::GOLDEN_BOOTS)); equipment.put(&EquipmentSlot::FEET, ItemStack::new(1, &Item::GOLDEN_BOOTS));
} }
} }
})
} }
fn mob_init_data_tracker(&self) { fn mob_init_data_tracker(&self) {
@ -558,8 +567,7 @@ impl Mob for PiglinEntity {
} }
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
if self.is_immune_to_zombification() { if self.is_immune_to_zombification() {
nbt.put_bool("IsImmuneToZombification", true); nbt.put_bool("IsImmuneToZombification", true);
} }
@ -581,7 +589,10 @@ impl Mob for PiglinEntity {
nbt.put_bool("IsDancing", true); nbt.put_bool("IsDancing", true);
} }
let inv = self.inventory.lock().await; let inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !inv.is_empty() { if !inv.is_empty() {
let mut items_tag = Vec::new(); let mut items_tag = Vec::new();
for item in inv.iter() { for item in inv.iter() {
@ -595,11 +606,9 @@ impl Mob for PiglinEntity {
nbt.put_list("Inventory", items_tag); nbt.put_list("Inventory", items_tag);
} }
} }
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { if let Some(immune) = nbt.get_bool("IsImmuneToZombification") {
self.set_immune_to_zombification(immune); self.set_immune_to_zombification(immune);
} }
@ -619,7 +628,10 @@ impl Mob for PiglinEntity {
self.set_dancing(dancing); self.set_dancing(dancing);
} }
if let Some(inv_list) = nbt.get_list("Inventory") { if let Some(inv_list) = nbt.get_list("Inventory") {
let mut inv = self.inventory.lock().await; let mut inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
inv.clear(); inv.clear();
for tag in inv_list { for tag in inv_list {
if let Some(compound) = tag.extract_compound() if let Some(compound) = tag.extract_compound()
@ -629,29 +641,22 @@ impl Mob for PiglinEntity {
} }
} }
} }
})
} }
fn mob_interact<'a>( fn mob_interact(&self, player: &Arc<Player>, item_stack: &mut ItemStack) -> bool {
&'a self,
player: &'a Arc<Player>,
item_stack: &'a mut ItemStack,
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
if PiglinAi::can_admire(self, item_stack) { if PiglinAi::can_admire(self, item_stack) {
let mut given = item_stack.clone(); let mut given = item_stack.clone();
given.item_count = 1; given.item_count = 1;
if player.gamemode.load() != pumpkin_util::GameMode::Creative { if player.gamemode.load() != pumpkin_util::GameMode::Creative {
item_stack.item_count -= 1; item_stack.item_count -= 1;
} }
self.start_admiring(given).await; self.start_admiring(given);
return true; return true;
} }
self.mob_entity.mob_interact(player, item_stack) self.mob_entity.mob_interact(player, item_stack)
})
} }
fn mob_tick<'a>(&'a self, caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) {
let entity = &self.mob_entity.living_entity.entity; let entity = &self.mob_entity.living_entity.entity;
if !entity.is_alive() { if !entity.is_alive() {
return; return;
@ -686,12 +691,7 @@ impl Mob for PiglinEntity {
if self.admire_timer.load(Ordering::Relaxed) > 0 { if self.admire_timer.load(Ordering::Relaxed) > 0 {
let remaining = self.admire_timer.fetch_sub(1, Ordering::Relaxed) - 1; let remaining = self.admire_timer.fetch_sub(1, Ordering::Relaxed) - 1;
if remaining <= 0 { if remaining <= 0 {
let caller_clone = caller.clone(); self.stop_holding_off_hand_item(true);
tokio::spawn(async move {
if let Some(piglin) = caller_clone.cast_any().downcast_ref::<Self>() {
piglin.stop_holding_off_hand_item(true).await;
}
});
} }
} }
} }

View File

@ -13,7 +13,7 @@ use pumpkin_protocol::java::client::play::Metadata;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal,
@ -196,8 +196,7 @@ impl Mob for PiglinBruteEntity {
} }
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
if self.is_immune_to_zombification() { if self.is_immune_to_zombification() {
nbt.put_bool("IsImmuneToZombification", true); nbt.put_bool("IsImmuneToZombification", true);
} }
@ -206,18 +205,15 @@ impl Mob for PiglinBruteEntity {
nbt.put_int("TimeInOverworld", time_in_overworld); nbt.put_int("TimeInOverworld", time_in_overworld);
} }
nbt.put_bool("CanPickUpLoot", true); nbt.put_bool("CanPickUpLoot", true);
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { if let Some(immune) = nbt.get_bool("IsImmuneToZombification") {
self.set_immune_to_zombification(immune); self.set_immune_to_zombification(immune);
} }
if let Some(time) = nbt.get_int("TimeInOverworld") { if let Some(time) = nbt.get_int("TimeInOverworld") {
self.time_in_overworld.store(time, Ordering::Relaxed); self.time_in_overworld.store(time, Ordering::Relaxed);
} }
})
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) { fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) {

View File

@ -1,5 +1,5 @@
use std::sync::{ use std::sync::{
Arc, Weak, Arc, Mutex, Weak,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
}; };
@ -10,10 +10,9 @@ use pumpkin_data::tracked_data;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use pumpkin_protocol::java::client::play::Metadata; use pumpkin_protocol::java::client::play::Metadata;
use tokio::sync::Mutex;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, ranged_crossbow_attack::RangedCrossbowAttackGoal, look_at_entity::LookAtEntityGoal, ranged_crossbow_attack::RangedCrossbowAttackGoal,
@ -115,8 +114,11 @@ impl PillagerEntity {
); );
} }
pub async fn add_to_inventory(&self, item: ItemStack) -> Option<ItemStack> { pub fn add_to_inventory(&self, item: ItemStack) -> Option<ItemStack> {
let mut inv = self.inventory.lock().await; let mut inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if inv.len() < Self::INVENTORY_SIZE { if inv.len() < Self::INVENTORY_SIZE {
inv.push(item); inv.push(item);
None None
@ -163,24 +165,17 @@ impl Mob for PillagerEntity {
} }
fn mob_init_data_tracker(&self) { fn mob_init_data_tracker(&self) {
let entity = self.get_entity(); self.set_charging_crossbow(self.is_charging_crossbow.load(Ordering::Relaxed));
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, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.write_raider_nbt(nbt); self.write_raider_nbt(nbt);
nbt.put_bool("CanPickUpLoot", true); nbt.put_bool("CanPickUpLoot", true);
let inv = self.inventory.lock().await; let inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !inv.is_empty() { if !inv.is_empty() {
let mut items_tag = Vec::new(); let mut items_tag = Vec::new();
for item in inv.iter() { for item in inv.iter() {
@ -194,15 +189,16 @@ impl Mob for PillagerEntity {
nbt.put_list("Inventory", items_tag); nbt.put_list("Inventory", items_tag);
} }
} }
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.read_raider_nbt(nbt); self.read_raider_nbt(nbt);
if let Some(inv_list) = nbt.get_list("Inventory") { if let Some(inv_list) = nbt.get_list("Inventory") {
let mut inv = self.inventory.lock().await; let mut inv = self
.inventory
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
inv.clear(); inv.clear();
for tag in inv_list { for tag in inv_list {
if let Some(compound) = tag.extract_compound() if let Some(compound) = tag.extract_compound()
@ -212,7 +208,6 @@ impl Mob for PillagerEntity {
} }
} }
} }
})
} }
fn on_damage( fn on_damage(

View File

@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use crate::entity::{ use crate::entity::{
Entity, NbtFuture, Entity,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal,
look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal,
@ -97,16 +97,12 @@ impl Mob for RavagerEntity {
Some(self) Some(self)
} }
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
self.write_raider_nbt(nbt); self.write_raider_nbt(nbt);
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.read_raider_nbt(nbt); self.read_raider_nbt(nbt);
})
} }
} }

View File

@ -21,7 +21,7 @@ use crate::entity::ai::goal::revenge::RevengeGoal;
use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::ai::goal::{Controls, Goal};
use crate::entity::mob::{Mob, MobEntity}; use crate::entity::mob::{Mob, MobEntity};
use crate::entity::projectile::shulker_bullet::ShulkerBulletEntity; use crate::entity::projectile::shulker_bullet::ShulkerBulletEntity;
use crate::entity::{Entity, EntityBase, NbtFuture}; use crate::entity::{Entity, EntityBase};
const DEFAULT_ATTACH_FACE: BlockDirection = BlockDirection::Down; const DEFAULT_ATTACH_FACE: BlockDirection = BlockDirection::Down;
const NO_COLOR: u8 = 16; const NO_COLOR: u8 = 16;
@ -322,16 +322,13 @@ impl ShulkerEntity {
} }
impl Mob for ShulkerEntity { impl Mob for ShulkerEntity {
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
nbt.put_byte("AttachFace", self.attach_face.load(Ordering::Relaxed) as i8); nbt.put_byte("AttachFace", self.attach_face.load(Ordering::Relaxed) as i8);
nbt.put_byte("PeekAmount", self.peek_amount.load(Ordering::Relaxed) as i8); nbt.put_byte("PeekAmount", self.peek_amount.load(Ordering::Relaxed) as i8);
nbt.put_byte("Color", self.color.load(Ordering::Relaxed) as i8); nbt.put_byte("Color", self.color.load(Ordering::Relaxed) as i8);
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
if let Some(face) = nbt.get_byte("AttachFace") { if let Some(face) = nbt.get_byte("AttachFace") {
self.attach_face.store(face as u8, Ordering::Relaxed); self.attach_face.store(face as u8, Ordering::Relaxed);
} }
@ -341,7 +338,6 @@ impl Mob for ShulkerEntity {
if let Some(color) = nbt.get_byte("Color") { if let Some(color) = nbt.get_byte("Color") {
self.color.store(color as u8, Ordering::Relaxed); self.color.store(color as u8, Ordering::Relaxed);
} }
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {

View File

@ -7,7 +7,7 @@ use pumpkin_data::item_stack::ItemStack;
use pumpkin_util::Difficulty; use pumpkin_util::Difficulty;
use crate::entity::{ use crate::entity::{
Entity, EntityBaseFuture, Entity,
ai::goal::{ ai::goal::{
active_target::ActiveTargetGoal, bow_attack::BowAttackGoal, active_target::ActiveTargetGoal, bow_attack::BowAttackGoal,
look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal,
@ -76,12 +76,11 @@ impl Mob for SkeletonEntityBase {
&self.mob_entity &self.mob_entity
} }
fn populate_default_equipment_slots<'a>( fn populate_default_equipment_slots(
&'a self, &self,
_world: &'a Arc<World>, _world: &Arc<World>,
difficulty: &'a RegionalDifficulty, difficulty: &RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
Box::pin(async move {
// Default armor slots (super.populateDefaultEquipmentSlots) // Default armor slots (super.populateDefaultEquipmentSlots)
if rand::random::<f32>() if rand::random::<f32>()
< MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier
@ -127,6 +126,5 @@ impl Mob for SkeletonEntityBase {
.lock() .lock()
.unwrap_or_else(std::sync::PoisonError::into_inner); .unwrap_or_else(std::sync::PoisonError::into_inner);
equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, &Item::BOW)); equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, &Item::BOW));
})
} }
} }

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::entity::{ use crate::entity::{
Entity, EntityBaseFuture, Entity,
mob::{Mob, MobEntity, equipment::RegionalDifficulty, skeleton::SkeletonEntityBase}, mob::{Mob, MobEntity, equipment::RegionalDifficulty, skeleton::SkeletonEntityBase},
}; };
use crate::world::World; use crate::world::World;
@ -23,20 +23,17 @@ impl Mob for SkeletonEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn populate_default_equipment_slots<'a>( fn populate_default_equipment_slots(
&'a self, &self,
world: &'a Arc<World>, world: &Arc<World>,
difficulty: &'a RegionalDifficulty, difficulty: &RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> { ) {
self.entity self.entity
.populate_default_equipment_slots(world, difficulty) .populate_default_equipment_slots(world, difficulty);
} }
fn populate_default_equipment_enchantments<'a>( fn populate_default_equipment_enchantments(&self, difficulty: &RegionalDifficulty) {
&'a self,
difficulty: &'a RegionalDifficulty,
) -> EntityBaseFuture<'a, ()> {
self.entity self.entity
.populate_default_equipment_enchantments(difficulty) .populate_default_equipment_enchantments(difficulty);
} }
} }

View File

@ -12,7 +12,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, NbtFuture, Entity, EntityBase,
ai::control::{Control, MoveControlTrait}, ai::control::{Control, MoveControlTrait},
ai::goal::{Goal, active_target::ActiveTargetGoal}, ai::goal::{Goal, active_target::ActiveTargetGoal},
mob::{Mob, MobEntity}, mob::{Mob, MobEntity},
@ -265,21 +265,17 @@ impl SlimeEntity {
} }
impl Mob for SlimeEntity { impl Mob for SlimeEntity {
fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { fn mob_write_nbt(&self, nbt: &mut NbtCompound) {
Box::pin(async move {
nbt.put_int("Size", self.get_size() - 1); nbt.put_int("Size", self.get_size() - 1);
nbt.put_bool("wasOnGround", self.was_on_ground.load(Ordering::Relaxed)); nbt.put_bool("wasOnGround", self.was_on_ground.load(Ordering::Relaxed));
})
} }
fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { fn mob_read_nbt(&self, nbt: &NbtCompound) {
Box::pin(async move {
self.set_size(nbt.get_int("Size").unwrap_or(0) + 1, false); self.set_size(nbt.get_int("Size").unwrap_or(0) + 1, false);
self.was_on_ground.store( self.was_on_ground.store(
nbt.get_bool("wasOnGround").unwrap_or(false), nbt.get_bool("wasOnGround").unwrap_or(false),
Ordering::Relaxed, Ordering::Relaxed,
); );
})
} }
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {

Some files were not shown because too many files have changed in this diff Show More