From 5d841d6d3b94b236ebf389ae142e389f517a3690 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 26 Aug 2026 16:45:10 +0200 Subject: [PATCH] refactor(entities): from async to sync Part 2 --- .../src/player/ender_chest_inventory.rs | 83 +- .../src/player/player_inventory.rs | 34 + crates/pumpkin/src/block/blocks/bed.rs | 12 +- crates/pumpkin/src/block/blocks/cauldron.rs | 66 +- .../pumpkin/src/block/blocks/ender_chest.rs | 2 +- crates/pumpkin/src/block/blocks/pumpkin.rs | 6 +- .../blocks/redstone/daylight_detector.rs | 6 +- .../src/block/blocks/redstone/dispenser.rs | 8 +- .../src/block/blocks/respawn_anchor.rs | 8 +- .../pumpkin/src/block/fluid/flowing_trait.rs | 58 +- crates/pumpkin/src/block/fluid/lava.rs | 119 +- crates/pumpkin/src/block/fluid/pathfinder.rs | 4 +- crates/pumpkin/src/block/fluid/water.rs | 7 +- crates/pumpkin/src/block/mod.rs | 6 +- .../argument_types/entity_selector/mod.rs | 6 +- .../src/command/commands/advancement.rs | 15 +- crates/pumpkin/src/command/commands/clear.rs | 6 +- crates/pumpkin/src/command/commands/damage.rs | 4 +- crates/pumpkin/src/command/commands/data.rs | 4 +- crates/pumpkin/src/command/commands/effect.rs | 6 +- .../pumpkin/src/command/commands/enchant.rs | 8 +- .../pumpkin/src/command/commands/execute.rs | 2 +- .../src/command/commands/experience.rs | 12 +- .../src/command/commands/fetchprofile.rs | 4 +- .../pumpkin/src/command/commands/gamemode.rs | 2 +- crates/pumpkin/src/command/commands/give.rs | 2 +- crates/pumpkin/src/command/commands/item.rs | 2 +- crates/pumpkin/src/command/commands/kick.rs | 4 +- crates/pumpkin/src/command/commands/kill.rs | 8 +- crates/pumpkin/src/command/commands/list.rs | 10 +- crates/pumpkin/src/command/commands/msg.rs | 8 +- .../pumpkin/src/command/commands/playsound.rs | 2 +- crates/pumpkin/src/command/commands/recipe.rs | 4 +- crates/pumpkin/src/command/commands/ride.rs | 59 +- crates/pumpkin/src/command/commands/rotate.rs | 2 +- .../pumpkin/src/command/commands/spectate.rs | 14 +- crates/pumpkin/src/command/commands/summon.rs | 2 +- crates/pumpkin/src/command/commands/tag.rs | 17 +- .../pumpkin/src/command/commands/teleport.rs | 11 +- crates/pumpkin/src/command/commands/title.rs | 10 +- .../pumpkin/src/command/commands/transfer.rs | 2 +- .../src/command/commands/worldborder.rs | 255 ++-- .../src/command/context/command_source.rs | 2 +- crates/pumpkin/src/command/mod.rs | 4 +- crates/pumpkin/src/data/player_server.rs | 6 +- .../pumpkin/src/entity/boss/ender_dragon.rs | 207 ++- .../boss/ender_dragon/phase/charging.rs | 58 +- .../boss/ender_dragon/phase/circling.rs | 159 ++- .../entity/boss/ender_dragon/phase/dying.rs | 135 +- .../boss/ender_dragon/phase/fly_to_portal.rs | 33 +- .../boss/ender_dragon/phase/hovering.rs | 31 +- .../entity/boss/ender_dragon/phase/landing.rs | 26 +- .../ender_dragon/phase/landing_approach.rs | 33 +- .../src/entity/boss/ender_dragon/phase/mod.rs | 11 +- .../boss/ender_dragon/phase/sit_attacking.rs | 69 +- .../boss/ender_dragon/phase/sit_breathing.rs | 104 +- .../boss/ender_dragon/phase/strafing.rs | 292 ++-- .../boss/ender_dragon/phase/taking_off.rs | 33 +- crates/pumpkin/src/entity/boss/wither.rs | 88 +- crates/pumpkin/src/entity/breath.rs | 4 +- .../src/entity/decoration/armor_stand.rs | 110 +- .../pumpkin/src/entity/decoration/display.rs | 214 ++- .../src/entity/decoration/end_crystal.rs | 6 +- .../src/entity/decoration/item_frame.rs | 140 +- .../src/entity/decoration/leash_knot.rs | 94 +- .../pumpkin/src/entity/decoration/painting.rs | 16 +- crates/pumpkin/src/entity/effect/hunger.rs | 24 +- crates/pumpkin/src/entity/effect/infested.rs | 104 +- crates/pumpkin/src/entity/effect/mod.rs | 122 +- crates/pumpkin/src/entity/effect/oozing.rs | 53 +- crates/pumpkin/src/entity/effect/poison.rs | 34 +- crates/pumpkin/src/entity/effect/raid_omen.rs | 40 +- .../pumpkin/src/entity/effect/regeneration.rs | 20 +- .../pumpkin/src/entity/effect/saturation.rs | 30 +- crates/pumpkin/src/entity/effect/weaving.rs | 89 +- .../pumpkin/src/entity/effect/wind_charged.rs | 47 +- crates/pumpkin/src/entity/effect/wither.rs | 26 +- crates/pumpkin/src/entity/experience_orb.rs | 11 +- crates/pumpkin/src/entity/hunger.rs | 33 +- crates/pumpkin/src/entity/interaction.rs | 168 ++- crates/pumpkin/src/entity/item.rs | 105 +- crates/pumpkin/src/entity/lightning.rs | 11 +- crates/pumpkin/src/entity/living.rs | 226 ++- crates/pumpkin/src/entity/marker.rs | 33 +- crates/pumpkin/src/entity/mob/bat.rs | 20 +- crates/pumpkin/src/entity/mob/creaking.rs | 38 +- crates/pumpkin/src/entity/mob/creeper.rs | 168 +-- crates/pumpkin/src/entity/mob/enderman.rs | 32 +- crates/pumpkin/src/entity/mob/evoker.rs | 22 +- crates/pumpkin/src/entity/mob/ghast.rs | 22 +- crates/pumpkin/src/entity/mob/illusioner.rs | 14 +- crates/pumpkin/src/entity/mob/mod.rs | 516 +++---- crates/pumpkin/src/entity/mob/piglin.rs | 296 ++-- crates/pumpkin/src/entity/mob/piglin_brute.rs | 38 +- crates/pumpkin/src/entity/mob/pillager.rs | 93 +- crates/pumpkin/src/entity/mob/ravager.rs | 14 +- crates/pumpkin/src/entity/mob/shulker.rs | 34 +- crates/pumpkin/src/entity/mob/skeleton/mod.rs | 88 +- .../src/entity/mob/skeleton/skeleton.rs | 21 +- crates/pumpkin/src/entity/mob/slime.rs | 24 +- crates/pumpkin/src/entity/mob/vindicator.rs | 14 +- crates/pumpkin/src/entity/mob/witch.rs | 18 +- .../pumpkin/src/entity/mob/zombie/drowned.rs | 14 +- crates/pumpkin/src/entity/mob/zombie/husk.rs | 14 +- crates/pumpkin/src/entity/mob/zombie/mod.rs | 141 +- .../pumpkin/src/entity/mob/zombie/zombie.rs | 33 +- .../src/entity/mob/zombie/zombie_villager.rs | 14 +- crates/pumpkin/src/entity/mod.rs | 952 ++++++------- crates/pumpkin/src/entity/passive/animal.rs | 118 +- .../pumpkin/src/entity/passive/armadillo.rs | 85 +- crates/pumpkin/src/entity/passive/cat.rs | 200 ++- crates/pumpkin/src/entity/passive/chicken.rs | 56 +- .../src/entity/passive/copper_golem.rs | 115 +- crates/pumpkin/src/entity/passive/cow.rs | 8 +- crates/pumpkin/src/entity/passive/fox.rs | 53 +- crates/pumpkin/src/entity/passive/frog.rs | 29 +- .../pumpkin/src/entity/passive/happy_ghast.rs | 43 +- .../pumpkin/src/entity/passive/iron_golem.rs | 56 +- crates/pumpkin/src/entity/passive/nautilus.rs | 130 +- crates/pumpkin/src/entity/passive/ocelot.rs | 87 +- crates/pumpkin/src/entity/passive/parrot.rs | 32 +- crates/pumpkin/src/entity/passive/pig.rs | 48 +- crates/pumpkin/src/entity/passive/sheep.rs | 34 +- crates/pumpkin/src/entity/passive/sniffer.rs | 43 +- crates/pumpkin/src/entity/passive/strider.rs | 55 +- .../src/entity/passive/villager/mod.rs | 933 +++++++------ .../src/entity/passive/wandering_trader.rs | 339 ++--- crates/pumpkin/src/entity/passive/wolf.rs | 86 +- crates/pumpkin/src/entity/player.rs | 1222 +++++++++-------- .../pumpkin/src/entity/player/advancement.rs | 9 +- crates/pumpkin/src/entity/projectile/arrow.rs | 38 +- .../src/entity/projectile/evoker_fangs.rs | 24 +- .../pumpkin/src/entity/projectile/fireball.rs | 38 +- .../src/entity/projectile/wind_charge.rs | 18 +- .../src/entity/projectile/wither_skull.rs | 22 +- crates/pumpkin/src/entity/tnt.rs | 10 +- crates/pumpkin/src/entity/vehicle/boat.rs | 65 +- crates/pumpkin/src/entity/vehicle/minecart.rs | 347 +++-- .../src/entity/vehicle/minecart/chest.rs | 19 +- .../src/entity/vehicle/minecart/container.rs | 48 +- .../src/entity/vehicle/minecart/hopper.rs | 21 +- .../src/entity/vehicle/minecart/rideable.rs | 12 +- .../src/entity/vehicle/minecart/tnt.rs | 15 +- crates/pumpkin/src/item/items/armor_stand.rs | 90 +- crates/pumpkin/src/item/items/axe.rs | 150 +- crates/pumpkin/src/item/items/boat.rs | 169 ++- crates/pumpkin/src/item/items/bone_meal.rs | 36 +- crates/pumpkin/src/item/items/bow.rs | 63 +- crates/pumpkin/src/item/items/brush.rs | 278 ++-- crates/pumpkin/src/item/items/bucket.rs | 218 ++- crates/pumpkin/src/item/items/bundle.rs | 69 +- crates/pumpkin/src/item/items/clock.rs | 22 +- crates/pumpkin/src/item/items/compass.rs | 70 +- crates/pumpkin/src/item/items/crossbow.rs | 159 +-- crates/pumpkin/src/item/items/egg.rs | 77 +- crates/pumpkin/src/item/items/end_crystal.rs | 61 +- crates/pumpkin/src/item/items/ender_eye.rs | 167 ++- crates/pumpkin/src/item/items/ender_pearl.rs | 80 +- .../src/item/items/experience_bottle.rs | 34 +- .../pumpkin/src/item/items/firework_rocket.rs | 61 +- crates/pumpkin/src/item/items/fishing_rod.rs | 92 +- crates/pumpkin/src/item/items/glass_bottle.rs | 129 +- crates/pumpkin/src/item/items/goat_horn.rs | 30 +- crates/pumpkin/src/item/items/hoe.rs | 143 +- crates/pumpkin/src/item/items/honeycomb.rs | 23 +- .../src/item/items/ignite/fire_charge.rs | 43 +- .../src/item/items/ignite/flint_and_steel.rs | 87 +- .../pumpkin/src/item/items/ignite/ignition.rs | 9 +- crates/pumpkin/src/item/items/lead.rs | 105 +- crates/pumpkin/src/item/items/map.rs | 90 +- crates/pumpkin/src/item/items/minecart.rs | 67 +- crates/pumpkin/src/item/items/name_tag.rs | 26 +- crates/pumpkin/src/item/items/on_a_stick.rs | 56 +- crates/pumpkin/src/item/items/potions.rs | 185 ++- crates/pumpkin/src/item/items/saddle.rs | 43 +- crates/pumpkin/src/item/items/shears.rs | 89 +- crates/pumpkin/src/item/items/shovel.rs | 105 +- crates/pumpkin/src/item/items/snowball.rs | 71 +- crates/pumpkin/src/item/items/spawn_egg.rs | 78 +- crates/pumpkin/src/item/items/spyglass.rs | 48 +- crates/pumpkin/src/item/items/trident.rs | 195 ++- crates/pumpkin/src/item/items/wind_charge.rs | 38 +- .../pumpkin/src/item/items/writable_book.rs | 28 +- crates/pumpkin/src/item/mod.rs | 40 +- crates/pumpkin/src/item/registry.rs | 36 +- .../src/net/bedrock/play/inventory_action.rs | 38 +- .../src/net/bedrock/play/player_action.rs | 62 +- .../src/net/bedrock/play/player_auth_input.rs | 20 +- .../src/net/bedrock/play/request_ability.rs | 16 +- .../src/net/java/play/client_command.rs | 5 +- crates/pumpkin/src/net/java/play/interact.rs | 5 +- .../pumpkin/src/net/java/play/move_vehicle.rs | 9 +- .../pumpkin/src/net/java/play/paddle_boat.rs | 7 +- .../src/net/java/play/player_abilities.rs | 7 +- .../src/net/java/play/player_action.rs | 42 +- .../pumpkin/src/net/java/play/player_input.rs | 14 +- .../src/net/java/play/player_position.rs | 8 +- crates/pumpkin/src/net/java/play/tag_query.rs | 2 +- crates/pumpkin/src/net/java/play/use_item.rs | 10 +- .../pumpkin/src/net/java/play/use_item_on.rs | 5 +- .../loader/wasm/wasm_host/wit/v0_1/entity.rs | 35 +- .../loader/wasm/wasm_host/wit/v0_1/player.rs | 77 +- .../loader/wasm/wasm_host/wit/v0_1/world.rs | 120 +- crates/pumpkin/src/server/mod.rs | 2 +- crates/pumpkin/src/world/border.rs | 26 +- crates/pumpkin/src/world/explosion.rs | 8 +- crates/pumpkin/src/world/mod.rs | 191 +-- crates/pumpkin/src/world/portal/mod.rs | 45 +- crates/pumpkin/src/world/portal/nether.rs | 53 +- 209 files changed, 7557 insertions(+), 7959 deletions(-) diff --git a/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs b/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs index fc4df2fb3..f5c6af29b 100644 --- a/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs +++ b/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs @@ -10,6 +10,7 @@ //! Ender chests track when players open and close them to properly //! manage the viewer count for animation purposes. +use std::sync::{Mutex, RwLock}; use std::{any::Any, pin::Pin, sync::Arc}; use pumpkin_data::item_stack::ItemStack; @@ -17,7 +18,6 @@ use pumpkin_world::{ block::viewer::ViewerCountTracker, inventory::{Clearable, Inventory, InventoryFuture}, }; -use tokio::sync::{Mutex, RwLock}; /// A player's ender chest inventory. /// @@ -55,21 +55,33 @@ impl EnderChestInventory { /// Sets the viewer count tracker for this inventory. /// /// Used to animate the ender chest lid based on viewers. - pub async fn set_tracker(&self, tracker: Arc) { - let old = self.tracker.lock().await.replace(tracker); + pub fn set_tracker(&self, tracker: Arc) { + let old = self + .tracker + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .replace(tracker); if let Some(old_tracker) = old { old_tracker.close_container(); } } /// Checks if this inventory has a tracker set. - pub async fn has_tracker(&self) -> bool { - self.tracker.lock().await.is_some() + pub fn has_tracker(&self) -> bool { + self.tracker + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() } /// Checks if the given tracker is associated with this inventory. - pub async fn is_tracker(&self, tracker: &Arc) -> bool { - if let Some(value) = self.tracker.lock().await.as_ref() { + pub fn is_tracker(&self, tracker: &Arc) -> bool { + if let Some(value) = self + .tracker + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { return Arc::ptr_eq(value, tracker); } false @@ -83,14 +95,20 @@ impl Inventory for EnderChestInventory { fn is_empty(&self) -> InventoryFuture<'_, bool> { 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) }) } fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { - let items = self.items.read().await; + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); items .get(slot) .cloned() @@ -100,7 +118,10 @@ impl Inventory for EnderChestInventory { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { 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 { std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) } else { @@ -111,7 +132,10 @@ impl Inventory for EnderChestInventory { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { 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 { items[slot].split(amount) } else { @@ -122,7 +146,10 @@ impl Inventory for EnderChestInventory { fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { 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] = stack; } @@ -131,7 +158,12 @@ impl Inventory for EnderChestInventory { fn on_open(&self) -> InventoryFuture<'_, ()> { 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(); } }) @@ -139,7 +171,11 @@ impl Inventory for EnderChestInventory { fn on_close(&self) -> InventoryFuture<'_, ()> { 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 { tracker.close_container(); } @@ -156,7 +192,10 @@ impl Inventory for EnderChestInventory { impl Clearable for EnderChestInventory { fn clear(&self) -> Pin + Send + '_>> { 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()); }) } @@ -222,24 +261,24 @@ mod tests { let tracker1 = Arc::new(ViewerCountTracker::new()); let tracker2 = Arc::new(ViewerCountTracker::new()); - ec.set_tracker(tracker1.clone()).await; - assert!(ec.has_tracker().await); - assert!(ec.is_tracker(&tracker1).await); - assert!(!ec.is_tracker(&tracker2).await); + ec.set_tracker(tracker1.clone()); + assert!(ec.has_tracker()); + assert!(ec.is_tracker(&tracker1)); + assert!(!ec.is_tracker(&tracker2)); ec.on_open().await; assert_eq!(tracker1.get_viewer_count(), 1); // 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!(ec.is_tracker(&tracker2).await); + assert!(ec.is_tracker(&tracker2)); ec.on_open().await; assert_eq!(tracker2.get_viewer_count(), 1); ec.on_close().await; assert_eq!(tracker2.get_viewer_count(), 0); - assert!(!ec.has_tracker().await); + assert!(!ec.has_tracker()); } } diff --git a/crates/pumpkin-inventory/src/player/player_inventory.rs b/crates/pumpkin-inventory/src/player/player_inventory.rs index ee6cd891b..8f983a845 100644 --- a/crates/pumpkin-inventory/src/player/player_inventory.rs +++ b/crates/pumpkin-inventory/src/player/player_inventory.rs @@ -404,6 +404,40 @@ impl PlayerInventory { 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. pub async fn offer_or_drop_stack(&self, stack: ItemStack, player: &dyn InventoryPlayer) { self.offer(stack, true, player).await; diff --git a/crates/pumpkin/src/block/blocks/bed.rs b/crates/pumpkin/src/block/blocks/bed.rs index 2673d7b13..b4cdbeb02 100644 --- a/crates/pumpkin/src/block/blocks/bed.rs +++ b/crates/pumpkin/src/block/blocks/bed.rs @@ -233,13 +233,11 @@ impl BedBlock { world.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS); world.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS); - world - .explode( - bed_head_pos.to_centered_f64(), - 5.0, - crate::world::ExplosionInteraction::Block, - ) - .await; + world.explode( + bed_head_pos.to_centered_f64(), + 5.0, + crate::world::ExplosionInteraction::Block, + ); return BlockActionResult::SuccessServer; } diff --git a/crates/pumpkin/src/block/blocks/cauldron.rs b/crates/pumpkin/src/block/blocks/cauldron.rs index e6d26005b..0154b3687 100644 --- a/crates/pumpkin/src/block/blocks/cauldron.rs +++ b/crates/pumpkin/src/block/blocks/cauldron.rs @@ -47,6 +47,18 @@ fn fire_cauldron_change( !event.cancelled } +fn give_item_or_drop( + player: &crate::entity::player::Player, + world: &std::sync::Arc, + 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 { #[allow(clippy::too_many_lines)] fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { @@ -78,13 +90,7 @@ impl BlockBehaviour for CauldronBlock { &args.position.to_f64(), ); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) - .await; - }); + give_item_or_drop(args.player, args.world, &Item::BUCKET); return BlockActionResult::Success; } else if item_id == Item::LAVA_BUCKET.id { args.world.set_block_state( @@ -98,13 +104,7 @@ impl BlockBehaviour for CauldronBlock { &args.position.to_f64(), ); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) - .await; - }); + give_item_or_drop(args.player, args.world, &Item::BUCKET); return BlockActionResult::Success; } else if item_id == Item::POWDER_SNOW_BUCKET.id { let state_id = Block::POWDER_SNOW_CAULDRON @@ -118,13 +118,7 @@ impl BlockBehaviour for CauldronBlock { &args.position.to_f64(), ); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack(ItemStack::new(1, &Item::BUCKET), player.as_ref()) - .await; - }); + give_item_or_drop(args.player, args.world, &Item::BUCKET); return BlockActionResult::Success; } else if item_id == Item::POTION.id { let state_id = Block::WATER_CAULDRON @@ -138,16 +132,7 @@ impl BlockBehaviour for CauldronBlock { &args.position.to_f64(), ); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack( - ItemStack::new(1, &Item::GLASS_BOTTLE), - player.as_ref(), - ) - .await; - }); + give_item_or_drop(args.player, args.world, &Item::GLASS_BOTTLE); return BlockActionResult::Success; } } @@ -187,13 +172,7 @@ impl BlockBehaviour for CauldronBlock { args.world .play_sound(sound, SoundCategory::Blocks, &args.position.to_f64()); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack(ItemStack::new(1, result_item), player.as_ref()) - .await; - }); + give_item_or_drop(args.player, args.world, result_item); return BlockActionResult::Success; } } @@ -218,16 +197,7 @@ impl BlockBehaviour for CauldronBlock { &args.position.to_f64(), ); args.item_stack.decrement_unless_creative(gamemode, 1); - let player = Arc::clone(args.player); - tokio::spawn(async move { - player - .inventory - .offer_or_drop_stack( - ItemStack::new(1, &Item::GLASS_BOTTLE), - player.as_ref(), - ) - .await; - }); + give_item_or_drop(args.player, args.world, &Item::GLASS_BOTTLE); return BlockActionResult::Success; } } diff --git a/crates/pumpkin/src/block/blocks/ender_chest.rs b/crates/pumpkin/src/block/blocks/ender_chest.rs index 613d4e02d..6a1fc77f6 100644 --- a/crates/pumpkin/src/block/blocks/ender_chest.rs +++ b/crates/pumpkin/src/block/blocks/ender_chest.rs @@ -35,7 +35,7 @@ impl ScreenHandlerFactory for EnderChestScreenFactory { ) -> BoxFuture<'a, Option> { Box::pin(async move { if let Some(tracker) = &self.tracker { - self.inventory.set_tracker(tracker.clone()).await; + self.inventory.set_tracker(tracker.clone()); } let handler = create_generic_9x3(sync_id, player_inventory, self.inventory.clone()).await; diff --git a/crates/pumpkin/src/block/blocks/pumpkin.rs b/crates/pumpkin/src/block/blocks/pumpkin.rs index e7ce1ddef..39c8e5d2b 100644 --- a/crates/pumpkin/src/block/blocks/pumpkin.rs +++ b/crates/pumpkin/src/block/blocks/pumpkin.rs @@ -41,11 +41,7 @@ impl crate::block::BlockBehaviour for PumpkinBlock { ItemStack::new(4, &Item::PUMPKIN_SEEDS), )); args.world.spawn_entity(item_entity); - let player = Arc::clone(args.player); - let slot = args.equipment_slot.clone(); - tokio::spawn(async move { - player.damage_item_in_slot(&slot, 1).await; - }); + args.player.damage_item_in_slot(args.equipment_slot, 1); BlockActionResult::Consume } } diff --git a/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs b/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs index 351465898..0048b35b1 100644 --- a/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs +++ b/crates/pumpkin/src/block/blocks/redstone/daylight_detector.rs @@ -28,7 +28,11 @@ impl BlockBehaviour for DaylightDetectorBlock { } 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 { return BlockActionResult::Pass; } diff --git a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs index d28bd7dbe..ce062a1bc 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -669,7 +669,7 @@ impl DispenserBlock { item: &mut ItemStack, ) { 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); return; }; @@ -717,7 +717,6 @@ impl DispenserBlock { *ctx.position, ctx.facing.to_block_direction(), ) - .await }; if emptied { @@ -737,7 +736,7 @@ impl DispenserBlock { true } else { Ignition::ignite_block( - |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { + |world: Arc, pos: BlockPos, new_state_id: BlockStateId| { world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); }, ctx.world, @@ -745,7 +744,6 @@ impl DispenserBlock { front, front_block, ) - .await }; if ignited { @@ -761,7 +759,7 @@ impl DispenserBlock { let front = Self::target_position(ctx); 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); Self::play_dispense_effects(ctx, WorldEvent::SoundDispenserDispense); } else { diff --git a/crates/pumpkin/src/block/blocks/respawn_anchor.rs b/crates/pumpkin/src/block/blocks/respawn_anchor.rs index 47b4a987c..afecefff1 100644 --- a/crates/pumpkin/src/block/blocks/respawn_anchor.rs +++ b/crates/pumpkin/src/block/blocks/respawn_anchor.rs @@ -54,13 +54,9 @@ impl BlockBehaviour for RespawnAnchorBlock { if args.world.dimension != Dimension::THE_NETHER { args.world .break_block(args.position, None, BlockFlags::SKIP_DROPS); - let world = Arc::clone(args.world); let center_pos = args.position.to_centered_f64(); - tokio::spawn(async move { - world - .explode(center_pos, 5.0, crate::world::ExplosionInteraction::Block) - .await; - }); + args.world + .explode(center_pos, 5.0, crate::world::ExplosionInteraction::Block); return BlockActionResult::SuccessServer; } diff --git a/crates/pumpkin/src/block/fluid/flowing_trait.rs b/crates/pumpkin/src/block/fluid/flowing_trait.rs index 445782c6d..a86b32e7d 100644 --- a/crates/pumpkin/src/block/fluid/flowing_trait.rs +++ b/crates/pumpkin/src/block/fluid/flowing_trait.rs @@ -72,12 +72,7 @@ pub trait FlowingFluid: Send + Sync { /// 3. Triggering fluid spread to adjacent positions /// /// Sources (level 8, non-falling) always spread without state changes. - async fn on_scheduled_tick_internal( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, - ) { + fn on_scheduled_tick_internal(&self, world: &Arc, fluid: &Fluid, block_pos: &BlockPos) { let current_block_state_id = world.get_block_state_id(block_pos); let block = Block::from_state_id(current_block_state_id); @@ -96,7 +91,7 @@ pub trait FlowingFluid: Send + Sync { // Update state if non-source 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 { 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 - self.try_flow(world, fluid, block_pos, &state_for_spreading) - .await; + self.try_flow(world, fluid, block_pos, &state_for_spreading); } /// 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 /// /// Sources with 3+ adjacent sources also spread to sides when flowing down. - async fn try_flow( + fn try_flow( &self, world: &Arc, fluid: &Fluid, @@ -153,24 +147,23 @@ pub trait FlowingFluid: Send + Sync { // Try to flow down first if is_hole { let falling_props = self.get_flowing(fluid, Level::L8, true); - self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid)) - .await; + self.spread_to(world, fluid, &below_pos, falling_props.to_state_id(fluid)); // Check if we should also spread to sides if props.level == Level::L8 && props.falling == Falling::False { - let source_count = self.count_source_neighbors(world, fluid, block_pos).await; + let source_count = self.count_source_neighbors(world, fluid, block_pos); if source_count >= 3 { - self.flow_to_sides(world, fluid, block_pos, props).await; + self.flow_to_sides(world, fluid, block_pos, props); } } return; } // 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, world: &Arc, fluid: &Fluid, @@ -205,7 +198,7 @@ pub trait FlowingFluid: Send + Sync { /// /// # Returns /// New fluid properties, or None if fluid should drain - async fn get_new_liquid( + fn get_new_liquid( &self, world: &Arc, fluid: &Fluid, @@ -295,7 +288,7 @@ pub trait FlowingFluid: Send + Sync { /// - Fluid tick scheduling for non-source blocks /// /// Called by `spread_to` implementations after fluid-specific pre-checks. - async fn apply_spread( + fn apply_spread( &self, world: &Arc, fluid: &Fluid, @@ -318,9 +311,7 @@ pub trait FlowingFluid: Send + Sync { // Check for infinite source formation before quiescence checks if !current_is_source && self.can_convert_to_source(world) { - let should_convert = self - .check_infinite_source_formation(world, fluid, pos) - .await; + let should_convert = self.check_infinite_source_formation(world, fluid, pos); if should_convert { let source_props = self.get_source(fluid, false); @@ -355,7 +346,7 @@ pub trait FlowingFluid: Send + Sync { &pumpkin_data::Block::WATER, ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; @@ -365,9 +356,7 @@ pub trait FlowingFluid: Send + Sync { // Check for infinite source formation after placing new fluid if self.can_convert_to_source(world) { - let should_convert = self - .check_infinite_source_formation(world, fluid, pos) - .await; + let should_convert = self.check_infinite_source_formation(world, fluid, pos); if should_convert { let source_props = self.get_source(fluid, false); @@ -396,7 +385,7 @@ pub trait FlowingFluid: Send + Sync { /// /// # Returns /// `true` if position should convert to a source block - async fn check_infinite_source_formation( + fn check_infinite_source_formation( &self, world: &Arc, fluid: &Fluid, @@ -444,23 +433,16 @@ pub trait FlowingFluid: Send + Sync { /// /// Default implementation delegates to `apply_spread`. Implementations like /// lava can override to add fluid-specific logic (e.g., water -> stone conversion). - async fn spread_to( - &self, - world: &Arc, - fluid: &Fluid, - pos: &BlockPos, - state_id: BlockStateId, - ) { + fn spread_to(&self, world: &Arc, fluid: &Fluid, pos: &BlockPos, state_id: BlockStateId) { let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); - self.apply_spread(world, fluid, pos, state_id, new_props) - .await; + self.apply_spread(world, fluid, pos, state_id, new_props); } /// Spreads fluid horizontally to adjacent positions using pathfinding. /// /// Uses `get_spread` to find optimal flow directions (shortest distance to holes) /// and the computed fluid state for each target position. - async fn flow_to_sides( + fn flow_to_sides( &self, world: &Arc, fluid: &Fluid, @@ -479,12 +461,12 @@ pub trait FlowingFluid: Send + Sync { 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) { 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); } } } diff --git a/crates/pumpkin/src/block/fluid/lava.rs b/crates/pumpkin/src/block/fluid/lava.rs index fc17273ca..9067b6d90 100644 --- a/crates/pumpkin/src/block/fluid/lava.rs +++ b/crates/pumpkin/src/block/fluid/lava.rs @@ -6,7 +6,6 @@ use crate::{ }; use pumpkin_data::{ Block, BlockDirection, BlockState, BlockStateId, - block_properties::blocks_movement, damage::DamageType, dimension::Dimension, fluid::{Falling, Fluid, FluidProperties, Level}, @@ -160,12 +159,7 @@ impl FluidBehaviour for FlowingLava { } fn on_scheduled_tick(&self, world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) { - let world = world.clone(); - let block_pos = *block_pos; - tokio::spawn(async move { - Self.on_scheduled_tick_internal(&world, &Fluid::FLOWING_LAVA, &block_pos) - .await; - }); + Self.on_scheduled_tick_internal(world, &Fluid::FLOWING_LAVA, block_pos); } fn on_neighbor_update( @@ -193,68 +187,58 @@ impl FluidBehaviour for FlowingLava { } fn random_tick(&self, _fluid: &Fluid, world: &Arc, block_pos: &BlockPos) { - let world = world.clone(); - let block_pos = *block_pos; - tokio::spawn(async move { - if !Self::can_spread_fire_around(&world, &block_pos) { - return; - } + if !Self::can_spread_fire_around(world, block_pos) { + return; + } - let passes = rand::random_range(0..3); - if passes > 0 { - let mut test_pos = block_pos; + let passes = rand::random_range(0..3); + if passes > 0 { + let mut test_pos = *block_pos; - for _ in 0..passes { - test_pos = test_pos.offset(Vector3::new( - rand::random_range(-1..=1), - 1, - rand::random_range(-1..=1), - )); + for _ in 0..passes { + test_pos = test_pos.offset(Vector3::new( + rand::random_range(-1..=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; } - - 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; - } - } - } else { - for _ in 0..3 { - let test_pos = block_pos.offset(Vector3::new( - rand::random_range(-1..=1), - 0, - rand::random_range(-1..=1), - )); - - if !world.is_loaded(&test_pos) { - return; - } - - let above_pos = test_pos.up(); - if !world.is_loaded(&above_pos) { - return; - } - - if world - .get_block_state_if_loaded(&above_pos) - .is_some_and(BlockState::is_air) - && Self::is_flammable(&world, &test_pos) - { - Self::ignite_fire_if_possible(&world, &above_pos); - } + } else if block.is_solid() { + return; } } - }); + } else { + for _ in 0..3 { + let test_pos = block_pos.offset(Vector3::new( + rand::random_range(-1..=1), + 0, + rand::random_range(-1..=1), + )); + + if !world.is_loaded(&test_pos) { + return; + } + + let above_pos = test_pos.up(); + if world + .get_block_state_if_loaded(&above_pos) + .is_some_and(BlockState::is_air) + && Self::is_flammable(world, &test_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 } - async fn spread_to( - &self, - world: &Arc, - fluid: &Fluid, - pos: &BlockPos, - state_id: BlockStateId, - ) { + fn spread_to(&self, world: &Arc, fluid: &Fluid, pos: &BlockPos, state_id: BlockStateId) { let new_props = FlowingFluidProperties::from_state_id(state_id, fluid); let current_state_id = world.get_block_state_id(pos); 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 - self.apply_spread(world, fluid, pos, state_id, new_props) - .await; + self.apply_spread(world, fluid, pos, state_id, new_props); } } diff --git a/crates/pumpkin/src/block/fluid/pathfinder.rs b/crates/pumpkin/src/block/fluid/pathfinder.rs index fb12e1f20..e2d8b14b5 100644 --- a/crates/pumpkin/src/block/fluid/pathfinder.rs +++ b/crates/pumpkin/src/block/fluid/pathfinder.rs @@ -30,7 +30,7 @@ fn is_hole(world: &Arc, fluid: &Fluid, pos: &BlockPos) -> bool { /// - Holes (downward flow opportunities) get distance 0 priority /// - All directions with equal minimum distance are returned /// - Returns up to 4 directions with their computed fluid states -pub async fn get_spread( +pub fn get_spread( fluid_impl: &T, world: &Arc, fluid: &Fluid, @@ -63,7 +63,7 @@ pub async fn get_spread( } // 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; }; diff --git a/crates/pumpkin/src/block/fluid/water.rs b/crates/pumpkin/src/block/fluid/water.rs index cc955302d..1c7f6d29a 100644 --- a/crates/pumpkin/src/block/fluid/water.rs +++ b/crates/pumpkin/src/block/fluid/water.rs @@ -36,12 +36,7 @@ impl FluidBehaviour for FlowingWater { } fn on_scheduled_tick(&self, world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) { - let world = world.clone(); - let block_pos = *block_pos; - tokio::spawn(async move { - Self.on_scheduled_tick_internal(&world, &Fluid::FLOWING_WATER, &block_pos) - .await; - }); + Self.on_scheduled_tick_internal(world, &Fluid::FLOWING_WATER, block_pos); } fn on_neighbor_update( diff --git a/crates/pumpkin/src/block/mod.rs b/crates/pumpkin/src/block/mod.rs index cdeb7e1d4..b291e837e 100644 --- a/crates/pumpkin/src/block/mod.rs +++ b/crates/pumpkin/src/block/mod.rs @@ -401,7 +401,7 @@ pub struct BlockEvent { pub data: u8, } -pub async fn drop_loot( +pub fn drop_loot( world: &Arc, block: &Block, pos: &BlockPos, @@ -419,7 +419,7 @@ pub async fn drop_loot( cancelled: false, }; if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if !event.cancelled { for stack in event.items { @@ -440,7 +440,7 @@ pub async fn drop_loot( exp: amount, }; 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 { ExperienceOrbEntity::spawn(world, pos.to_f64(), event.exp as u32); diff --git a/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs b/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs index fe724d47d..ae45a2dbb 100644 --- a/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs +++ b/crates/pumpkin/src/command/argument_types/entity_selector/mod.rs @@ -521,7 +521,8 @@ impl EntitySelectorPredicate { let has_tag = entity .get_entity() .scoreboard_tags - .blocking_lock() + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) .contains(expected_tag); has_tag ^ invert } @@ -567,8 +568,7 @@ impl EntitySelectorPredicate { } Self::Nbt(expected_nbt, invert) => { let mut actual_nbt = NbtCompound::default(); - // write_nbt is asynchronous, so we can poll it synchronously because it does not do IO. - futures::executor::block_on(entity.write_nbt(&mut actual_nbt)); + entity.write_nbt(&mut actual_nbt); matches_nbt_compound(expected_nbt, &actual_nbt) ^ invert } Self::Predicate(_predicate_id, invert) => { diff --git a/crates/pumpkin/src/command/commands/advancement.rs b/crates/pumpkin/src/command/commands/advancement.rs index 52cb1f813..ef79218ba 100644 --- a/crates/pumpkin/src/command/commands/advancement.rs +++ b/crates/pumpkin/src/command/commands/advancement.rs @@ -314,7 +314,7 @@ async fn perform( } .create_without_context_args_slice(&[ first_advancement.name(), - first_player.get_display_name().await, + first_player.get_display_name(), ])) } else { Err(match action { @@ -333,7 +333,7 @@ async fn perform( } .create_without_context_args_slice(&[ TextComponent::text(advancements.len().to_string()), - first_player.get_display_name().await, + first_player.get_display_name(), ])) } else { Err(match action { @@ -350,10 +350,7 @@ async fn perform( if let [first_player] = targets { TextComponent::translate( format!("{}.one.to.one.success", action.get_key()), - [ - first_advancement.name(), - first_player.get_display_name().await, - ], + [first_advancement.name(), first_player.get_display_name()], ) } else { TextComponent::translate( @@ -369,7 +366,7 @@ async fn perform( format!("{}.many.to.one.success", action.get_key()), [ TextComponent::text(advancements.len().to_string()), - first.get_display_name().await, + first.get_display_name(), ], ) } else { @@ -431,7 +428,7 @@ pub async fn perform_criterion( .create_without_context_args_slice(&[ TextComponent::text(criterion.to_owned()), advancement.name(), - first_player.get_display_name().await, + first_player.get_display_name(), ])) } else { Err(match action { @@ -451,7 +448,7 @@ pub async fn perform_criterion( [ TextComponent::text(criterion.to_owned()), advancement.name(), - first_player.get_display_name().await, + first_player.get_display_name(), ], ) } else { diff --git a/crates/pumpkin/src/command/commands/clear.rs b/crates/pumpkin/src/command/commands/clear.rs index d36c772b6..e8e983b8a 100644 --- a/crates/pumpkin/src/command/commands/clear.rs +++ b/crates/pumpkin/src/command/commands/clear.rs @@ -130,7 +130,7 @@ async fn clear_command_text_output( ([target], true, _) => Err(TextComponent::translate_cross( translation::java::CLEAR_FAILED_SINGLE, translation::bedrock::COMMANDS_CLEAR_FAILURE, - [target.get_display_name().await], + [target.get_display_name()], )), (targets, true, _) => Err(TextComponent::translate_cross( translation::java::CLEAR_FAILED_MULTIPLE, @@ -142,7 +142,7 @@ async fn clear_command_text_output( translation::java::COMMANDS_CLEAR_SUCCESS_SINGLE, [ TextComponent::text(item_count.to_string()), - target.get_display_name().await, + target.get_display_name(), ], )), (targets, false, false) => Ok(TextComponent::translate_cross( @@ -158,7 +158,7 @@ async fn clear_command_text_output( translation::java::COMMANDS_CLEAR_TEST_SINGLE, [ TextComponent::text(item_count.to_string()), - target.get_display_name().await, + target.get_display_name(), ], )), (targets, false, true) => Ok(TextComponent::translate_cross( diff --git a/crates/pumpkin/src/command/commands/damage.rs b/crates/pumpkin/src/command/commands/damage.rs index db9f2d679..80df30d5e 100644 --- a/crates/pumpkin/src/command/commands/damage.rs +++ b/crates/pumpkin/src/command/commands/damage.rs @@ -87,7 +87,7 @@ impl CommandExecutor for LocationExecutor { 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), ); - send_damage_result(sender, success, amount, target.get_display_name().await).await + send_damage_result(sender, success, amount, target.get_display_name()).await }) } } diff --git a/crates/pumpkin/src/command/commands/data.rs b/crates/pumpkin/src/command/commands/data.rs index 08f911ed7..fa776753b 100644 --- a/crates/pumpkin/src/command/commands/data.rs +++ b/crates/pumpkin/src/command/commands/data.rs @@ -32,7 +32,7 @@ impl CommandExecutor for GetEntityDataExecutor { let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else { 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, ) -> Result { let mut nbt = NbtCompound::new(); - entity.write_nbt(&mut nbt).await; + entity.write_nbt(&mut nbt); let tag = NbtTag::Compound(nbt); let result = get_i32_result(&tag)?; diff --git a/crates/pumpkin/src/command/commands/effect.rs b/crates/pumpkin/src/command/commands/effect.rs index d79ba145a..652eed0f1 100644 --- a/crates/pumpkin/src/command/commands/effect.rs +++ b/crates/pumpkin/src/command/commands/effect.rs @@ -124,7 +124,7 @@ impl CommandExecutor for GiveExecutor { .send_message(TextComponent::translate_cross( "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; } else { @@ -183,7 +183,7 @@ impl CommandExecutor for ClearExecutor { .send_message(TextComponent::translate_cross( "commands.effect.clear.everything.success.single", "commands.effect.clear.everything.success.single", - [targets[0].get_display_name().await], + [targets[0].get_display_name()], )) .await; } else { @@ -228,7 +228,7 @@ impl CommandExecutor for ClearExecutor { effect.translation_key, [], ), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], )) .await; diff --git a/crates/pumpkin/src/command/commands/enchant.rs b/crates/pumpkin/src/command/commands/enchant.rs index 7922b4c72..5b6cf214a 100644 --- a/crates/pumpkin/src/command/commands/enchant.rs +++ b/crates/pumpkin/src/command/commands/enchant.rs @@ -76,7 +76,7 @@ impl CommandExecutor for Executor { translation::bedrock::COMMANDS_ENCHANT_SUCCESS, [ enchantment.get_fullname(level), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], ); sender.send_message(msg).await; @@ -141,7 +141,7 @@ async fn enchant_target( let msg = TextComponent::translate_cross( translation::java::COMMANDS_ENCHANT_FAILED_ITEMLESS, translation::bedrock::COMMANDS_ENCHANT_NOITEM, - [target.get_display_name().await], + [target.get_display_name()], ); return Err(CommandError::CommandFailed(msg)); } @@ -170,9 +170,7 @@ async fn enchant_target( let inventory = player.inventory(); inventory.set_held_item(item.clone()); - player - .sync_hand_slot(inventory.get_selected_slot() as usize, item) - .await; + player.sync_hand_slot(inventory.get_selected_slot() as usize, item); Ok(()) } diff --git a/crates/pumpkin/src/command/commands/execute.rs b/crates/pumpkin/src/command/commands/execute.rs index 30d0c2093..c49fd501a 100644 --- a/crates/pumpkin/src/command/commands/execute.rs +++ b/crates/pumpkin/src/command/commands/execute.rs @@ -39,7 +39,7 @@ fn execute_as_modifier<'a>( let mut sources = Vec::new(); for target in targets { 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(); source.entity = Some(target.clone()); source.name = name; diff --git a/crates/pumpkin/src/command/commands/experience.rs b/crates/pumpkin/src/command/commands/experience.rs index 6638bfce4..250d13234 100644 --- a/crates/pumpkin/src/command/commands/experience.rs +++ b/crates/pumpkin/src/command/commands/experience.rs @@ -62,7 +62,7 @@ impl Executor { translation_key, translation_key, [ - target.get_display_name().await, + target.get_display_name(), TextComponent::text(val.to_string()), ], )) @@ -126,20 +126,20 @@ impl Executor { match self.exp_type { ExpType::Levels => { if self.mode == Mode::Add { - target.add_experience_levels(amount).await; + target.add_experience_levels(amount); } else { - target.set_experience_level(amount, true).await; + target.set_experience_level(amount, true); } } ExpType::Points => { if self.mode == Mode::Add { - target.add_experience_points(amount).await; + target.add_experience_points(amount); } else { let current_lvl = target.experience_level.load(Ordering::Relaxed); if amount > experience::points_in_level(current_lvl) { 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 - 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); sender.send_message(msg).await; diff --git a/crates/pumpkin/src/command/commands/fetchprofile.rs b/crates/pumpkin/src/command/commands/fetchprofile.rs index adad40626..adea9e8fc 100644 --- a/crates/pumpkin/src/command/commands/fetchprofile.rs +++ b/crates/pumpkin/src/command/commands/fetchprofile.rs @@ -353,12 +353,12 @@ impl CommandExecutor for PrintForEntityExecutor { &context.source, &player.gameprofile, translation::java::COMMANDS_FETCHPROFILE_ENTITY_SUCCESS, - player.get_display_name().await, + player.get_display_name(), ) .await; Ok(1) } 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())) } }) } diff --git a/crates/pumpkin/src/command/commands/gamemode.rs b/crates/pumpkin/src/command/commands/gamemode.rs index 2ab4c210e..b2cf0b001 100644 --- a/crates/pumpkin/src/command/commands/gamemode.rs +++ b/crates/pumpkin/src/command/commands/gamemode.rs @@ -82,7 +82,7 @@ impl CommandExecutor for TargetExecutor { .send_message(TextComponent::translate_cross( translation::java::COMMANDS_GAMEMODE_SUCCESS_OTHER, translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_OTHER, - [target.get_display_name().await, gamemode_comp], + [target.get_display_name(), gamemode_comp], )) .await; } diff --git a/crates/pumpkin/src/command/commands/give.rs b/crates/pumpkin/src/command/commands/give.rs index 7b704a1f3..b58a1e6df 100644 --- a/crates/pumpkin/src/command/commands/give.rs +++ b/crates/pumpkin/src/command/commands/give.rs @@ -73,7 +73,7 @@ impl CommandExecutor for Executor { id: item_name.to_string().into(), count: Some(item_count.min(99)), }), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], ) } else { diff --git a/crates/pumpkin/src/command/commands/item.rs b/crates/pumpkin/src/command/commands/item.rs index 01b366a9c..e758b1569 100644 --- a/crates/pumpkin/src/command/commands/item.rs +++ b/crates/pumpkin/src/command/commands/item.rs @@ -259,7 +259,7 @@ impl CommandExecutor for EntityReplaceExecutor { 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("[") .add_child(item.translated_name()) .add_child(TextComponent::text("]")) diff --git a/crates/pumpkin/src/command/commands/kick.rs b/crates/pumpkin/src/command/commands/kick.rs index 0f242f2bd..68605541c 100644 --- a/crates/pumpkin/src/command/commands/kick.rs +++ b/crates/pumpkin/src/command/commands/kick.rs @@ -51,13 +51,13 @@ impl CommandExecutor for Executor { TextComponent::translate_cross( translation::java::COMMANDS_KICK_SUCCESS, translation::bedrock::COMMANDS_KICK_SUCCESS_REASON, - [target.get_display_name().await, reason.clone()], + [target.get_display_name(), reason.clone()], ) } else { TextComponent::translate_cross( translation::java::COMMANDS_KICK_SUCCESS, translation::bedrock::COMMANDS_KICK_SUCCESS, - [target.get_display_name().await, reason.clone()], + [target.get_display_name(), reason.clone()], ) }; diff --git a/crates/pumpkin/src/command/commands/kill.rs b/crates/pumpkin/src/command/commands/kill.rs index adaa532f6..8ce9aae7b 100644 --- a/crates/pumpkin/src/command/commands/kill.rs +++ b/crates/pumpkin/src/command/commands/kill.rs @@ -23,14 +23,14 @@ impl CommandExecutor for TargetsExecutor { let target_count = targets.len(); for target in &targets { - target.kill(target.as_ref()).await; + target.kill(target.as_ref()); } let msg = if target_count == 1 { TextComponent::translate_cross( translation::java::COMMANDS_KILL_SUCCESS_SINGLE, translation::bedrock::COMMANDS_KILL_SUCCESSFUL, - [targets[0].get_display_name().await], + [targets[0].get_display_name()], ) } else { TextComponent::translate_cross( @@ -53,7 +53,7 @@ impl CommandExecutor for SelfExecutor { fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> { Box::pin(async move { let target = context.source.entity_or_err()?; - target.kill(&*target).await; + target.kill(&*target); context .source @@ -61,7 +61,7 @@ impl CommandExecutor for SelfExecutor { TextComponent::translate_cross( translation::java::COMMANDS_KILL_SUCCESS_SINGLE, translation::bedrock::COMMANDS_KILL_SUCCESSFUL, - [target.get_display_name().await], + [target.get_display_name()], ), true, ) diff --git a/crates/pumpkin/src/command/commands/list.rs b/crates/pumpkin/src/command/commands/list.rs index e0927b676..ab7e0ff7c 100644 --- a/crates/pumpkin/src/command/commands/list.rs +++ b/crates/pumpkin/src/command/commands/list.rs @@ -12,7 +12,7 @@ use crate::{ context::command_context::CommandContext, node::{CommandExecutor, CommandExecutorResult, dispatcher::CommandDispatcher}, }, - entity::{EntityBase, EntityBaseFuture, player::Player}, + entity::{EntityBase, player::Player}, }; const DESCRIPTION: &str = "Print the list of online players."; @@ -33,7 +33,7 @@ impl CommandExecutor for ListCommandExecutor { let players_len = players.len(); 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), }; @@ -75,10 +75,8 @@ impl CommandExecutor for ListCommandExecutor { } } -async fn get_player_names(players: &[Arc]) -> TextComponent { - let display_name_futures: Vec> = - players.iter().map(|p| p.get_display_name()).collect(); - let display_names = futures::future::join_all(display_name_futures).await; +fn get_player_names(players: &[Arc]) -> TextComponent { + let display_names = players.iter().map(|p| p.get_display_name()).collect(); TextComponent::join_with_comma(display_names) } diff --git a/crates/pumpkin/src/command/commands/msg.rs b/crates/pumpkin/src/command/commands/msg.rs index 901029b75..395c3e80b 100644 --- a/crates/pumpkin/src/command/commands/msg.rs +++ b/crates/pumpkin/src/command/commands/msg.rs @@ -42,8 +42,8 @@ impl CommandExecutor for Executor { .send_message( &TextComponent::text(msg.clone()), MSG_COMMAND_OUTGOING, - &player.get_display_name().await, - Some(&target.get_display_name().await), + &player.get_display_name(), + Some(&target.get_display_name()), ) .await; } @@ -52,8 +52,8 @@ impl CommandExecutor for Executor { .send_message( &TextComponent::text(msg.clone()), MSG_COMMAND_INCOMING, - &player.get_display_name().await, - Some(&target.get_display_name().await), + &player.get_display_name(), + Some(&target.get_display_name()), ) .await; } diff --git a/crates/pumpkin/src/command/commands/playsound.rs b/crates/pumpkin/src/command/commands/playsound.rs index 0dc1d4cc3..223db9832 100644 --- a/crates/pumpkin/src/command/commands/playsound.rs +++ b/crates/pumpkin/src/command/commands/playsound.rs @@ -155,7 +155,7 @@ impl CommandExecutor for Executor { translation::bedrock::COMMANDS_PLAYSOUND_SUCCESS, [ TextComponent::text(sound_name), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], )) .await; diff --git a/crates/pumpkin/src/command/commands/recipe.rs b/crates/pumpkin/src/command/commands/recipe.rs index 2de2a19f7..074c25201 100644 --- a/crates/pumpkin/src/command/commands/recipe.rs +++ b/crates/pumpkin/src/command/commands/recipe.rs @@ -121,7 +121,7 @@ impl CommandExecutor for RecipeGiveExecutor { translation::java::COMMANDS_RECIPE_GIVE_SUCCESS_SINGLE, [ TextComponent::text(recipe_count_str), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], ); context.source.send_feedback(msg, true).await; @@ -202,7 +202,7 @@ impl CommandExecutor for RecipeTakeExecutor { translation::java::COMMANDS_RECIPE_TAKE_SUCCESS_SINGLE, [ TextComponent::text(taken_count_str), - targets[0].get_display_name().await, + targets[0].get_display_name(), ], ); context.source.send_feedback(msg, true).await; diff --git a/crates/pumpkin/src/command/commands/ride.rs b/crates/pumpkin/src/command/commands/ride.rs index 1f13cb6d7..35632b0d0 100644 --- a/crates/pumpkin/src/command/commands/ride.rs +++ b/crates/pumpkin/src/command/commands/ride.rs @@ -39,13 +39,23 @@ static ERROR_GENERIC: CommandErrorType<2> = CommandErrorType::new( ); #[allow(clippy::assigning_clones)] -async fn is_riding_recursive(entity: &dyn EntityBase, possible_vehicle: &dyn EntityBase) -> bool { - let mut current = possible_vehicle.get_entity().vehicle.lock().await.clone(); +fn is_riding_recursive(entity: &dyn EntityBase, possible_vehicle: &dyn EntityBase) -> bool { + let mut current = possible_vehicle + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); while let Some(vehicle) = current { if vehicle.get_entity().entity_id == entity.get_entity().entity_id { 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 } @@ -79,17 +89,22 @@ impl CommandExecutor for RideMountExecutor { 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()); 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 curr_veh.get_entity().entity_id == vehicle.get_entity().entity_id { last_error = Some(ERROR_ALREADY_RIDING.create_without_context( - target.get_display_name().await, - vehicle.get_display_name().await, + target.get_display_name(), + vehicle.get_display_name(), )); continue; } @@ -102,17 +117,13 @@ impl CommandExecutor for RideMountExecutor { vehicle .get_entity() - .add_passenger(vehicle.clone(), target.clone()) - .await; + .add_passenger(vehicle.clone(), target.clone()); success_count += 1; let msg = TextComponent::translate_cross( translation::java::COMMANDS_RIDE_MOUNT_SUCCESS, translation::java::COMMANDS_RIDE_MOUNT_SUCCESS, - [ - target.get_display_name().await, - vehicle.get_display_name().await, - ], + [target.get_display_name(), vehicle.get_display_name()], ); context.source.send_feedback(msg, true).await; } @@ -122,8 +133,8 @@ impl CommandExecutor for RideMountExecutor { return Err(err); } return Err(ERROR_GENERIC.create_without_context( - targets[0].get_display_name().await, - vehicle.get_display_name().await, + targets[0].get_display_name(), + vehicle.get_display_name(), )); } @@ -143,7 +154,12 @@ impl CommandExecutor for RideDismountExecutor { let mut last_error = None; 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 { vehicle .get_entity() @@ -154,13 +170,12 @@ impl CommandExecutor for RideDismountExecutor { let msg = TextComponent::translate_cross( 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; } else { - last_error = Some( - ERROR_NOT_RIDING.create_without_context(target.get_display_name().await), - ); + last_error = + 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 { return Err(err); } - return Err( - ERROR_NOT_RIDING.create_without_context(targets[0].get_display_name().await) - ); + return Err(ERROR_NOT_RIDING.create_without_context(targets[0].get_display_name())); } Ok(success_count) diff --git a/crates/pumpkin/src/command/commands/rotate.rs b/crates/pumpkin/src/command/commands/rotate.rs index efb7196b7..1a607f246 100644 --- a/crates/pumpkin/src/command/commands/rotate.rs +++ b/crates/pumpkin/src/command/commands/rotate.rs @@ -87,7 +87,7 @@ async fn rotate_entity( /// Sends success message for the rotate command. 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 .send_message(TextComponent::translate_cross( translation::java::COMMANDS_ROTATE_SUCCESS, diff --git a/crates/pumpkin/src/command/commands/spectate.rs b/crates/pumpkin/src/command/commands/spectate.rs index 784298e38..3f8428ca0 100644 --- a/crates/pumpkin/src/command/commands/spectate.rs +++ b/crates/pumpkin/src/command/commands/spectate.rs @@ -35,7 +35,7 @@ impl CommandExecutor for StopSpectateExecutor { }; 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( 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 { - let display_name = player.get_display_name().await; + let display_name = player.get_display_name(); return Err(CommandError::CommandFailed(TextComponent::translate_cross( 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 player_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( 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) .await; - let target_name = target.get_display_name().await; + let target_name = target.get_display_name(); sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED, @@ -155,7 +155,7 @@ impl CommandExecutor for SpectateTargetOtherExecutor { // First validate all players for player in players { 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( 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(); 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( translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, translation::java::COMMANDS_SPECTATE_CANNOT_SPECTATE, @@ -201,7 +201,7 @@ impl CommandExecutor for SpectateTargetOtherExecutor { succeeded += 1; } - let target_name = target.get_display_name().await; + let target_name = target.get_display_name(); sender .send_message(TextComponent::translate_cross( translation::java::COMMANDS_SPECTATE_SUCCESS_STARTED, diff --git a/crates/pumpkin/src/command/commands/summon.rs b/crates/pumpkin/src/command/commands/summon.rs index da5611c3e..367d7c78d 100644 --- a/crates/pumpkin/src/command/commands/summon.rs +++ b/crates/pumpkin/src/command/commands/summon.rs @@ -66,7 +66,7 @@ impl CommandExecutor for Executor { } }; 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); sender .send_message(TextComponent::translate_cross( diff --git a/crates/pumpkin/src/command/commands/tag.rs b/crates/pumpkin/src/command/commands/tag.rs index 6d313bec0..4eacd3d23 100644 --- a/crates/pumpkin/src/command/commands/tag.rs +++ b/crates/pumpkin/src/command/commands/tag.rs @@ -48,8 +48,8 @@ impl CommandExecutor for ChangeExecutor { for target in &targets { let entity = target.get_entity(); let success = match self.0 { - Action::Add => entity.add_scoreboard_tag(&tag).await, - Action::Remove => entity.remove_scoreboard_tag(&tag).await, + Action::Add => entity.add_scoreboard_tag(&tag), + Action::Remove => entity.remove_scoreboard_tag(&tag), }; if success { changed += 1; @@ -90,10 +90,7 @@ impl CommandExecutor for ChangeExecutor { TextComponent::translate_cross( single_key.0, single_key.1, - [ - TextComponent::text(tag), - targets[0].get_display_name().await, - ], + [TextComponent::text(tag), targets[0].get_display_name()], ) } else { TextComponent::translate_cross( @@ -123,7 +120,11 @@ impl CommandExecutor for ListExecutor { // BTreeSet keeps the output deterministic. let mut all_tags = BTreeSet::new(); 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()); } @@ -131,7 +132,7 @@ impl CommandExecutor for ListExecutor { TextComponent::text(all_tags.iter().cloned().collect::>().join(", ")); 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() { TextComponent::translate_cross( translation::java::COMMANDS_TAG_LIST_SINGLE_EMPTY, diff --git a/crates/pumpkin/src/command/commands/teleport.rs b/crates/pumpkin/src/command/commands/teleport.rs index fb3eb21ae..ce7b130a2 100644 --- a/crates/pumpkin/src/command/commands/teleport.rs +++ b/crates/pumpkin/src/command/commands/teleport.rs @@ -70,7 +70,7 @@ async fn success_key_and_arg( multiple_key: &'static str, ) -> (&'static str, TextComponent) { if targets.len() == 1 { - (single_key, targets[0].get_display_name().await) + (single_key, targets[0].get_display_name()) } else { (multiple_key, TextComponent::text(targets.len().to_string())) } @@ -118,7 +118,7 @@ impl CommandExecutor for EntitiesToEntityExecutor { .send_message(TextComponent::translate_cross( key, translation::bedrock::COMMANDS_TP_SUCCESSVICTIM, - [target_arg, destination.get_display_name().await], + [target_arg, destination.get_display_name()], )) .await; @@ -383,10 +383,7 @@ impl CommandExecutor for SelfToEntityExecutor { .send_message(TextComponent::translate_cross( translation::java::COMMANDS_TELEPORT_SUCCESS_ENTITY_SINGLE, translation::bedrock::COMMANDS_TP_SUCCESSVICTIM, - [ - player.get_display_name().await, - destination.get_display_name().await, - ], + [player.get_display_name(), destination.get_display_name()], )) .await; @@ -433,7 +430,7 @@ impl CommandExecutor for SelfToPosExecutor { translation::java::COMMANDS_TELEPORT_SUCCESS_LOCATION_SINGLE, translation::bedrock::COMMANDS_TP_SUCCESS_COORDINATES, [ - player.get_display_name().await, + player.get_display_name(), TextComponent::text(pos.x.to_string()), TextComponent::text(pos.y.to_string()), TextComponent::text(pos.z.to_string()), diff --git a/crates/pumpkin/src/command/commands/title.rs b/crates/pumpkin/src/command/commands/title.rs index c8365e38c..a461cf11f 100644 --- a/crates/pumpkin/src/command/commands/title.rs +++ b/crates/pumpkin/src/command/commands/title.rs @@ -53,11 +53,7 @@ impl CommandExecutor for ClearOrResetExecutor { } else { "commands.title.cleared.single" }; - TextComponent::translate_cross( - text, - text, - [targets[0].get_display_name().await], - ) + TextComponent::translate_cross(text, text, [targets[0].get_display_name()]) } else { let text = if reset { "commands.title.reset.multiple" @@ -105,7 +101,7 @@ impl CommandExecutor for TitleExecutor { TextComponent::translate_cross( format!("commands.title.show.{mode_name}.single").clone(), format!("commands.title.show.{mode_name}.single"), - [targets[0].get_display_name().await], + [targets[0].get_display_name()], ) } else { TextComponent::translate_cross( @@ -148,7 +144,7 @@ impl CommandExecutor for TimesTitleExecutor { TextComponent::translate_cross( "commands.title.times.single", "commands.title.times.single", - [targets[0].get_display_name().await], + [targets[0].get_display_name()], ) } else { TextComponent::translate_cross( diff --git a/crates/pumpkin/src/command/commands/transfer.rs b/crates/pumpkin/src/command/commands/transfer.rs index 8022d35d9..37b4feb8e 100644 --- a/crates/pumpkin/src/command/commands/transfer.rs +++ b/crates/pumpkin/src/command/commands/transfer.rs @@ -128,7 +128,7 @@ impl CommandExecutor for TargetPlayerExecutor { "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(port.to_string()), ], diff --git a/crates/pumpkin/src/command/commands/worldborder.rs b/crates/pumpkin/src/command/commands/worldborder.rs index ea1cbba8a..4105ceb9f 100644 --- a/crates/pumpkin/src/command/commands/worldborder.rs +++ b/crates/pumpkin/src/command/commands/worldborder.rs @@ -52,9 +52,13 @@ impl CommandExecutor for GetExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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 .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_GET, @@ -81,20 +85,28 @@ impl CommandExecutor for SetExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; - let mut border = world.worldborder.lock().await; - let distance = distance_consumer().find_arg_default_name(args)??; - if (distance - border.new_diameter).abs() < f64::EPSILON { - return Err(CommandError::CommandFailed( - pumpkin_macros::translate_cross!( - translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, - translation::bedrock::COMMANDS_WORLDBORDER_SET_SUCCESS - ), - )); - } + 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 { + return Err(CommandError::CommandFailed( + pumpkin_macros::translate_cross!( + translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, + translation::bedrock::COMMANDS_WORLDBORDER_SET_SUCCESS + ), + )); + } + + let d = border.new_diameter; + border.set_diameter(world, distance, None); + (d, (distance - d) as i32) + }; - let d = border.new_diameter; sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE, @@ -104,9 +116,7 @@ impl CommandExecutor for SetExecutor { )) .await; - border.set_diameter(world, distance, None); - - Ok((distance - d) as i32) + Ok(diff) }) } } @@ -124,14 +134,18 @@ impl CommandExecutor for SetTimeExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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 time = time_consumer().find_arg_default_name(args)??; - let old_dist = format!("{:.1}", border.new_diameter); - match distance.total_cmp(&border.new_diameter) { - std::cmp::Ordering::Equal => { + 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 ordering = distance.total_cmp(&border.new_diameter); + if ordering == std::cmp::Ordering::Equal { return Err(CommandError::CommandFailed( pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, @@ -139,8 +153,14 @@ impl CommandExecutor for SetTimeExecutor { ), )); } + 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}"); + match ordering { std::cmp::Ordering::Less => { - let dist = format!("{distance:.1}"); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_SHRINK, @@ -152,7 +172,6 @@ impl CommandExecutor for SetTimeExecutor { .await; } std::cmp::Ordering::Greater => { - let dist = format!("{distance:.1}"); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_GROW, @@ -163,12 +182,10 @@ impl CommandExecutor for SetTimeExecutor { )) .await; } + std::cmp::Ordering::Equal => unreachable!(), } - let d = border.new_diameter; - border.set_diameter(world, distance, Some(i64::from(time) * 1000)); - - Ok((distance - d) as i32) + Ok(diff) }) } } @@ -186,8 +203,6 @@ impl CommandExecutor for AddExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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)??; 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 old_dist = format!("{:.1}", border.new_diameter); + border.set_diameter(world, distance, None); + (dist, old_dist) + }; - let dist = format!("{distance:.1}"); - let old_dist = format!("{:.1}", border.new_diameter); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_IMMEDIATE, @@ -211,7 +235,6 @@ impl CommandExecutor for AddExecutor { TextComponent::text(old_dist) )) .await; - border.set_diameter(world, distance, None); Ok(distance_add as i32) }) } @@ -230,16 +253,19 @@ impl CommandExecutor for AddTimeExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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 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 old_dist = format!("{:.1}", border.new_diameter); - match distance.total_cmp(&border.new_diameter) { - std::cmp::Ordering::Equal => { + let distance = distance_add + border.new_diameter; + let old_dist = format!("{:.1}", border.new_diameter); + let ordering = distance.total_cmp(&border.new_diameter); + if ordering == std::cmp::Ordering::Equal { return Err(CommandError::CommandFailed( pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_FAILED_NOCHANGE, @@ -247,8 +273,13 @@ impl CommandExecutor for AddTimeExecutor { ), )); } + border.set_diameter(world, distance, Some(i64::from(time) * 1000)); + (distance, old_dist, ordering) + }; + + let dist = format!("{distance:.1}"); + match ordering { std::cmp::Ordering::Less => { - let dist = format!("{distance:.1}"); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_SHRINK, @@ -260,7 +291,6 @@ impl CommandExecutor for AddTimeExecutor { .await; } std::cmp::Ordering::Greater => { - let dist = format!("{distance:.1}"); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_SET_GROW, @@ -271,10 +301,9 @@ impl CommandExecutor for AddTimeExecutor { )) .await; } + std::cmp::Ordering::Equal => unreachable!(), } - border.set_diameter(world, distance, Some(i64::from(time) * 1000)); - Ok(distance_add as i32) }) } @@ -293,10 +322,14 @@ impl CommandExecutor for CenterExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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)?; + world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_center(world, x, y); + sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_CENTER_SUCCESS, @@ -305,7 +338,7 @@ impl CommandExecutor for CenterExecutor { TextComponent::text(format!("{y:.2}")) )) .await; - border.set_center(world, x, y); + Ok(0) }) } @@ -324,21 +357,29 @@ impl CommandExecutor for DamageAmountExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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)??; - if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON { - return Err(CommandError::CommandFailed( - pumpkin_macros::translate_cross!( - translation::java::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_FAILED, - translation::bedrock::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_SUCCESS - ), - )); - } + 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 { + return Err(CommandError::CommandFailed( + pumpkin_macros::translate_cross!( + translation::java::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_FAILED, + translation::bedrock::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_SUCCESS + ), + )); + } + + 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}"); - let old_damage = format!("{:.2}", border.damage_per_block); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_DAMAGE_AMOUNT_SUCCESS, @@ -347,7 +388,7 @@ impl CommandExecutor for DamageAmountExecutor { TextComponent::text(old_damage) )) .await; - border.damage_per_block = damage_per_block; + Ok(damage_per_block as i32) }) } @@ -366,21 +407,29 @@ impl CommandExecutor for DamageBufferExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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)??; - if (buffer - border.buffer).abs() < f32::EPSILON { - return Err(CommandError::CommandFailed( - pumpkin_macros::translate_cross!( - translation::java::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_FAILED, - translation::bedrock::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_SUCCESS - ), - )); - } + let old_buf = { + let mut border = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + if (buffer - border.buffer).abs() < f32::EPSILON { + return Err(CommandError::CommandFailed( + pumpkin_macros::translate_cross!( + translation::java::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_FAILED, + translation::bedrock::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_SUCCESS + ), + )); + } + + let old_buf = format!("{:.2}", border.buffer); + border.buffer = buffer; + old_buf + }; let buf = format!("{buffer:.2}"); - let old_buf = format!("{:.2}", border.buffer); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_DAMAGE_BUFFER_SUCCESS, @@ -389,7 +438,7 @@ impl CommandExecutor for DamageBufferExecutor { TextComponent::text(old_buf) )) .await; - border.buffer = buffer; + Ok(buffer as i32) }) } @@ -408,28 +457,37 @@ impl CommandExecutor for WarningDistanceExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); 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)??; - if distance == border.warning_blocks { - return Err(CommandError::CommandFailed( - pumpkin_macros::translate_cross!( - translation::java::COMMANDS_WORLDBORDER_WARNING_DISTANCE_FAILED, - translation::bedrock::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS - ), - )); - } + let old_warning = { + let mut border = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + if distance == border.warning_blocks { + return Err(CommandError::CommandFailed( + pumpkin_macros::translate_cross!( + translation::java::COMMANDS_WORLDBORDER_WARNING_DISTANCE_FAILED, + translation::bedrock::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS + ), + )); + } + + let old_warning = border.warning_blocks; + border.set_warning_distance(world, distance); + old_warning + }; sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS, translation::bedrock::COMMANDS_WORLDBORDER_WARNING_DISTANCE_SUCCESS, TextComponent::text(distance.to_string()), - TextComponent::text(border.warning_blocks.to_string()) + TextComponent::text(old_warning.to_string()) )) .await; - border.set_warning_distance(world, distance); + Ok(distance) }) } @@ -448,28 +506,37 @@ impl CommandExecutor for WarningTimeExecutor { // TODO: Maybe ask player for world, or get the current world let worlds = server.worlds.load(); let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; - let mut border = world.worldborder.lock().await; - let time = time_consumer().find_arg_default_name(args)??; - if time == border.warning_time { - return Err(CommandError::CommandFailed( - pumpkin_macros::translate_cross!( - translation::java::COMMANDS_WORLDBORDER_WARNING_TIME_FAILED, - translation::bedrock::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS - ), - )); - } + let old_time = { + let mut border = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + if time == border.warning_time { + return Err(CommandError::CommandFailed( + pumpkin_macros::translate_cross!( + translation::java::COMMANDS_WORLDBORDER_WARNING_TIME_FAILED, + translation::bedrock::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS + ), + )); + } + + let old_time = border.warning_time; + border.set_warning_delay(world, time); + old_time + }; sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS, translation::bedrock::COMMANDS_WORLDBORDER_WARNING_TIME_SUCCESS, TextComponent::text(time.to_string()), - TextComponent::text(border.warning_time.to_string()) + TextComponent::text(old_time.to_string()) )) .await; - border.set_warning_delay(world, time); + Ok(time) }) } diff --git a/crates/pumpkin/src/command/context/command_source.rs b/crates/pumpkin/src/command/context/command_source.rs index 833612417..859be40fb 100644 --- a/crates/pumpkin/src/command/context/command_source.rs +++ b/crates/pumpkin/src/command/context/command_source.rs @@ -203,7 +203,7 @@ impl CommandSource { #[must_use] pub async fn with_entity(self, entity: Arc) -> Self { let name = entity.get_name().get_text(); - let display_name = entity.get_display_name().await; + let display_name = entity.get_display_name(); Self { output: self.output, world: self.world, diff --git a/crates/pumpkin/src/command/mod.rs b/crates/pumpkin/src/command/mod.rs index 6917e8165..07856f422 100644 --- a/crates/pumpkin/src/command/mod.rs +++ b/crates/pumpkin/src/command/mod.rs @@ -331,8 +331,8 @@ impl CommandSender { Some(player.clone()), player.position(), player.rotation().into(), - player.get_display_name().await.get_text(), - player.get_display_name().await, + player.get_display_name().get_text(), + player.get_display_name(), server.clone(), ), Self::CommandBlock(command_entity, world) => { diff --git a/crates/pumpkin/src/data/player_server.rs b/crates/pumpkin/src/data/player_server.rs index dca02bf2a..f92819abd 100644 --- a/crates/pumpkin/src/data/player_server.rs +++ b/crates/pumpkin/src/data/player_server.rs @@ -54,7 +54,7 @@ impl ServerPlayerData { player.on_handled_screen_closed().await; let mut nbt = NbtCompound::new(); - player.write_nbt(&mut nbt).await; + player.write_nbt(&mut nbt); let storage = self.storage.clone(); let uuid = player.gameprofile.id; @@ -84,7 +84,7 @@ impl ServerPlayerData { for world in server.worlds.load().iter() { for player in world.players.load().iter() { let mut nbt = NbtCompound::new(); - player.write_nbt(&mut nbt).await; + player.write_nbt(&mut nbt); let storage = self.storage.clone(); let uuid = player.gameprofile.id; @@ -194,7 +194,7 @@ impl ServerPlayerData { let uuid = player.gameprofile.id; let mut nbt = NbtCompound::new(); - player.write_nbt(&mut nbt).await; + player.write_nbt(&mut nbt); let storage = self.storage.clone(); tokio::task::spawn_blocking(move || storage.save_player_data(&uuid, nbt)) diff --git a/crates/pumpkin/src/entity/boss/ender_dragon.rs b/crates/pumpkin/src/entity/boss/ender_dragon.rs index 44b0672d0..7338dbabc 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon.rs @@ -4,9 +4,8 @@ use pumpkin_data::{Block, BlockStateId}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::{chunk::ChunkHeightmapType, world::BlockFlags}; -use std::sync::Arc; use std::sync::atomic::Ordering; -use tokio::sync::Mutex; +use std::sync::{Arc, Mutex}; use crate::entity::{ Entity, EntityBase, @@ -284,31 +283,43 @@ impl EnderDragonEntity { } } - pub async fn set_phase(&self, phase_type: EnderDragonPhase) { - let mut phase_lock = self.phase.lock().await; + pub fn set_phase(&self, phase_type: EnderDragonPhase) { + let mut phase_lock = self + .phase + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if *phase_lock == phase_type { return; } let old_phase = self.phase_manager.get_phase(*phase_lock); - old_phase.end(self).await; + old_phase.end(self); *phase_lock = 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) { - let mut initialized = self.nodes_initialized.lock().await; + fn ensure_nodes_initialized(&self) { + let mut initialized = self + .nodes_initialized + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if *initialized { return; } 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 { let mut y_adjustment = 5; let node_x; @@ -346,19 +357,28 @@ impl EnderDragonEntity { let new_path = find_path(&nodes, nearest, dest, None); drop(nodes); - *self.target_node.lock().await = dest; - *self.path.lock().await = new_path; + *self + .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; } - 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(); - self.find_closest_node_to(pos).await + self.find_closest_node_to(pos) } - pub async fn find_closest_node_to(&self, pos: Vector3) -> usize { - self.ensure_nodes_initialized().await; - let nodes = self.nodes.lock().await; + pub fn find_closest_node_to(&self, pos: Vector3) -> usize { + self.ensure_nodes_initialized(); + let nodes = self + .nodes + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); 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) } - pub async fn steer_toward( + pub fn steer_toward( &self, pos: Vector3, target: Vector3, @@ -459,7 +479,10 @@ impl EnderDragonEntity { } 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 += y_rot_d * turn_speed; entity.yaw.store(yaw + *y_rot_a * 0.1); @@ -485,11 +508,24 @@ impl EnderDragonEntity { )); } - async fn update_flap_time(&self) { - let sitting = self.phase.lock().await.is_sitting(); - let in_wall = *self.in_wall.lock().await; - let mut flap = self.flap_time.lock().await; - let mut o_flap = self.o_flap_time.lock().await; + fn update_flap_time(&self) { + let sitting = self + .phase + .lock() + .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; *flap += if sitting { @@ -504,11 +540,19 @@ impl EnderDragonEntity { } } - async fn tick_growl(&self) { - if self.phase.lock().await.is_sitting() { + fn tick_growl(&self) { + if self + .phase + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_sitting() + { 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 { *t -= 1; } else { @@ -537,7 +581,7 @@ impl EnderDragonEntity { .cloned() } - async fn handle_player_collisions(&self) { + fn handle_player_collisions(&self) { let world = self.mob_entity.living_entity.entity.world.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); 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); } } @@ -596,8 +645,11 @@ impl EnderDragonEntity { } } - async fn tick_block_breaking(&self) { - let phase_type = *self.phase.lock().await; + fn tick_block_breaking(&self) { + let phase_type = *self + .phase + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if phase_type.is_sitting() || phase_type == EnderDragonPhase::Dying { return; } @@ -622,9 +674,11 @@ impl EnderDragonEntity { } } - async fn tick_parts(&self) { - let history: tokio::sync::MutexGuard<'_, DragonFlightHistory> = - self.flight_history.lock().await; + fn tick_parts(&self) { + let history = self + .flight_history + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let p5 = history.get(5); let p10 = history.get(10); let p0 = history.get(0); @@ -657,13 +711,21 @@ impl EnderDragonEntity { 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 } else { (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 ss2 = rot2.sin() 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.ensure_nodes_initialized().await; - self.update_flap_time().await; + self.ensure_nodes_initialized(); + self.update_flap_time(); { let y = self.mob_entity.living_entity.entity.pos.load().y; 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(); { @@ -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); 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 { - self.handle_player_collisions().await; - self.tick_block_breaking().await; + self.handle_player_collisions(); + self.tick_block_breaking(); } - phase.tick(self).await; + phase.tick(self); if phase_type == EnderDragonPhase::Dying { 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 { let pos = self.mob_entity.living_entity.entity.pos.load(); self.steer_toward( @@ -750,18 +824,23 @@ impl EnderDragonEntity { phase.get_fly_speed(), phase.get_turn_speed(), 0.5, - ) - .await; + ); } self.mob_entity.living_entity.entity.send_pos_rot(); - self.tick_parts().await; + self.tick_parts(); } - pub async fn hurt(&self, damage: f32) { - let phase_type: EnderDragonPhase = *self.phase.lock().await; + pub fn hurt(&self, damage: f32) { + let phase_type: EnderDragonPhase = *self + .phase + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); 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) { - let entity_id = self.mob_entity.living_entity.entity.entity_id; - let world = self.mob_entity.living_entity.entity.world.load_full(); - tokio::spawn(async move { - let Some(entity) = world.get_entity_by_id(entity_id) else { - return; - }; - let Some(dragon) = entity.cast_any().downcast_ref::() else { - return; - }; - dragon.ai_step().await; - }); + self.ai_step(); } fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { let living = &self.mob_entity.living_entity; if living.health.load() <= 0.0 { - let world = self.mob_entity.living_entity.entity.world.load_full(); - let entity_id = self.mob_entity.living_entity.entity.entity_id; - tokio::spawn(async move { - if let Some(entity) = world.get_entity_by_id(entity_id) - && let Some(dragon) = entity.cast_any().downcast_ref::() - { - dragon.set_phase(EnderDragonPhase::Dying).await; - } - }); + self.set_phase(EnderDragonPhase::Dying); } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/charging.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/charging.rs index fbae7840b..ca9e4a060 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/charging.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/charging.rs @@ -1,7 +1,6 @@ use super::EnderDragonPhase; use crate::entity::EntityBase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; pub struct ChargingPhase; @@ -11,34 +10,41 @@ impl super::Phase for ChargingPhase { EnderDragonPhase::Charging } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let target_id = { - let guard = dragon.target_player.lock().await; - *guard + fn tick(&self, dragon: &EnderDragonEntity) { + let target_id = { + let guard = dragon + .target_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *guard + }; + let world = dragon.mob_entity.living_entity.entity.world.load(); + let pos = dragon.mob_entity.living_entity.entity.pos.load(); + + let target_pos = if let Some(id) = target_id + && let Some(player) = world.players.load().iter().find(|p| p.gameprofile.id == id) + { + player.get_entity().pos.load() + } else { + let origin = { + let guard = dragon + .fight_origin + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.0 }; - let world = dragon.mob_entity.living_entity.entity.world.load(); - let pos = dragon.mob_entity.living_entity.entity.pos.load(); + Vector3::new(origin.x as f64, NODE_Y as f64 - 20.0, origin.z as f64) + }; - let target_pos = if let Some(id) = target_id - && let Some(player) = world.players.load().iter().find(|p| p.gameprofile.id == id) - { - player.get_entity().pos.load() - } else { - let origin = { - let guard = dragon.fight_origin.lock().await; - guard.0 - }; - Vector3::new(origin.x as f64, NODE_Y as f64 - 20.0, origin.z as f64) - }; + if pos.distance_squared(target_pos) < 25.0 { + dragon.set_phase(EnderDragonPhase::Hovering); + return; + } - if pos.distance_squared(target_pos) < 25.0 { - dragon.set_phase(EnderDragonPhase::Hovering).await; - 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 { diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs index 2b018e34d..ed49a00b1 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/circling.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; use std::sync::atomic::Ordering; @@ -11,84 +10,104 @@ impl super::Phase for CirclingPhase { EnderDragonPhase::Circling } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let pos = dragon.mob_entity.living_entity.entity.pos.load(); - let mut target_location = dragon.target_location.lock().await; + fn tick(&self, dragon: &EnderDragonEntity) { + let pos = dragon.mob_entity.living_entity.entity.pos.load(); + 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)); - if target_location.is_none() - || !(100.0..=22500.0).contains(&d0) - || dragon - .mob_entity - .living_entity - .entity - .horizontal_collision - .load(Ordering::Relaxed) - { - let mut path = dragon.path.lock().await; - if path.is_empty() { - drop(path); - let i = dragon.find_closest_node().await; - let mut j = i as i32; + if target_location.is_none() + || !(100.0..=22500.0).contains(&d0) + || dragon + .mob_entity + .living_entity + .entity + .horizontal_collision + .load(Ordering::Relaxed) + { + let mut path = dragon + .path + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if path.is_empty() { + drop(path); + let i = dragon.find_closest_node(); + let mut j = i as i32; - let mut clockwise = dragon.holding_pattern_clockwise.lock().await; - if rand::random_range(0..8) == 0 { - *clockwise = !*clockwise; - j = i as i32 + 6; - } - if *clockwise { - j += 1; - } else { - j -= 1; - } - drop(clockwise); - - let world = dragon.mob_entity.living_entity.entity.world.load(); - let crystals_alive = world.dragon_fight.as_ref().is_some_and(|fight| { - fight - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .alive_crystals() - > 0 - }); - - let j = if crystals_alive { - j.rem_euclid(12) as usize - } else { - (j - 12).rem_euclid(8) as usize + 12 - }; - - let mut path_lock = dragon.path.lock().await; - let nodes = dragon.nodes.lock().await; - *path_lock = find_path(&nodes, i, j, None); - drop(nodes); - path = path_lock; + let mut clockwise = dragon + .holding_pattern_clockwise + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if rand::random_range(0..8) == 0 { + *clockwise = !*clockwise; + j = i as i32 + 6; } + if *clockwise { + j += 1; + } else { + j -= 1; + } + drop(clockwise); - if let Some(next_node_idx) = path.first().copied() { - path.remove(0); - let nodes = dragon.nodes.lock().await; - if let Some(node) = nodes[next_node_idx] { - let mut y_target = node.y + rand::random_range(0.0..20.0); - while y_target < node.y { - y_target = node.y + rand::random_range(0.0..20.0); - } - *target_location = Some(Vector3::new(node.x, y_target, node.z)); + let world = dragon.mob_entity.living_entity.entity.world.load(); + let crystals_alive = world.dragon_fight.as_ref().is_some_and(|fight| { + fight + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .alive_crystals() + > 0 + }); + + let j = if crystals_alive { + j.rem_euclid(12) as usize + } else { + (j - 12).rem_euclid(8) as usize + 12 + }; + + let mut path_lock = dragon + .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); + drop(nodes); + path = path_lock; + } + + if let Some(next_node_idx) = path.first().copied() { + path.remove(0); + let nodes = dragon + .nodes + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(node) = nodes[next_node_idx] { + let mut y_target = node.y + rand::random_range(0.0..20.0); + while y_target < node.y { + y_target = node.y + rand::random_range(0.0..20.0); } + *target_location = Some(Vector3::new(node.x, y_target, node.z)); } } - drop(target_location); + } + drop(target_location); - if rand::random_range(0..64) == 0 { - if rand::random_bool(0.5) { - dragon.set_phase(EnderDragonPhase::FlyToPortal).await; - } else if let Some(player) = dragon.find_nearest_player() { - *dragon.target_player.lock().await = Some(player.gameprofile.id); - dragon.set_phase(EnderDragonPhase::Strafing).await; - } + if rand::random_range(0..64) == 0 { + if rand::random_bool(0.5) { + dragon.set_phase(EnderDragonPhase::FlyToPortal); + } else if let Some(player) = dragon.find_nearest_player() { + *dragon + .target_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + Some(player.gameprofile.id); + dragon.set_phase(EnderDragonPhase::Strafing); } - }) + } } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs index 1afe09e83..9a4af318e 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs @@ -1,7 +1,6 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{DEATH_TIMER_MAX, EnderDragonEntity}; use crate::entity::experience_orb::ExperienceOrbEntity; -use futures::future::BoxFuture; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::vector3::Vector3; @@ -13,85 +12,79 @@ impl super::Phase for DyingPhase { EnderDragonPhase::Dying } - fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - *dragon.target_location.lock().await = None; - }) + fn begin(&self, dragon: &EnderDragonEntity) { + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let mut t = dragon.dragon_death_time.lock().await; - *t += 1; + fn tick(&self, dragon: &EnderDragonEntity) { + let mut t = dragon + .dragon_death_time + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *t += 1; - let entity = &dragon.mob_entity.living_entity.entity; - let world = entity.world.load(); + let entity = &dragon.mob_entity.living_entity.entity; + let world = entity.world.load(); - if *t == 1 { - world.play_sound( - Sound::EntityEnderDragonDeath, - SoundCategory::Hostile, - &entity.pos.load(), - ); - } + if *t == 1 { + world.play_sound( + Sound::EntityEnderDragonDeath, + SoundCategory::Hostile, + &entity.pos.load(), + ); + } - if *t >= 180 && *t <= 200 { - let xo = (rand::random::() - 0.5) * 8.0; - let yo = (rand::random::() - 0.5) * 4.0; - let zo = (rand::random::() - 0.5) * 8.0; - let pos = entity.pos.load(); - world.spawn_particle( - Vector3::new( - pos.x + xo as f64, - pos.y + 2.0 + yo as f64, - pos.z + zo as f64, - ), - Vector3::new(0.0, 0.0, 0.0), - 0.0, - 1, - Particle::ExplosionEmitter, - ); - } + if *t >= 180 && *t <= 200 { + let xo = (rand::random::() - 0.5) * 8.0; + let yo = (rand::random::() - 0.5) * 4.0; + let zo = (rand::random::() - 0.5) * 8.0; + let pos = entity.pos.load(); + world.spawn_particle( + Vector3::new( + pos.x + xo as f64, + pos.y + 2.0 + yo as f64, + pos.z + zo as f64, + ), + Vector3::new(0.0, 0.0, 0.0), + 0.0, + 1, + Particle::ExplosionEmitter, + ); + } - let xp_count = if let Some(ref fight_mutex) = world.dragon_fight - && !fight_mutex + let xp_count = if let Some(ref fight_mutex) = world.dragon_fight + && !fight_mutex + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .has_previously_killed_dragon() + { + 12000 + } else { + 500 + }; + + if *t > 150 && *t % 5 == 0 { + ExperienceOrbEntity::spawn(&world, entity.pos.load(), (xp_count as f32 * 0.08) as u32); + } + + entity.velocity.store(Vector3::new(0.0, 0.1, 0.0)); + + if *t >= DEATH_TIMER_MAX { + ExperienceOrbEntity::spawn(&world, entity.pos.load(), (xp_count as f32 * 0.2) as u32); + + if let Some(ref fight_mutex) = world.dragon_fight { + fight_mutex .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) - .has_previously_killed_dragon() - { - 12000 - } else { - 500 - }; - - if *t > 150 && *t % 5 == 0 { - ExperienceOrbEntity::spawn( - &world, - entity.pos.load(), - (xp_count as f32 * 0.08) as u32, - ); + .set_dragon_killed(&world, entity.entity_uuid); } - - entity.velocity.store(Vector3::new(0.0, 0.1, 0.0)); - - if *t >= DEATH_TIMER_MAX { - ExperienceOrbEntity::spawn( - &world, - entity.pos.load(), - (xp_count as f32 * 0.2) as u32, - ); - - if let Some(ref fight_mutex) = world.dragon_fight { - fight_mutex - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .set_dragon_killed(&world, entity.entity_uuid); - } - for part in &dragon.parts { - part.entity.remove(); - } - entity.remove(); + for part in &dragon.parts { + part.entity.remove(); } - }) + entity.remove(); + } } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/fly_to_portal.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/fly_to_portal.rs index 09a335d01..f26428759 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/fly_to_portal.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/fly_to_portal.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; pub struct FlyToPortalPhase; @@ -10,21 +9,25 @@ impl super::Phase for FlyToPortalPhase { EnderDragonPhase::FlyToPortal } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let origin = { - let guard = dragon.fight_origin.lock().await; - guard.0 - }; - 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(); + fn tick(&self, dragon: &EnderDragonEntity) { + let origin = { + let guard = dragon + .fight_origin + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.0 + }; + 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(); - if pos.distance_squared(target) < 4.0 { - dragon.set_phase(EnderDragonPhase::LandingApproach).await; - return; - } + if pos.distance_squared(target) < 4.0 { + dragon.set_phase(EnderDragonPhase::LandingApproach); + return; + } - *dragon.target_location.lock().await = Some(target); - }) + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target); } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/hovering.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/hovering.rs index 96f65fe08..47df3dee4 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/hovering.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/hovering.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; pub struct HoveringPhase; @@ -10,20 +9,24 @@ impl super::Phase for HoveringPhase { EnderDragonPhase::Hovering } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let origin = { - let guard = dragon.fight_origin.lock().await; - guard.0 - }; - let target = Vector3::new(origin.x as f64, NODE_Y as f64 + 10.0, origin.z as f64); + fn tick(&self, dragon: &EnderDragonEntity) { + let origin = { + let guard = dragon + .fight_origin + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.0 + }; + let target = Vector3::new(origin.x as f64, NODE_Y as f64 + 10.0, origin.z as f64); - if rand::random_bool(0.01) { - dragon.set_phase(EnderDragonPhase::TakingOff).await; - return; - } + if rand::random_bool(0.01) { + dragon.set_phase(EnderDragonPhase::TakingOff); + return; + } - *dragon.target_location.lock().await = Some(target); - }) + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target); } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing.rs index 637c51760..dcd970f0a 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::EnderDragonEntity; -use futures::future::BoxFuture; pub struct LandingPhase; @@ -9,17 +8,22 @@ impl super::Phase for LandingPhase { EnderDragonPhase::Landing } - fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - *dragon.target_location.lock().await = None; - }) + fn begin(&self, dragon: &EnderDragonEntity) { + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - dragon.set_phase(EnderDragonPhase::SitAttacking).await; - *dragon.ticks_sitting.lock().await = 0; - *dragon.sit_attack_timer.lock().await = 0; - }) + fn tick(&self, dragon: &EnderDragonEntity) { + dragon.set_phase(EnderDragonPhase::SitAttacking); + *dragon + .ticks_sitting + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = 0; + *dragon + .sit_attack_timer + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = 0; } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing_approach.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing_approach.rs index abc03ec6a..fadf7607e 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing_approach.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/landing_approach.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, Vector3Ext}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; pub struct LandingApproachPhase; @@ -10,21 +9,25 @@ impl super::Phase for LandingApproachPhase { EnderDragonPhase::LandingApproach } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let origin = { - let guard = dragon.fight_origin.lock().await; - guard.0 - }; - 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(); + fn tick(&self, dragon: &EnderDragonEntity) { + let origin = { + let guard = dragon + .fight_origin + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.0 + }; + 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(); - if pos.distance_squared(target) < 1.0 { - dragon.set_phase(EnderDragonPhase::Landing).await; - return; - } + if pos.distance_squared(target) < 1.0 { + dragon.set_phase(EnderDragonPhase::Landing); + return; + } - *dragon.target_location.lock().await = Some(target); - }) + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target); } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/mod.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/mod.rs index 772f12d1d..92558681f 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/mod.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/mod.rs @@ -1,5 +1,4 @@ use crate::entity::boss::ender_dragon::EnderDragonEntity; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; use std::sync::Arc; @@ -29,13 +28,9 @@ pub use taking_off::TakingOffPhase; pub trait Phase: Send + Sync { fn get_type(&self) -> EnderDragonPhase; - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()>; - fn begin<'a>(&'a self, _dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async {}) - } - fn end<'a>(&'a self, _dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async {}) - } + fn tick(&self, dragon: &EnderDragonEntity); + fn begin(&self, _dragon: &EnderDragonEntity) {} + fn end(&self, _dragon: &EnderDragonEntity) {} fn is_sitting(&self) -> bool { false } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_attacking.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_attacking.rs index d317912c4..8ed6c9dca 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_attacking.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_attacking.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::EnderDragonEntity; -use futures::future::BoxFuture; pub struct SitAttackingPhase; @@ -9,39 +8,51 @@ impl super::Phase for SitAttackingPhase { EnderDragonPhase::SitAttacking } - fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - *dragon.target_location.lock().await = None; - }) + fn begin(&self, dragon: &EnderDragonEntity) { + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let mut timer = dragon.sit_attack_timer.lock().await; - *timer += 1; + fn tick(&self, dragon: &EnderDragonEntity) { + let mut timer = dragon + .sit_attack_timer + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *timer += 1; - if *timer > 40 { - *timer = 0; - let should_breathe = rand::random_bool(0.5); - let should_take_off = *dragon.ticks_sitting.lock().await > 200; - drop(timer); + if *timer > 40 { + *timer = 0; + let should_breathe = rand::random_bool(0.5); + let should_take_off = *dragon + .ticks_sitting + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + > 200; + drop(timer); - if should_breathe { - dragon.set_phase(EnderDragonPhase::SitBreathing).await; - *dragon.breathing_timer.lock().await = 0; - } else if should_take_off { - dragon.set_phase(EnderDragonPhase::TakingOff).await; - } - } else { - drop(timer); + if should_breathe { + dragon.set_phase(EnderDragonPhase::SitBreathing); + *dragon + .breathing_timer + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = 0; + } else if should_take_off { + dragon.set_phase(EnderDragonPhase::TakingOff); } + } else { + drop(timer); + } - let mut dmg = dragon.sitting_damage_received.lock().await; - if *dmg > 150.0 { - *dmg = 0.0; - drop(dmg); - dragon.set_phase(EnderDragonPhase::TakingOff).await; - } - }) + let mut dmg = dragon + .sitting_damage_received + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if *dmg > 150.0 { + *dmg = 0.0; + drop(dmg); + dragon.set_phase(EnderDragonPhase::TakingOff); + } } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs index f6adca9b8..8371d5f37 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/sit_breathing.rs @@ -2,7 +2,6 @@ use super::EnderDragonPhase; use crate::entity::{ Entity, area_effect_cloud::AreaEffectCloudEntity, boss::ender_dragon::EnderDragonEntity, }; -use futures::future::BoxFuture; use pumpkin_data::entity::EntityType; use pumpkin_util::math::vector3::Vector3; @@ -13,61 +12,66 @@ impl super::Phase for SitBreathingPhase { EnderDragonPhase::SitBreathing } - fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - *dragon.target_location.lock().await = None; - }) + fn begin(&self, dragon: &EnderDragonEntity) { + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let mut timer = dragon.breathing_timer.lock().await; - *timer += 1; + fn tick(&self, dragon: &EnderDragonEntity) { + let mut timer = dragon + .breathing_timer + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *timer += 1; - if *timer > 100 { - *timer = 0; - drop(timer); - dragon.set_phase(EnderDragonPhase::SitAttacking).await; - return; - } + if *timer > 100 { + *timer = 0; drop(timer); + dragon.set_phase(EnderDragonPhase::SitAttacking); + return; + } + drop(timer); - let timer_val = *dragon.breathing_timer.lock().await; - if timer_val == 1 { - let entity = &dragon.mob_entity.living_entity.entity; - let pos = entity.pos.load(); - let yaw = entity.yaw.load().to_radians() as f64; - let world = entity.world.load(); + let timer_val = *dragon + .breathing_timer + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if timer_val == 1 { + let entity = &dragon.mob_entity.living_entity.entity; + let pos = entity.pos.load(); + let yaw = entity.yaw.load().to_radians() as f64; + let world = entity.world.load(); - // Spawn the lingering cloud at the dragon's head position - let offset = Vector3::new(-yaw.sin() * 2.0, 0.5, yaw.cos() * 2.0); - let cloud_pos = pos.add(&offset); + // Spawn the lingering cloud at the dragon's head position + let offset = Vector3::new(-yaw.sin() * 2.0, 0.5, yaw.cos() * 2.0); + let cloud_pos = pos.add(&offset); - let cloud_entity = - Entity::new(world.clone(), cloud_pos, &EntityType::AREA_EFFECT_CLOUD); - let cloud = AreaEffectCloudEntity::create( - cloud_entity, - pumpkin_data::item_stack::ItemStack::new( - 0, - &pumpkin_data::item::Item::DRAGON_BREATH, - ), - vec![( - &pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE, - 1, - 0, - false, - true, - true, - )], - 600, // duration - 3.0, // radius - 20, // reapplication delay - 20, // wait time - 0.5, // radius on use - -100, // duration on use - ); - world.spawn_entity(cloud); - } - }) + let cloud_entity = + Entity::new(world.clone(), cloud_pos, &EntityType::AREA_EFFECT_CLOUD); + let cloud = AreaEffectCloudEntity::create( + cloud_entity, + pumpkin_data::item_stack::ItemStack::new( + 0, + &pumpkin_data::item::Item::DRAGON_BREATH, + ), + vec![( + &pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE, + 1, + 0, + false, + true, + true, + )], + 600, // duration + 3.0, // radius + 20, // reapplication delay + 20, // wait time + 0.5, // radius on use + -100, // duration on use + ); + world.spawn_entity(cloud); + } } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs index ffc735e09..d83bd784a 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/strafing.rs @@ -5,7 +5,6 @@ use crate::entity::{ area_effect_cloud::AreaEffectCloudEntity, boss::ender_dragon::{EnderDragonEntity, Vector3Ext, find_path}, }; -use futures::future::BoxFuture; use pumpkin_data::entity::EntityType; use pumpkin_util::math::vector3::Vector3; use std::sync::atomic::Ordering; @@ -17,153 +16,182 @@ impl super::Phase for StrafingPhase { EnderDragonPhase::Strafing } - fn begin<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - *dragon.fireball_charge.lock().await = 0; - *dragon.target_location.lock().await = None; - }) + fn begin(&self, dragon: &EnderDragonEntity) { + *dragon + .fireball_charge + .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)] - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let target_id = *dragon.target_player.lock().await; - let world = dragon.mob_entity.living_entity.entity.world.load(); - let pos = dragon.mob_entity.living_entity.entity.pos.load(); + fn tick(&self, dragon: &EnderDragonEntity) { + let target_id = *dragon + .target_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let world = dragon.mob_entity.living_entity.entity.world.load(); + let pos = dragon.mob_entity.living_entity.entity.pos.load(); - let player_target = if let Some(id) = target_id { - world - .players - .load() - .iter() - .find(|p| p.gameprofile.id == id) - .cloned() - } else { - None - }; + let player_target = if let Some(id) = target_id { + world + .players + .load() + .iter() + .find(|p| p.gameprofile.id == id) + .cloned() + } else { + None + }; - let Some(player) = player_target else { - dragon.set_phase(EnderDragonPhase::Circling).await; - return; - }; + let Some(player) = player_target else { + dragon.set_phase(EnderDragonPhase::Circling); + return; + }; - let player_pos = player.get_entity().pos.load(); - let mut path = dragon.path.lock().await; - let mut target_location = dragon.target_location.lock().await; + let player_pos = player.get_entity().pos.load(); + let mut path = dragon + .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() { + let d2 = player_pos.x - pos.x; + let d3 = player_pos.z - pos.z; + let d4 = d2.hypot(d3); + let d5 = (0.4 + d4 / 80.0 - 1.0).clamp(0.0, 10.0); + *target_location = Some(Vector3::new(player_pos.x, player_pos.y + d5, player_pos.z)); + } + + let d11 = target_location + .map(|loc| pos.distance_squared(loc)) + .unwrap_or(0.0); + if !(100.0..=22500.0).contains(&d11) + || dragon + .mob_entity + .living_entity + .entity + .horizontal_collision + .load(Ordering::Relaxed) + { if path.is_empty() { - let d2 = player_pos.x - pos.x; - let d3 = player_pos.z - pos.z; - let d4 = d2.hypot(d3); - let d5 = (0.4 + d4 / 80.0 - 1.0).clamp(0.0, 10.0); - *target_location = - Some(Vector3::new(player_pos.x, player_pos.y + d5, player_pos.z)); + drop(path); + let i = dragon.find_closest_node(); + let j = dragon.find_closest_node_to(player_pos); + + let mut path_lock = dragon + .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); + drop(nodes); + path = path_lock; } - let d11 = target_location - .map(|loc| pos.distance_squared(loc)) - .unwrap_or(0.0); - if !(100.0..=22500.0).contains(&d11) - || dragon - .mob_entity - .living_entity - .entity - .horizontal_collision - .load(Ordering::Relaxed) - { - if path.is_empty() { - drop(path); - let i = dragon.find_closest_node().await; - let j = dragon.find_closest_node_to(player_pos).await; - - let mut path_lock = dragon.path.lock().await; - let nodes = dragon.nodes.lock().await; - *path_lock = find_path(&nodes, i, j, None); - drop(nodes); - path = path_lock; - } - - if let Some(next_node_idx) = path.first().copied() { - path.remove(0); - let nodes = dragon.nodes.lock().await; - if let Some(node) = nodes[next_node_idx] { - let mut y_target = node.y + rand::random_range(0.0..20.0); - while y_target < node.y { - y_target = node.y + rand::random_range(0.0..20.0); - } - *target_location = Some(Vector3::new(node.x, y_target, node.z)); + if let Some(next_node_idx) = path.first().copied() { + path.remove(0); + let nodes = dragon + .nodes + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(node) = nodes[next_node_idx] { + let mut y_target = node.y + rand::random_range(0.0..20.0); + while y_target < node.y { + y_target = node.y + rand::random_range(0.0..20.0); } + *target_location = Some(Vector3::new(node.x, y_target, node.z)); } } - drop(path); - drop(target_location); + } + drop(path); + drop(target_location); - if player_pos.distance_squared(pos) < 4096.0 { - let mut charge = dragon.fireball_charge.lock().await; + if player_pos.distance_squared(pos) < 4096.0 { + let mut charge = dragon + .fireball_charge + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - let aim_diff = player_pos - pos; - let aim = if aim_diff.length_squared() > 1e-6 { - aim_diff.normalize() - } else { - Vector3::new(0.0, 0.0, 0.0) - }; - - let yaw = dragon.mob_entity.living_entity.entity.yaw.load(); - let dir = Vector3::new( - (yaw * (std::f32::consts::PI / 180.0)).sin() as f64, - 0.0, - -(yaw * (std::f32::consts::PI / 180.0)).cos() as f64, - ); - - let dir_norm = if dir.length_squared() > 1e-6 { - dir.normalize() - } else { - Vector3::new(0.0, 0.0, 0.0) - }; - - let dot = dir_norm.dot(&aim) as f32; - let angle_degs = dot.acos().to_degrees() + 0.5; - - *charge += 1; - - if *charge >= 5 && angle_degs < 10.0 { - *charge = 0; - drop(charge); - - let cloud_entity = - Entity::new(world.clone(), player_pos, &EntityType::AREA_EFFECT_CLOUD); - let cloud = AreaEffectCloudEntity::create( - cloud_entity, - pumpkin_data::item_stack::ItemStack::new( - 0, - &pumpkin_data::item::Item::DRAGON_BREATH, - ), - vec![( - &pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE, - 1, - 0, - false, - true, - true, - )], - 600, - 3.0, - 20, - 20, - 0.5, - -100, - ); - world.spawn_entity(cloud); - - dragon.path.lock().await.clear(); - dragon.set_phase(EnderDragonPhase::Circling).await; - } + let aim_diff = player_pos - pos; + let aim = if aim_diff.length_squared() > 1e-6 { + aim_diff.normalize() } else { - let mut charge = dragon.fireball_charge.lock().await; - if *charge > 0 { - *charge -= 1; - } + Vector3::new(0.0, 0.0, 0.0) + }; + + let yaw = dragon.mob_entity.living_entity.entity.yaw.load(); + let dir = Vector3::new( + (yaw * (std::f32::consts::PI / 180.0)).sin() as f64, + 0.0, + -(yaw * (std::f32::consts::PI / 180.0)).cos() as f64, + ); + + let dir_norm = if dir.length_squared() > 1e-6 { + dir.normalize() + } else { + Vector3::new(0.0, 0.0, 0.0) + }; + + let dot = dir_norm.dot(&aim) as f32; + let angle_degs = dot.acos().to_degrees() + 0.5; + + *charge += 1; + + if *charge >= 5 && angle_degs < 10.0 { + *charge = 0; + drop(charge); + + let cloud_entity = + Entity::new(world.clone(), player_pos, &EntityType::AREA_EFFECT_CLOUD); + let cloud = AreaEffectCloudEntity::create( + cloud_entity, + pumpkin_data::item_stack::ItemStack::new( + 0, + &pumpkin_data::item::Item::DRAGON_BREATH, + ), + vec![( + &pumpkin_data::effect::StatusEffect::INSTANT_DAMAGE, + 1, + 0, + false, + true, + true, + )], + 600, + 3.0, + 20, + 20, + 0.5, + -100, + ); + world.spawn_entity(cloud); + + dragon + .path + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clear(); + dragon.set_phase(EnderDragonPhase::Circling); } - }) + } else { + let mut charge = dragon + .fireball_charge + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if *charge > 0 { + *charge -= 1; + } + } } } diff --git a/crates/pumpkin/src/entity/boss/ender_dragon/phase/taking_off.rs b/crates/pumpkin/src/entity/boss/ender_dragon/phase/taking_off.rs index 4fec74bf4..81b0bca85 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon/phase/taking_off.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon/phase/taking_off.rs @@ -1,6 +1,5 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{EnderDragonEntity, NODE_Y, Vector3Ext}; -use futures::future::BoxFuture; use pumpkin_util::math::vector3::Vector3; pub struct TakingOffPhase; @@ -10,21 +9,25 @@ impl super::Phase for TakingOffPhase { EnderDragonPhase::TakingOff } - fn tick<'a>(&'a self, dragon: &'a EnderDragonEntity) -> BoxFuture<'a, ()> { - Box::pin(async move { - let origin = { - let guard = dragon.fight_origin.lock().await; - guard.0 - }; - 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(); + fn tick(&self, dragon: &EnderDragonEntity) { + let origin = { + let guard = dragon + .fight_origin + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + guard.0 + }; + 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(); - if pos.distance_squared(target) < 16.0 { - dragon.set_phase(EnderDragonPhase::Circling).await; - return; - } + if pos.distance_squared(target) < 16.0 { + dragon.set_phase(EnderDragonPhase::Circling); + return; + } - *dragon.target_location.lock().await = Some(target); - }) + *dragon + .target_location + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(target); } } diff --git a/crates/pumpkin/src/entity/boss/wither.rs b/crates/pumpkin/src/entity/boss/wither.rs index 248cbc69c..0170aa4b8 100644 --- a/crates/pumpkin/src/entity/boss/wither.rs +++ b/crates/pumpkin/src/entity/boss/wither.rs @@ -1,8 +1,7 @@ use std::sync::{ - Arc, Weak, + Arc, Mutex, Weak, atomic::{AtomicBool, AtomicI32, Ordering}, }; -use tokio::sync::Mutex; use uuid::Uuid; use pumpkin_data::{ @@ -26,7 +25,7 @@ use pumpkin_world::world::BlockFlags; use crate::{ entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, revenge::RevengeGoal, @@ -259,7 +258,7 @@ impl WitherEntity { } } - async fn update_bossbar(&self, world: &Arc, progress: f32) { + fn update_bossbar(&self, world: &Arc, progress: f32) { let pos = self.mob_entity.living_entity.entity.pos.load(); let tracking_radius_sq = 50.0 * 50.0; let players = world.players.load(); @@ -273,7 +272,10 @@ impl WitherEntity { .map(|p| p.gameprofile.id) .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 ¤t { if !bossbar_players.contains(&uid) { @@ -306,8 +308,11 @@ impl WitherEntity { } } - async fn remove_all_bossbar(&self, world: &Arc) { - let mut bossbar_players = self.bossbar_players.lock().await; + fn remove_all_bossbar(&self, world: &Arc) { + let mut bossbar_players = self + .bossbar_players + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let players = world.players.load(); for player in players.iter() { if bossbar_players.contains(&player.gameprofile.id) { @@ -318,18 +323,18 @@ impl WitherEntity { } #[expect(clippy::too_many_lines)] - async fn async_mob_tick(&self) { + fn tick_wither(&self) { let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); if world.level_info.load().difficulty == Difficulty::Peaceful { - self.remove_all_bossbar(&world).await; + self.remove_all_bossbar(&world); entity.remove(); return; } 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) { let pos = entity.block_pos.load(); world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); @@ -343,18 +348,16 @@ impl WitherEntity { if invul > 0 { let new_count = invul - 1; let progress = (1.0 - (new_count as f32) / 220.0).clamp(0.0, 1.0); - self.update_bossbar(&world, progress).await; + self.update_bossbar(&world, progress); if new_count <= 0 { let pos = entity.pos.load(); let eye_y = pos.y + entity.get_eye_height(); - world - .explode( - Vector3::new(pos.x, eye_y, pos.z), - 7.0, - ExplosionInteraction::Mob, - ) - .await; + world.explode( + Vector3::new(pos.x, eye_y, pos.z), + 7.0, + ExplosionInteraction::Mob, + ); if !entity.silent.load(Ordering::Relaxed) { world.sync_world_event( @@ -378,7 +381,7 @@ impl WitherEntity { } else { 0.0 }; - self.update_bossbar(&world, progress).await; + self.update_bossbar(&world, progress); if tick_count % 20 == 0 { living.heal(1.0); @@ -604,17 +607,7 @@ impl Mob for WitherEntity { } fn mob_tick<'a>(&'a self, _caller: &'a Arc) { - let entity_id = self.mob_entity.living_entity.entity.entity_id; - let world = self.mob_entity.living_entity.entity.world.load_full(); - tokio::spawn(async move { - let Some(entity) = world.get_entity_by_id(entity_id) else { - return; - }; - let Some(wither) = entity.cast_any().downcast_ref::() else { - return; - }; - wither.async_mob_tick().await; - }); + self.tick_wither(); } fn pre_damage(&self, damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool { @@ -662,35 +655,22 @@ impl Mob for WitherEntity { fn post_tick(&self) { let entity = &self.mob_entity.living_entity.entity; if !entity.is_alive() || self.mob_entity.living_entity.health.load() <= 0.0 { - let entity_id = entity.entity_id; let world = entity.world.load_full(); - tokio::spawn(async move { - let Some(entity) = world.get_entity_by_id(entity_id) else { - return; - }; - let Some(wither) = entity.cast_any().downcast_ref::() else { - return; - }; - wither.remove_all_bossbar(&world).await; - if !wither.dropped_loot.swap(true, Ordering::SeqCst) { - let pos = entity.get_entity().block_pos.load(); - world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); - } - }); + self.remove_all_bossbar(&world); + if !self.dropped_loot.swap(true, Ordering::SeqCst) { + let pos = entity.get_entity().block_pos.load(); + world.drop_stack(&pos, ItemStack::new(1, &Item::NETHER_STAR)); + } } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("Invul", self.get_invulnerable_ticks()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("Invul", self.get_invulnerable_ticks()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(invul) = nbt.get_int("Invul") { - self.set_invulnerable_ticks(invul); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(invul) = nbt.get_int("Invul") { + self.set_invulnerable_ticks(invul); + } } } diff --git a/crates/pumpkin/src/entity/breath.rs b/crates/pumpkin/src/entity/breath.rs index be992fc8b..03779d726 100644 --- a/crates/pumpkin/src/entity/breath.rs +++ b/crates/pumpkin/src/entity/breath.rs @@ -74,9 +74,7 @@ impl BreathManager { player.entity_id(), new_air, ); - tokio::spawn(async move { - server.plugin_manager.fire(&server, &mut event).await; - }); + server.plugin_manager.fire_blocking(&server, &mut event); } self.send_air_supply(player); } diff --git a/crates/pumpkin/src/entity/decoration/armor_stand.rs b/crates/pumpkin/src/entity/decoration/armor_stand.rs index d618820b0..3b17bcb2d 100644 --- a/crates/pumpkin/src/entity/decoration/armor_stand.rs +++ b/crates/pumpkin/src/entity/decoration/armor_stand.rs @@ -1,6 +1,6 @@ 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 pumpkin_data::item_stack::ItemStack; use pumpkin_data::{ @@ -230,72 +230,68 @@ impl ArmorStandEntity { } impl EntityBase for ArmorStandEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let disabled_slots = self.disabled_slots.load(Ordering::Relaxed); - // ... + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + let disabled_slots = self.disabled_slots.load(Ordering::Relaxed); + // ... - nbt.put_bool("Invisible", self.is_invisible()); - nbt.put_bool("Small", self.is_small()); - nbt.put_bool("ShowArms", self.should_show_arms()); - nbt.put_int("DisabledSlots", disabled_slots); - nbt.put_bool("NoBasePlate", !self.should_show_base_plate()); - if self.is_marker() { - nbt.put_bool("Marker", true); - } + nbt.put_bool("Invisible", self.is_invisible()); + nbt.put_bool("Small", self.is_small()); + nbt.put_bool("ShowArms", self.should_show_arms()); + nbt.put_int("DisabledSlots", disabled_slots); + nbt.put_bool("NoBasePlate", !self.should_show_base_plate()); + if self.is_marker() { + nbt.put_bool("Marker", true); + } - nbt.put("Pose", self.pack_rotation()); - }) + nbt.put("Pose", self.pack_rotation()); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let mut flags = 0u8; - // ... + fn read_custom_nbt(&self, nbt: &NbtCompound) { + let mut flags = 0u8; + // ... - if let Some(invisible) = nbt.get_bool("Invisible") - && invisible - { - self.get_entity().set_invisible(invisible); - } + if let Some(invisible) = nbt.get_bool("Invisible") + && invisible + { + self.get_entity().set_invisible(invisible); + } - if let Some(small) = nbt.get_bool("Small") - && small - { - flags |= ArmorStandFlags::Small as u8; - } + if let Some(small) = nbt.get_bool("Small") + && small + { + flags |= ArmorStandFlags::Small as u8; + } - if let Some(show_arms) = nbt.get_bool("ShowArms") - && show_arms - { - flags |= ArmorStandFlags::ShowArms as u8; - } + if let Some(show_arms) = nbt.get_bool("ShowArms") + && show_arms + { + flags |= ArmorStandFlags::ShowArms as u8; + } - if let Some(disabled_slots) = nbt.get_int("DisabledSlots") { - self.disabled_slots.store(disabled_slots, Ordering::Relaxed); - } + if let Some(disabled_slots) = nbt.get_int("DisabledSlots") { + self.disabled_slots.store(disabled_slots, Ordering::Relaxed); + } - if let Some(no_base_plate) = nbt.get_bool("NoBasePlate") { - if !no_base_plate { - flags |= ArmorStandFlags::HideBasePlate as u8; - } - } else { + if let Some(no_base_plate) = nbt.get_bool("NoBasePlate") { + if !no_base_plate { flags |= ArmorStandFlags::HideBasePlate as u8; } + } else { + flags |= ArmorStandFlags::HideBasePlate as u8; + } - if let Some(marker) = nbt.get_bool("Marker") - && marker - { - flags |= ArmorStandFlags::Marker as u8; - } + if let Some(marker) = nbt.get_bool("Marker") + && marker + { + flags |= ArmorStandFlags::Marker as u8; + } - self.armor_stand_flags.store(flags, Ordering::Relaxed); + self.armor_stand_flags.store(flags, Ordering::Relaxed); - if let Some(pose_tag) = nbt.get("Pose") { - let packed: PackedRotation = pose_tag.clone().into(); - self.unpack_rotation(&packed); - } - }) + if let Some(pose_tag) = nbt.get("Pose") { + let packed: PackedRotation = pose_tag.clone().into(); + self.unpack_rotation(&packed); + } } fn get_entity(&self) -> &Entity { @@ -306,11 +302,9 @@ impl EntityBase for ArmorStandEntity { Some(&self.living_entity) } - fn kill<'a>(&'a self, _caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.get_entity().remove(); - // TODO: emit GameEvent::ENTITY_DIE - }) + fn kill(&self, _caller: &dyn EntityBase) { + self.get_entity().remove(); + // TODO: emit GameEvent::ENTITY_DIE } fn damage_with_context( diff --git a/crates/pumpkin/src/entity/decoration/display.rs b/crates/pumpkin/src/entity/decoration/display.rs index 6fae2cb3a..79744e24a 100644 --- a/crates/pumpkin/src/entity/decoration/display.rs +++ b/crates/pumpkin/src/entity/decoration/display.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::{ use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use crate::{ - entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}, + entity::{Entity, EntityBase, living::LivingEntity}, server::Server, }; @@ -796,20 +796,16 @@ impl BlockDisplayEntity { } impl EntityBase for BlockDisplayEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.write_display_nbt(nbt); - nbt.put_int("block_state", self.block_state.load(Ordering::Relaxed)); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + self.display.write_display_nbt(nbt); + nbt.put_int("block_state", self.block_state.load(Ordering::Relaxed)); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.read_display_nbt(nbt); - if let Some(state) = nbt.get_int("block_state") { - self.block_state.store(state, Ordering::Relaxed); - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + self.display.read_display_nbt(nbt); + if let Some(state) = nbt.get_int("block_state") { + self.block_state.store(state, Ordering::Relaxed); + } } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} @@ -919,42 +915,38 @@ impl ItemDisplayEntity { } impl EntityBase for ItemDisplayEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.write_display_nbt(nbt); - let display_mode_str = match self.item_display.load(Ordering::Relaxed) { - 1 => "thirdperson_lefthand", - 2 => "thirdperson_righthand", - 3 => "firstperson_lefthand", - 4 => "firstperson_righthand", - 5 => "head", - 6 => "gui", - 7 => "ground", - 8 => "fixed", - _ => "none", - }; - nbt.put_string("item_display", display_mode_str.to_string()); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + self.display.write_display_nbt(nbt); + let display_mode_str = match self.item_display.load(Ordering::Relaxed) { + 1 => "thirdperson_lefthand", + 2 => "thirdperson_righthand", + 3 => "firstperson_lefthand", + 4 => "firstperson_righthand", + 5 => "head", + 6 => "gui", + 7 => "ground", + 8 => "fixed", + _ => "none", + }; + nbt.put_string("item_display", display_mode_str.to_string()); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.read_display_nbt(nbt); - if let Some(mode_str) = nbt.get_string("item_display") { - let mode = match mode_str { - "thirdperson_lefthand" => 1, - "thirdperson_righthand" => 2, - "firstperson_lefthand" => 3, - "firstperson_righthand" => 4, - "head" => 5, - "gui" => 6, - "ground" => 7, - "fixed" => 8, - _ => 0, - }; - self.item_display.store(mode, Ordering::Relaxed); - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + self.display.read_display_nbt(nbt); + if let Some(mode_str) = nbt.get_string("item_display") { + let mode = match mode_str { + "thirdperson_lefthand" => 1, + "thirdperson_righthand" => 2, + "firstperson_lefthand" => 3, + "firstperson_righthand" => 4, + "head" => 5, + "gui" => 6, + "ground" => 7, + "fixed" => 8, + _ => 0, + }; + self.item_display.store(mode, Ordering::Relaxed); + } } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} @@ -1203,77 +1195,73 @@ impl TextDisplayEntity { } impl EntityBase for TextDisplayEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.write_display_nbt(nbt); - let text_json_res = pumpkin_util::serde_json::to_string( - &*self - .text - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner), - ); - if let Ok(text_json) = text_json_res { - nbt.put_string("text", text_json); - } - nbt.put_int("line_width", self.line_width.load(Ordering::Relaxed)); - nbt.put_int("background", self.background.load(Ordering::Relaxed)); - nbt.put_byte("text_opacity", self.text_opacity.load(Ordering::Relaxed)); + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + self.display.write_display_nbt(nbt); + let text_json_res = pumpkin_util::serde_json::to_string( + &*self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + if let Ok(text_json) = text_json_res { + nbt.put_string("text", text_json); + } + nbt.put_int("line_width", self.line_width.load(Ordering::Relaxed)); + nbt.put_int("background", self.background.load(Ordering::Relaxed)); + nbt.put_byte("text_opacity", self.text_opacity.load(Ordering::Relaxed)); - let flags = self.flags.load(Ordering::Relaxed); - nbt.put_bool("shadow", flags & 1 != 0); - nbt.put_bool("see_through", flags & 2 != 0); - nbt.put_bool("default_background", flags & 4 != 0); - let align_str = if flags & 8 != 0 { - "left" - } else if flags & 16 != 0 { - "right" - } else { - "center" - }; - nbt.put_string("alignment", align_str.to_string()); - }) + let flags = self.flags.load(Ordering::Relaxed); + nbt.put_bool("shadow", flags & 1 != 0); + nbt.put_bool("see_through", flags & 2 != 0); + nbt.put_bool("default_background", flags & 4 != 0); + let align_str = if flags & 8 != 0 { + "left" + } else if flags & 16 != 0 { + "right" + } else { + "center" + }; + nbt.put_string("alignment", align_str.to_string()); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.display.read_display_nbt(nbt); - if let Some(text_json) = nbt.get_string("text") - && let Ok(component) = pumpkin_util::serde_json::from_str(text_json) - { - *self - .text - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = component; - } - if let Some(lw) = nbt.get_int("line_width") { - self.line_width.store(lw, Ordering::Relaxed); - } - if let Some(bg) = nbt.get_int("background") { - self.background.store(bg, Ordering::Relaxed); - } - if let Some(opacity) = nbt.get_byte("text_opacity") { - self.text_opacity.store(opacity, Ordering::Relaxed); - } + fn read_custom_nbt(&self, nbt: &NbtCompound) { + self.display.read_display_nbt(nbt); + if let Some(text_json) = nbt.get_string("text") + && let Ok(component) = pumpkin_util::serde_json::from_str(text_json) + { + *self + .text + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = component; + } + if let Some(lw) = nbt.get_int("line_width") { + self.line_width.store(lw, Ordering::Relaxed); + } + if let Some(bg) = nbt.get_int("background") { + self.background.store(bg, Ordering::Relaxed); + } + if let Some(opacity) = nbt.get_byte("text_opacity") { + self.text_opacity.store(opacity, Ordering::Relaxed); + } - let mut flags = 0u8; - if nbt.get_bool("shadow").unwrap_or(false) { - flags |= 1; + let mut flags = 0u8; + if nbt.get_bool("shadow").unwrap_or(false) { + flags |= 1; + } + if nbt.get_bool("see_through").unwrap_or(false) { + flags |= 2; + } + if nbt.get_bool("default_background").unwrap_or(false) { + flags |= 4; + } + if let Some(align) = nbt.get_string("alignment") { + match align { + "left" => flags |= 8, + "right" => flags |= 16, + _ => {} } - if nbt.get_bool("see_through").unwrap_or(false) { - flags |= 2; - } - if nbt.get_bool("default_background").unwrap_or(false) { - flags |= 4; - } - if let Some(align) = nbt.get_string("alignment") { - match align { - "left" => flags |= 8, - "right" => flags |= 16, - _ => {} - } - } - self.flags.store(flags, Ordering::Relaxed); - }) + } + self.flags.store(flags, Ordering::Relaxed); } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} diff --git a/crates/pumpkin/src/entity/decoration/end_crystal.rs b/crates/pumpkin/src/entity/decoration/end_crystal.rs index 00f07ad72..2f8190676 100644 --- a/crates/pumpkin/src/entity/decoration/end_crystal.rs +++ b/crates/pumpkin/src/entity/decoration/end_crystal.rs @@ -52,11 +52,7 @@ impl EntityBase for EndCrystalEntity { if !damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_EXPLOSION) { let world = self.entity.world.load(); let pos = self.entity.pos.load(); - tokio::spawn(async move { - world - .explode(pos, 6.0, crate::world::ExplosionInteraction::Block) - .await; - }); + world.explode(pos, 6.0, crate::world::ExplosionInteraction::Block); } // TODO diff --git a/crates/pumpkin/src/entity/decoration/item_frame.rs b/crates/pumpkin/src/entity/decoration/item_frame.rs index 1b888e810..a9ec34ad7 100644 --- a/crates/pumpkin/src/entity/decoration/item_frame.rs +++ b/crates/pumpkin/src/entity/decoration/item_frame.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; use crate::entity::player::Player; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}; +use crate::entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}; use crossbeam::atomic::AtomicCell; use pumpkin_data::BlockDirection; use pumpkin_data::damage::DamageType; @@ -282,52 +282,48 @@ impl ItemFrameEntity { } impl EntityBase for ItemFrameEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let item = self - .item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if !item.is_empty() { - let mut item_compound = NbtCompound::new(); - item.write_item_stack(&mut item_compound); - nbt.put_compound("Item", item_compound); - } - nbt.put_float("ItemDropChance", self.item_drop_chance.load()); - nbt.put_byte("ItemRotation", self.rotation.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("Fixed", self.fixed.load(Ordering::Relaxed)); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + let item = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !item.is_empty() { + let mut item_compound = NbtCompound::new(); + item.write_item_stack(&mut item_compound); + nbt.put_compound("Item", item_compound); + } + nbt.put_float("ItemDropChance", self.item_drop_chance.load()); + nbt.put_byte("ItemRotation", self.rotation.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("Fixed", self.fixed.load(Ordering::Relaxed)); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(item_compound) = nbt.get_compound("Item") - && let Some(stack) = ItemStack::read_item_stack(item_compound) - { - *self - .item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; - } - self.rotation.store( - (nbt.get_byte("ItemRotation").unwrap_or(0) as u8) % 8, - Ordering::Relaxed, - ); - let facing = nbt.get_byte("Facing").unwrap_or(0) as u8 % 6; - self.facing.store(facing, Ordering::Relaxed); - // The spawn packet's data field carries the frame's direction. - self.entity.data.store(i32::from(facing), Ordering::Relaxed); - self.item_drop_chance - .store(nbt.get_float("ItemDropChance").unwrap_or(1.0)); - self.invisible.store( - nbt.get_bool("Invisible").unwrap_or(false), - Ordering::Relaxed, - ); - self.fixed - .store(nbt.get_bool("Fixed").unwrap_or(false), Ordering::Relaxed); - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + if let Some(item_compound) = nbt.get_compound("Item") + && let Some(stack) = ItemStack::read_item_stack(item_compound) + { + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; + } + self.rotation.store( + (nbt.get_byte("ItemRotation").unwrap_or(0) as u8) % 8, + Ordering::Relaxed, + ); + let facing = nbt.get_byte("Facing").unwrap_or(0) as u8 % 6; + self.facing.store(facing, Ordering::Relaxed); + // The spawn packet's data field carries the frame's direction. + self.entity.data.store(i32::from(facing), Ordering::Relaxed); + self.item_drop_chance + .store(nbt.get_float("ItemDropChance").unwrap_or(1.0)); + self.invisible.store( + nbt.get_bool("Invisible").unwrap_or(false), + Ordering::Relaxed, + ); + self.fixed + .store(nbt.get_bool("Fixed").unwrap_or(false), Ordering::Relaxed); } fn get_entity(&self) -> &Entity { @@ -405,37 +401,31 @@ impl EntityBase for ItemFrameEntity { }) } - fn interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_fixed() { - return false; + fn interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if self.is_fixed() { + return false; + } + + let frame_has_item = !self.get_item().is_empty(); + let has_held_item = !item_stack.is_empty(); + + if frame_has_item { + let new_rot = self.get_rotation() + 1; + self.set_rotation(new_rot, true); + self.entity.play_sound(self.get_rotate_item_sound()); + true + } else if has_held_item && !self.entity.removed.load(Ordering::Relaxed) { + let mut new_stack = item_stack.clone(); + new_stack.item_count = 1; + self.set_item(new_stack, true); + + if !player.is_creative() { + item_stack.decrement(1); } - - let frame_has_item = !self.get_item().is_empty(); - let has_held_item = !item_stack.is_empty(); - - if frame_has_item { - let new_rot = self.get_rotation() + 1; - self.set_rotation(new_rot, true); - self.entity.play_sound(self.get_rotate_item_sound()); - true - } else if has_held_item && !self.entity.removed.load(Ordering::Relaxed) { - let mut new_stack = item_stack.clone(); - new_stack.item_count = 1; - self.set_item(new_stack, true); - - if !player.is_creative() { - item_stack.decrement(1); - } - true - } else { - false - } - }) + true + } else { + false + } } fn damage_with_context( diff --git a/crates/pumpkin/src/entity/decoration/leash_knot.rs b/crates/pumpkin/src/entity/decoration/leash_knot.rs index b5e72b993..f32c99766 100644 --- a/crates/pumpkin/src/entity/decoration/leash_knot.rs +++ b/crates/pumpkin/src/entity/decoration/leash_knot.rs @@ -1,5 +1,5 @@ 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 pumpkin_data::entity::EntityType; use pumpkin_data::item_stack::ItemStack; @@ -135,66 +135,60 @@ impl EntityBase for LeashKnotEntity { } } - fn interact<'a>( - &'a self, - player: &'a Arc, - _item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let world = player.world(); - let knot_id = self.entity.entity_id; - let player_id = player.entity_id(); + fn interact(&self, player: &Arc, _item_stack: &mut ItemStack) -> bool { + let world = player.world(); + let knot_id = self.entity.entity_id; + let player_id = player.entity_id(); - let search_dim = EntityDimensions { - width: 32.0, - height: 32.0, - eye_height: 16.0, - }; - let pos = self.entity.pos.load(); - let search_box = BoundingBox::new_from_pos(pos.x, pos.y, pos.z, &search_dim); - let entities = world.get_entities_at_box(&search_box); + let search_dim = EntityDimensions { + width: 32.0, + height: 32.0, + eye_height: 16.0, + }; + let pos = self.entity.pos.load(); + let search_box = BoundingBox::new_from_pos(pos.x, pos.y, pos.z, &search_dim); + let entities = world.get_entities_at_box(&search_box); - let mut attached_mob = false; - let mut player_leashed_mobs = Vec::new(); + let mut attached_mob = false; + let mut player_leashed_mobs = Vec::new(); + for entity_base in &entities { + let ent = entity_base.get_entity(); + if let Ok(guard) = ent.leashed_to.try_lock() + && let Some(holder) = guard.as_ref() + && holder.get_entity().entity_id == player_id + { + player_leashed_mobs.push(ent); + } + } + + if let Some(self_knot) = Self::get_knot(&world, self.pos) { + for mob in player_leashed_mobs { + mob.leash_to(self_knot.clone() as Arc); + attached_mob = true; + } + } + + let mut any_dropped = false; + if !attached_mob { for entity_base in &entities { let ent = entity_base.get_entity(); if let Ok(guard) = ent.leashed_to.try_lock() && let Some(holder) = guard.as_ref() - && holder.get_entity().entity_id == player_id + && holder.get_entity().entity_id == knot_id { - player_leashed_mobs.push(ent); + ent.leash_to(player.clone() as Arc); + any_dropped = true; } } + } - if let Some(self_knot) = Self::get_knot(&world, self.pos) { - for mob in player_leashed_mobs { - mob.leash_to(self_knot.clone() as Arc); - attached_mob = true; - } - } - - let mut any_dropped = false; - if !attached_mob { - for entity_base in &entities { - let ent = entity_base.get_entity(); - if let Ok(guard) = ent.leashed_to.try_lock() - && let Some(holder) = guard.as_ref() - && holder.get_entity().entity_id == knot_id - { - ent.leash_to(player.clone() as Arc); - any_dropped = true; - } - } - } - - if attached_mob || any_dropped { - self.play_placement_sound(&world); - true - } else { - false - } - }) + if attached_mob || any_dropped { + self.play_placement_sound(&world); + true + } else { + false + } } fn cast_any(&self) -> &dyn std::any::Any { self diff --git a/crates/pumpkin/src/entity/decoration/painting.rs b/crates/pumpkin/src/entity/decoration/painting.rs index a22aa76a7..8755e34d4 100644 --- a/crates/pumpkin/src/entity/decoration/painting.rs +++ b/crates/pumpkin/src/entity/decoration/painting.rs @@ -1,7 +1,7 @@ use core::f32; use std::sync::atomic::Ordering; -use crate::entity::{Entity, EntityBase, NbtFuture, living::LivingEntity}; +use crate::entity::{Entity, EntityBase, living::LivingEntity}; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::vector3::Vector3; @@ -17,17 +17,13 @@ impl PaintingEntity { } impl EntityBase for PaintingEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let facing = nbt.get_byte("facing").unwrap_or(3); - self.entity.data.store(facing as i32, Ordering::Relaxed); - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + let facing = nbt.get_byte("facing").unwrap_or(3); + self.entity.data.store(facing as i32, Ordering::Relaxed); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/effect/hunger.rs b/crates/pumpkin/src/entity/effect/hunger.rs index 29968cae4..a7c5d7527 100644 --- a/crates/pumpkin/src/entity/effect/hunger.rs +++ b/crates/pumpkin/src/entity/effect/hunger.rs @@ -1,4 +1,4 @@ -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct HungerMobEffect; @@ -11,19 +11,13 @@ impl MobEffect for HungerMobEffect { (duration as u32).is_multiple_of(20) } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let world = living.entity.world.load(); - if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) - && let Some(player) = entity.get_player() - { - let exhaustion = 0.1 * (f32::from(amplifier) + 1.0); - player.hunger_manager.add_exhaustion(exhaustion); - } - }) + fn apply_effect_tick(&self, living: &LivingEntity, amplifier: u8) { + let world = living.entity.world.load(); + if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) + && let Some(player) = entity.get_player() + { + let exhaustion = 0.1 * (f32::from(amplifier) + 1.0); + player.hunger_manager.add_exhaustion(exhaustion); + } } } diff --git a/crates/pumpkin/src/entity/effect/infested.rs b/crates/pumpkin/src/entity/effect/infested.rs index b0eae2a1e..6d11c54bf 100644 --- a/crates/pumpkin/src/entity/effect/infested.rs +++ b/crates/pumpkin/src/entity/effect/infested.rs @@ -5,69 +5,65 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; 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::r#type::from_type; pub struct InfestedMobEffect; impl MobEffect for InfestedMobEffect { - fn on_mob_hurt<'a>( - &'a self, - living: &'a LivingEntity, + fn on_mob_hurt( + &self, + living: &LivingEntity, _amplifier: u8, - _damage_type: &'a DamageType, + _damage_type: &DamageType, _damage_amount: f32, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - // Wither, ender dragon and silverfish are immune - if living.entity.entity_type == &EntityType::WITHER - || living.entity.entity_type == &EntityType::ENDER_DRAGON - || living.entity.entity_type == &EntityType::SILVERFISH - { - return; + ) { + // Wither, ender dragon and silverfish are immune + if living.entity.entity_type == &EntityType::WITHER + || living.entity.entity_type == &EntityType::ENDER_DRAGON + || living.entity.entity_type == &EntityType::SILVERFISH + { + return; + } + + let world = living.entity.world.load(); + + // 10% chance to spawn + if rand::random::() <= 0.1 { + let count = rand::random::() % 2 + 1; + let bbox = living.entity.bounding_box.load(); + let center = Vector3::new( + f64::midpoint(bbox.min.x, bbox.max.x), + f64::midpoint(bbox.min.y, bbox.max.y), + f64::midpoint(bbox.min.z, bbox.max.z), + ); + + let rot = living.entity.rotation(); + let vx = rot.x * 0.3; + let vy = rot.y * 0.45; + let vz = rot.z * 0.3; + + for _ in 0..count { + let random_angle = (rand::random::() - 0.5) * std::f32::consts::PI; + let cos_a = random_angle.cos(); + let sin_a = random_angle.sin(); + let rx = vx * cos_a + vz * sin_a; + let rz = -vx * sin_a + vz * cos_a; + + let silver = from_type(&EntityType::SILVERFISH, center, &world, Uuid::new_v4()); + let silver_entity = silver.get_entity(); + silver_entity.set_pos(center); + silver_entity.velocity.store(Vector3::new( + f64::from(rx), + f64::from(vy), + f64::from(rz), + )); + + world.spawn_entity(silver); } - let world = living.entity.world.load(); - - // 10% chance to spawn - if rand::random::() <= 0.1 { - let count = rand::random::() % 2 + 1; - let bbox = living.entity.bounding_box.load(); - let center = Vector3::new( - f64::midpoint(bbox.min.x, bbox.max.x), - f64::midpoint(bbox.min.y, bbox.max.y), - f64::midpoint(bbox.min.z, bbox.max.z), - ); - - let rot = living.entity.rotation(); - let vx = rot.x * 0.3; - let vy = rot.y * 0.45; - let vz = rot.z * 0.3; - - for _ in 0..count { - let random_angle = (rand::random::() - 0.5) * std::f32::consts::PI; - let cos_a = random_angle.cos(); - let sin_a = random_angle.sin(); - let rx = vx * cos_a + vz * sin_a; - let rz = -vx * sin_a + vz * cos_a; - - let silver = from_type(&EntityType::SILVERFISH, center, &world, Uuid::new_v4()); - - let entity = silver.get_entity(); - entity.set_pos(center); - entity.yaw.store(rand::random::() * 360.0); - entity.pitch.store(0.0); - entity.velocity.store(Vector3::new( - f64::from(rx), - f64::from(vy), - f64::from(rz), - )); - - world.spawn_entity(silver); - world.play_sound(Sound::EntitySilverfishHurt, SoundCategory::Hostile, ¢er); - } - } - }) + world.play_sound(Sound::EntitySilverfishHurt, SoundCategory::Hostile, ¢er); + } } } diff --git a/crates/pumpkin/src/entity/effect/mod.rs b/crates/pumpkin/src/entity/effect/mod.rs index e3f9dc3c9..724f18293 100644 --- a/crates/pumpkin/src/entity/effect/mod.rs +++ b/crates/pumpkin/src/entity/effect/mod.rs @@ -9,9 +9,6 @@ pub mod weaving; pub mod wind_charged; pub mod wither; -use std::future::Future; -use std::pin::Pin; - use pumpkin_data::damage::DamageType; use pumpkin_data::effect::StatusEffect; use pumpkin_nbt::compound::NbtCompound; @@ -19,9 +16,7 @@ use pumpkin_nbt::tag::NbtTag; use tracing::warn; use crate::entity::living::LivingEntity; -use crate::entity::{NBTInitFuture, NBTStorage, NBTStorageInit, NbtFuture}; - -pub type EffectFuture<'a, T = ()> = Pin + Send + 'a>>; +use crate::entity::{NBTStorage, NBTStorageInit}; pub trait MobEffect: Send + Sync { /// 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. - fn apply_effect_tick<'a>( - &'a self, - _living: &'a LivingEntity, - _amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move {}) - } + fn apply_effect_tick(&self, _living: &LivingEntity, _amplifier: u8) {} /// Called when an entity carrying this effect is hurt. - fn on_mob_hurt<'a>( - &'a self, - _living: &'a LivingEntity, + fn on_mob_hurt( + &self, + _living: &LivingEntity, _amplifier: u8, - _damage_type: &'a DamageType, + _damage_type: &DamageType, _damage_amount: f32, - ) -> EffectFuture<'a, ()> { - Box::pin(async move {}) + ) { } /// Called when an entity carrying this effect dies. - fn on_mob_death<'a>( - &'a self, - _living: &'a LivingEntity, - _amplifier: u8, - _damage_type: &'a DamageType, - ) -> EffectFuture<'a, ()> { - Box::pin(async move {}) - } + fn on_mob_death(&self, _living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) {} } pub static REGENERATION: regeneration::RegenerationMobEffect = regeneration::RegenerationMobEffect; @@ -99,57 +80,50 @@ pub fn get_mob_effect(effect: &'static StatusEffect) -> Option<&'static dyn MobE } impl NBTStorage for pumpkin_data::potion::Effect { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put("id", self.effect_type.minecraft_name); - if self.amplifier > 0 { - nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier))); - } - nbt.put("duration", NbtTag::Int(self.duration)); - if self.ambient { - nbt.put("ambient", NbtTag::Byte(1)); - } - if !self.show_particles { - nbt.put("show_particles", NbtTag::Byte(0)); - } - let show_icon: i8 = i8::from(self.show_icon); - nbt.put("show_icon", NbtTag::Byte(show_icon)); - }) + fn write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put("id", self.effect_type.minecraft_name); + if self.amplifier > 0 { + nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier))); + } + nbt.put("duration", NbtTag::Int(self.duration)); + if self.ambient { + nbt.put("ambient", NbtTag::Byte(1)); + } + if !self.show_particles { + nbt.put("show_particles", NbtTag::Byte(0)); + } + let show_icon: i8 = i8::from(self.show_icon); + nbt.put("show_icon", NbtTag::Byte(show_icon)); } } impl NBTStorageInit for pumpkin_data::potion::Effect { - fn create_from_nbt<'a>(nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self> - where - Self: 'a, - { - Box::pin(async move { - let Some(effect_id) = nbt.get_string("id") else { - warn!("Unable to read effect. Effect id is not present"); - return None; - }; - let Some(effect_type) = StatusEffect::from_minecraft_name(effect_id) else { - warn!("Unable to read effect. Unknown effect type: {effect_id}"); - return None; - }; - let Some(show_icon) = nbt.get_byte("show_icon") else { - warn!("Unable to read effect. Show icon is not present"); - return None; - }; - let amplifier = nbt.get_int("amplifier").unwrap_or(0) as u8; - let duration = nbt.get_int("duration").unwrap_or(0); - let ambient = nbt.get_byte("ambient").unwrap_or(0) == 1; - let show_particles = nbt.get_byte("show_particles").unwrap_or(1) == 1; - let show_icon = show_icon == 1; - Some(Self { - effect_type, - duration, - amplifier, - ambient, - show_particles, - show_icon, - blend: false, - }) + fn create_from_nbt(nbt: &mut NbtCompound) -> Option { + let Some(effect_id) = nbt.get_string("id") else { + warn!("Unable to read effect. Effect id is not present"); + return None; + }; + let Some(effect_type) = StatusEffect::from_minecraft_name(effect_id) else { + warn!("Unable to read effect. Unknown effect type: {effect_id}"); + return None; + }; + let Some(show_icon) = nbt.get_byte("show_icon") else { + warn!("Unable to read effect. Show icon is not present"); + return None; + }; + let amplifier = nbt.get_int("amplifier").unwrap_or(0) as u8; + let duration = nbt.get_int("duration").unwrap_or(0); + let ambient = nbt.get_byte("ambient").unwrap_or(0) == 1; + let show_particles = nbt.get_byte("show_particles").unwrap_or(1) == 1; + let show_icon = show_icon == 1; + Some(Self { + effect_type, + duration, + amplifier, + ambient, + show_particles, + show_icon, + blend: false, }) } } diff --git a/crates/pumpkin/src/entity/effect/oozing.rs b/crates/pumpkin/src/entity/effect/oozing.rs index 2c35b241d..633c697ad 100644 --- a/crates/pumpkin/src/entity/effect/oozing.rs +++ b/crates/pumpkin/src/entity/effect/oozing.rs @@ -4,7 +4,7 @@ use pumpkin_data::damage::DamageType; use pumpkin_data::entity::EntityType; 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::mob::slime::SlimeEntity; use crate::entity::r#type::from_type; @@ -12,36 +12,29 @@ use crate::entity::r#type::from_type; pub struct OozingMobEffect; impl MobEffect for OozingMobEffect { - fn on_mob_death<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - _damage_type: &'a DamageType, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - // Slimes are immune - if living.entity.entity_type == &EntityType::SLIME { - return; + fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) { + // Slimes are immune + if living.entity.entity_type == &EntityType::SLIME { + return; + } + + let world = living.entity.world.load(); + let pos = living.entity.pos.load(); + let spawn_pos = Vector3::new(pos.x, pos.y + 0.5, pos.z); + + // Spawns 2 slimes of size 2 (medium slimes) + for _ in 0..2 { + let entity_arc = from_type(&EntityType::SLIME, spawn_pos, &world, Uuid::new_v4()); + let entity = entity_arc.get_entity(); + entity.set_pos(spawn_pos); + entity.yaw.store(rand::random::() * 360.0); + entity.pitch.store(0.0); + + if let Some(slime) = entity_arc.cast_any().downcast_ref::() { + slime.set_size(2, true); } - let world = living.entity.world.load(); - let pos = living.entity.pos.load(); - let spawn_pos = Vector3::new(pos.x, pos.y + 0.5, pos.z); - - // Spawns 2 slimes of size 2 (medium slimes) - for _ in 0..2 { - let entity_arc = from_type(&EntityType::SLIME, spawn_pos, &world, Uuid::new_v4()); - let entity = entity_arc.get_entity(); - entity.set_pos(spawn_pos); - entity.yaw.store(rand::random::() * 360.0); - entity.pitch.store(0.0); - - if let Some(slime) = entity_arc.cast_any().downcast_ref::() { - slime.set_size(2, true); - } - - world.spawn_entity(entity_arc); - } - }) + world.spawn_entity(entity_arc); + } } } diff --git a/crates/pumpkin/src/entity/effect/poison.rs b/crates/pumpkin/src/entity/effect/poison.rs index 2ad98f576..33775a80d 100644 --- a/crates/pumpkin/src/entity/effect/poison.rs +++ b/crates/pumpkin/src/entity/effect/poison.rs @@ -1,6 +1,6 @@ use pumpkin_data::damage::DamageType; -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct PoisonMobEffect; @@ -18,25 +18,19 @@ impl MobEffect for PoisonMobEffect { } } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let current_health = living.health.load(); - if current_health > 1.0 - && let Some(dyn_self) = living - .entity - .world - .load() - .get_entity_by_id(living.entity.entity_id) - { - let damage_amount = (current_health - 1.0).min(1.0); - if damage_amount > 0.0 { - dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC); - } + fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) { + let current_health = living.health.load(); + if current_health > 1.0 + && let Some(dyn_self) = living + .entity + .world + .load() + .get_entity_by_id(living.entity.entity_id) + { + let damage_amount = (current_health - 1.0).min(1.0); + if damage_amount > 0.0 { + dyn_self.damage(&*dyn_self, damage_amount, DamageType::MAGIC); } - }) + } } } diff --git a/crates/pumpkin/src/entity/effect/raid_omen.rs b/crates/pumpkin/src/entity/effect/raid_omen.rs index 2dd9f327a..3c6bb10ff 100644 --- a/crates/pumpkin/src/entity/effect/raid_omen.rs +++ b/crates/pumpkin/src/entity/effect/raid_omen.rs @@ -1,4 +1,4 @@ -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct RaidOmenMobEffect; @@ -8,27 +8,21 @@ impl MobEffect for RaidOmenMobEffect { duration == 1 } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let world = living.entity.world.load(); - if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) - && let Some(player) = entity.get_player() - && !player.is_spectator() - { - let raid_pos = player - .get_raid_omen_position() - .unwrap_or_else(|| living.entity.block_pos.load()); - let mut raids = world - .raids - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - raids.create_or_extend_raid(player, raid_pos, &world); - player.clear_raid_omen_position(); - } - }) + fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) { + let world = living.entity.world.load(); + if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) + && let Some(player) = entity.get_player() + && !player.is_spectator() + { + let raid_pos = player + .get_raid_omen_position() + .unwrap_or_else(|| living.entity.block_pos.load()); + let mut raids = world + .raids + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + raids.create_or_extend_raid(player, raid_pos, &world); + player.clear_raid_omen_position(); + } } } diff --git a/crates/pumpkin/src/entity/effect/regeneration.rs b/crates/pumpkin/src/entity/effect/regeneration.rs index b20903704..55f7d75cc 100644 --- a/crates/pumpkin/src/entity/effect/regeneration.rs +++ b/crates/pumpkin/src/entity/effect/regeneration.rs @@ -1,4 +1,4 @@ -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct RegenerationMobEffect; @@ -16,17 +16,11 @@ impl MobEffect for RegenerationMobEffect { } } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let current_health = living.health.load(); - let max_health = living.get_max_health(); - if current_health < max_health && current_health > 0.0 { - living.heal(1.0); - } - }) + fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) { + let current_health = living.health.load(); + let max_health = living.get_max_health(); + if current_health < max_health && current_health > 0.0 { + living.heal(1.0); + } } } diff --git a/crates/pumpkin/src/entity/effect/saturation.rs b/crates/pumpkin/src/entity/effect/saturation.rs index ca879e80b..a077bb7ac 100644 --- a/crates/pumpkin/src/entity/effect/saturation.rs +++ b/crates/pumpkin/src/entity/effect/saturation.rs @@ -1,4 +1,4 @@ -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct SaturationMobEffect; @@ -8,22 +8,16 @@ impl MobEffect for SaturationMobEffect { true } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let world = living.entity.world.load(); - if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) - && let Some(player) = entity.get_player() - { - let hunger = amplifier + 1; - player.hunger_manager.add_hunger(hunger); - player - .hunger_manager - .add_saturation(f32::from(hunger) * 2.0); - } - }) + fn apply_effect_tick(&self, living: &LivingEntity, amplifier: u8) { + let world = living.entity.world.load(); + if let Some(entity) = world.get_entity_by_id(living.entity.entity_id) + && let Some(player) = entity.get_player() + { + let hunger = amplifier + 1; + player.hunger_manager.add_hunger(hunger); + player + .hunger_manager + .add_saturation(f32::from(hunger) * 2.0); + } } } diff --git a/crates/pumpkin/src/entity/effect/weaving.rs b/crates/pumpkin/src/entity/effect/weaving.rs index 771b80777..2ae803a5f 100644 --- a/crates/pumpkin/src/entity/effect/weaving.rs +++ b/crates/pumpkin/src/entity/effect/weaving.rs @@ -7,61 +7,54 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct WeavingMobEffect; impl MobEffect for WeavingMobEffect { - fn on_mob_death<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - _damage_type: &'a DamageType, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let world = living.entity.world.load(); + fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) { + let world = living.entity.world.load(); - // Check if mob griefing is enabled or if player - let mob_griefing = world.level_info.load().game_rules.mob_griefing; - if !living.is_player() && !mob_griefing { - return; + // Check if mob griefing is enabled or if player + let mob_griefing = world.level_info.load().game_rules.mob_griefing; + if !living.is_player() && !mob_griefing { + return; + } + + let center_pos = living.entity.block_pos.load(); + let cobweb_count = (rand::random::() % 2 + 2) as usize; // 2 to 3 cobwebs + + let mut positions_to_transform = HashSet::new(); + + // Sample up to 15 random positions in a cube of radius 1 + for _ in 0..15 { + let dx = (rand::random::() % 3) as i32 - 1; + let dy = (rand::random::() % 3) as i32 - 1; + let dz = (rand::random::() % 3) as i32 - 1; + + let target_pos = BlockPos(center_pos.0 + Vector3::new(dx, dy, dz)); + let below_pos = BlockPos(target_pos.0 + Vector3::new(0, -1, 0)); + + let target_state = world.get_block_state(&target_pos); + let below_state = world.get_block_state(&below_pos); + + if target_state.is_air() + && !below_state.is_air() + && positions_to_transform.insert(target_pos) + && positions_to_transform.len() >= cobweb_count + { + break; } + } - let center_pos = living.entity.block_pos.load(); - let cobweb_count = (rand::random::() % 2 + 2) as usize; // 2 to 3 cobwebs - - let mut positions_to_transform = HashSet::new(); - - // Sample up to 15 random positions in a cube of radius 1 - for _ in 0..15 { - let dx = (rand::random::() % 3) as i32 - 1; - let dy = (rand::random::() % 3) as i32 - 1; - let dz = (rand::random::() % 3) as i32 - 1; - - let target_pos = BlockPos(center_pos.0 + Vector3::new(dx, dy, dz)); - let below_pos = BlockPos(target_pos.0 + Vector3::new(0, -1, 0)); - - let target_state = world.get_block_state(&target_pos); - let below_state = world.get_block_state(&below_pos); - - if target_state.is_air() - && !below_state.is_air() - && positions_to_transform.insert(target_pos) - && positions_to_transform.len() >= cobweb_count - { - break; - } - } - - for target_pos in positions_to_transform { - world.set_block_state( - &target_pos, - Block::COBWEB.default_state.id, - BlockFlags::NOTIFY_ALL, - ); - world.sync_world_event(WorldEvent::AnimationSpawnCobweb, target_pos, 0); - } - }) + for target_pos in positions_to_transform { + world.set_block_state( + &target_pos, + Block::COBWEB.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + world.sync_world_event(WorldEvent::AnimationSpawnCobweb, target_pos, 0); + } } } diff --git a/crates/pumpkin/src/entity/effect/wind_charged.rs b/crates/pumpkin/src/entity/effect/wind_charged.rs index 2e3a9e224..53e9fc43b 100644 --- a/crates/pumpkin/src/entity/effect/wind_charged.rs +++ b/crates/pumpkin/src/entity/effect/wind_charged.rs @@ -2,7 +2,7 @@ use pumpkin_data::damage::DamageType; use pumpkin_data::sound::{Sound, SoundCategory}; 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::projectile::wind_charge::BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR; use crate::world::explosion::ExplosionInteraction; @@ -10,35 +10,26 @@ use crate::world::explosion::ExplosionInteraction; pub struct WindChargedMobEffect; impl MobEffect for WindChargedMobEffect { - fn on_mob_death<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - _damage_type: &'a DamageType, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let world = living.entity.world.load(); - let pos = living.entity.pos.load(); - let height = living.entity.height(); - let center = Vector3::new(pos.x, pos.y + f64::from(height) / 2.0, pos.z); + fn on_mob_death(&self, living: &LivingEntity, _amplifier: u8, _damage_type: &DamageType) { + let world = living.entity.world.load(); + let pos = living.entity.pos.load(); + let height = living.entity.height(); + let center = Vector3::new(pos.x, pos.y + f64::from(height) / 2.0, pos.z); - // gustStrength = 3.0 + random * 2.0 - let gust_strength = 3.0 + rand::random::() * 2.0; + // gustStrength = 3.0 + random * 2.0 + let gust_strength = 3.0 + rand::random::() * 2.0; - world - .explode_with_calculator( - center, - gust_strength, - ExplosionInteraction::Trigger, - Some(BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone()), - ) - .await; + world.explode_with_calculator( + center, + gust_strength, + ExplosionInteraction::Trigger, + Some(BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone()), + ); - world.play_sound( - Sound::EntityBreezeWindBurst, - SoundCategory::Neutral, - ¢er, - ); - }) + world.play_sound( + Sound::EntityBreezeWindBurst, + SoundCategory::Hostile, + ¢er, + ); } } diff --git a/crates/pumpkin/src/entity/effect/wither.rs b/crates/pumpkin/src/entity/effect/wither.rs index de5d163e7..c58eda506 100644 --- a/crates/pumpkin/src/entity/effect/wither.rs +++ b/crates/pumpkin/src/entity/effect/wither.rs @@ -1,6 +1,6 @@ use pumpkin_data::damage::DamageType; -use crate::entity::effect::{EffectFuture, MobEffect}; +use crate::entity::effect::MobEffect; use crate::entity::living::LivingEntity; pub struct WitherMobEffect; @@ -18,20 +18,14 @@ impl MobEffect for WitherMobEffect { } } - fn apply_effect_tick<'a>( - &'a self, - living: &'a LivingEntity, - _amplifier: u8, - ) -> EffectFuture<'a, ()> { - Box::pin(async move { - let dyn_self = living - .entity - .world - .load() - .get_entity_by_id(living.entity.entity_id); - if let Some(dyn_self) = dyn_self { - dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER); - } - }) + fn apply_effect_tick(&self, living: &LivingEntity, _amplifier: u8) { + let dyn_self = living + .entity + .world + .load() + .get_entity_by_id(living.entity.entity_id); + if let Some(dyn_self) = dyn_self { + dyn_self.damage(&*dyn_self, 1.0, DamageType::WITHER); + } } } diff --git a/crates/pumpkin/src/entity/experience_orb.rs b/crates/pumpkin/src/entity/experience_orb.rs index c04a72c74..e8a3a4828 100644 --- a/crates/pumpkin/src/entity/experience_orb.rs +++ b/crates/pumpkin/src/entity/experience_orb.rs @@ -115,14 +115,11 @@ impl EntityBase for ExperienceOrbEntity { if can_pickup { player.living_entity.pickup(&self.entity, 1); self.entity.remove(); - let player_clone = player.clone(); let amount = self.amount as i32; - tokio::spawn(async move { - let remaining = player_clone.apply_mending_from_xp(amount).await; - if remaining > 0 { - player_clone.add_experience_points(remaining).await; - } - }); + let remaining = player.apply_mending_from_xp(amount); + if remaining > 0 { + player.add_experience_points(remaining); + } } } } diff --git a/crates/pumpkin/src/entity/hunger.rs b/crates/pumpkin/src/entity/hunger.rs index 68d1d6e94..061696b03 100644 --- a/crates/pumpkin/src/entity/hunger.rs +++ b/crates/pumpkin/src/entity/hunger.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use super::{NBTStorage, NBTStorageInit, player::Player}; -use crate::entity::NbtFuture; use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; @@ -176,26 +175,22 @@ impl HungerManager { } impl NBTStorage for HungerManager { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_int("foodLevel", self.level.load().into()); - nbt.put_float("foodSaturationLevel", self.saturation.load()); - nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); - nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); - }) + fn write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("foodLevel", self.level.load().into()); + nbt.put_float("foodSaturationLevel", self.saturation.load()); + nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); + nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); } - fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.level - .store(nbt.get_int("foodLevel").unwrap_or(20) as u8); - self.saturation - .store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0)); - self.exhaustion - .store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0)); - self.tick_timer - .store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32); - }) + fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + self.level + .store(nbt.get_int("foodLevel").unwrap_or(20) as u8); + self.saturation + .store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0)); + self.exhaustion + .store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0)); + self.tick_timer + .store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32); } } diff --git a/crates/pumpkin/src/entity/interaction.rs b/crates/pumpkin/src/entity/interaction.rs index 9962e877f..74fa7d3e7 100644 --- a/crates/pumpkin/src/entity/interaction.rs +++ b/crates/pumpkin/src/entity/interaction.rs @@ -13,9 +13,7 @@ use pumpkin_util::math::{ }; use crate::{ - entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity, player::Player, - }, + entity::{Entity, EntityBase, living::LivingEntity, player::Player}, server::Server, }; @@ -161,85 +159,81 @@ impl InteractionEntity { } impl EntityBase for InteractionEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_float( - "width", - *self - .width - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner), - ); - nbt.put_float( - "height", - *self - .height - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner), - ); - nbt.put_bool("response", self.response.load(Ordering::Relaxed)); - - let attack = *self - .attack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(attack) = attack { - nbt.put("attack", NbtTag::Compound(attack.to_nbt())); - } - - let interaction = *self - .interaction - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(interaction) = interaction { - nbt.put("interaction", NbtTag::Compound(interaction.to_nbt())); - } - }) - } - - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let width = nbt.get_float("width").unwrap_or(1.0); - let height = nbt.get_float("height").unwrap_or(1.0); - let response = nbt.get_bool("response").unwrap_or(false); - + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_float( + "width", *self .width .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = width; + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_float( + "height", *self .height .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = height; - self.response.store(response, Ordering::Relaxed); - self.update_dimensions(); + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + nbt.put_bool("response", self.response.load(Ordering::Relaxed)); - if let Some(attack_compound) = nbt.get_compound("attack") { - *self - .attack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - PlayerAction::from_nbt(attack_compound); - } else { - *self - .attack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - } + let attack = *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(attack) = attack { + nbt.put("attack", NbtTag::Compound(attack.to_nbt())); + } - if let Some(interaction_compound) = nbt.get_compound("interaction") { - *self - .interaction - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - PlayerAction::from_nbt(interaction_compound); - } else { - *self - .interaction - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - } - }) + let interaction = *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(interaction) = interaction { + nbt.put("interaction", NbtTag::Compound(interaction.to_nbt())); + } + } + + fn read_custom_nbt(&self, nbt: &NbtCompound) { + let width = nbt.get_float("width").unwrap_or(1.0); + let height = nbt.get_float("height").unwrap_or(1.0); + let response = nbt.get_bool("response").unwrap_or(false); + + *self + .width + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = width; + *self + .height + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = height; + self.response.store(response, Ordering::Relaxed); + self.update_dimensions(); + + if let Some(attack_compound) = nbt.get_compound("attack") { + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + PlayerAction::from_nbt(attack_compound); + } else { + *self + .attack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } + + if let Some(interaction_compound) = nbt.get_compound("interaction") { + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + PlayerAction::from_nbt(interaction_compound); + } else { + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} @@ -331,21 +325,15 @@ impl EntityBase for InteractionEntity { false } - fn interact<'a>( - &'a self, - player: &'a Arc, - _item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let timestamp = self.entity.world.load().get_world_age(); - *self - .interaction - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(PlayerAction { - player: player.gameprofile.id, - timestamp, - }); - true - }) + fn interact(&self, player: &Arc, _item_stack: &mut ItemStack) -> bool { + let timestamp = self.entity.world.load().get_world_age(); + *self + .interaction + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(PlayerAction { + player: player.gameprofile.id, + timestamp, + }); + true } } diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index d6d239bd1..9a8fcc624 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -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 { entity: Entity, @@ -381,23 +381,18 @@ impl ItemEntity { if age >= 6000 { let entity_id = entity.entity_id; let world = entity.world.load_full(); - tokio::spawn(async move { - let mut despawn_event = - crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new( - entity_id, - ); - if let Some(server) = world.server.upgrade() { - server - .plugin_manager - .fire(&server, &mut despawn_event) - .await; - } - if !despawn_event.cancelled - && let Some(e) = world.get_entity_by_id(entity_id) - { - e.get_entity().remove(); - } - }); + let mut despawn_event = + crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new(entity_id); + if let Some(server) = world.server.upgrade() { + server + .plugin_manager + .fire_blocking(&server, &mut despawn_event); + } + if !despawn_event.cancelled + && let Some(e) = world.get_entity_by_id(entity_id) + { + e.get_entity().remove(); + } return false; } @@ -608,51 +603,47 @@ impl EntityBase for ItemEntity { 0.04 } - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let item = self - .item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut item_compound = NbtCompound::new(); - item.write_item_stack(&mut item_compound); - nbt.put_compound("Item", item_compound); + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + let item = self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut item_compound = NbtCompound::new(); + item.write_item_stack(&mut item_compound); + nbt.put_compound("Item", item_compound); - nbt.put_short("Age", self.item_age.load(Ordering::Relaxed) as i16); - nbt.put_short( - "PickupDelay", - self.pickup_delay.load(Ordering::Relaxed) as i16, - ); - nbt.put_short("Health", self.health.load(Relaxed) as i16); - }) + nbt.put_short("Age", self.item_age.load(Ordering::Relaxed) as i16); + nbt.put_short( + "PickupDelay", + self.pickup_delay.load(Ordering::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, ()> { - Box::pin(async { - // Restore the item stack from the "Item" compound - if let Some(item_compound) = nbt.get_compound("Item") - && let Some(stack) = ItemStack::read_item_stack(item_compound) - { - *self - .item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; - } + fn read_custom_nbt(&self, nbt: &NbtCompound) { + // Restore the item stack from the "Item" compound + if let Some(item_compound) = nbt.get_compound("Item") + && let Some(stack) = ItemStack::read_item_stack(item_compound) + { + *self + .item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; + } - // Vanilla stores Age as a short - self.item_age - .store(nbt.get_short("Age").unwrap_or(0) as u32, Ordering::Relaxed); + // Vanilla stores Age as a short + self.item_age + .store(nbt.get_short("Age").unwrap_or(0) as u32, Ordering::Relaxed); - // Vanilla stores PickupDelay as a short - if let Some(delay) = nbt.get_short("PickupDelay") { - self.pickup_delay.store(delay as u8, Ordering::Relaxed); - } + // Vanilla stores PickupDelay as a short + if let Some(delay) = nbt.get_short("PickupDelay") { + self.pickup_delay.store(delay as u8, Ordering::Relaxed); + } - // Vanilla stores Health as a short - if let Some(health) = nbt.get_short("Health") { - self.health.store(health as f32, Relaxed); - } - }) + // Vanilla stores Health as a short + if let Some(health) = nbt.get_short("Health") { + self.health.store(health as f32, Relaxed); + } } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/lightning.rs b/crates/pumpkin/src/entity/lightning.rs index c851198e4..4ad752d81 100644 --- a/crates/pumpkin/src/entity/lightning.rs +++ b/crates/pumpkin/src/entity/lightning.rs @@ -192,7 +192,7 @@ impl LightningBoltEntity { } impl EntityBase for LightningBoltEntity { - fn tick(&self, caller: &Arc, _server: &Server) { + fn tick(&self, _caller: &Arc, _server: &Server) { let entity = &self.entity; let life = self.life.load(Ordering::Relaxed); @@ -261,14 +261,7 @@ impl EntityBase for LightningBoltEntity { } let hit_id = hit_entity.get_entity().entity_id; if hit_guard.insert(hit_id) { - let caller_clone = caller.clone(); - let target = hit_entity.clone(); - tokio::spawn(async move { - if let Some(lightning) = caller_clone.cast_any().downcast_ref::() - { - target.on_lightning_strike(target.as_ref(), lightning).await; - } - }); + hit_entity.on_lightning_strike(hit_entity.as_ref(), self); } } } diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index 92f1973bb..44ed7407f 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -24,6 +24,7 @@ use tracing::warn; use super::experience_orb::ExperienceOrbEntity; use super::{Entity, EntityBase, NBTStorageInit}; use crate::block::OnLandedUponArgs; +use crate::entity::NBTStorage; use crate::entity::attributes::AttributeInstance; use crate::entity::attributes::Modifier; 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::slime::SlimeEntity; use crate::entity::player::statistics::{CustomStatistic, StatisticCategory}; -use crate::entity::{NBTStorage, NbtFuture}; use crate::server::Server; use crate::world::loot::{LootContextParameters, LootTableExt}; use crossbeam::atomic::AtomicCell; @@ -1443,9 +1443,13 @@ impl LivingEntity { fall_distance: f32, damage_per_distance: f32, ) { - let may_fly = caller - .get_player() - .is_some_and(|player| player.abilities.blocking_lock().allow_flying); + let may_fly = caller.get_player().is_some_and(|player| { + player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .allow_flying + }); if may_fly || self.is_immune_to_fall_damage() { return; } @@ -1472,7 +1476,7 @@ impl LivingEntity { } } - pub async fn get_death_message( + pub fn get_death_message( dyn_self: &dyn EntityBase, damage_type: DamageType, source: Option<&dyn EntityBase>, @@ -1486,16 +1490,13 @@ impl LivingEntity { TextComponent::translate_cross( format!("death.attack.{}.player", damage_type.message_id), format!("death.attack.{}.player", damage_type.message_id), - [ - dyn_self.get_display_name().await, - cause.get_display_name().await, - ], + [dyn_self.get_display_name(), cause.get_display_name()], ) } else { TextComponent::translate_cross( 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( translation::java::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("[") .add_child(TextComponent::translate_cross( 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("]")), } @@ -1618,7 +1619,7 @@ impl LivingEntity { }; for (effect_type, amplifier) in active_effects_vec { if let Some(mob_effect) = crate::entity::effect::get_mob_effect(effect_type) { - mob_effect.on_mob_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 }; if self.entity.entity_type == &EntityType::PLAYER && show_death_messages { //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() { for player in server.get_all_players() { player.send_system_message(&death_message).await; @@ -1816,15 +1817,7 @@ impl LivingEntity { } for (mob_effect, amplifier) in effects_to_apply { - let entity_id = self.entity.entity_id; - let world = self.entity.world.load_full(); - tokio::spawn(async move { - if let Some(entity) = world.get_entity_by_id(entity_id) - && let Some(living) = entity.get_living_entity() - { - mob_effect.apply_effect_tick(living, amplifier).await; - } - }); + mob_effect.apply_effect_tick(self, amplifier); } } @@ -2093,103 +2086,97 @@ impl LivingEntity { } impl LivingEntity { - pub fn write_living_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put("Health", NbtTag::Float(self.health.load())); - // Avoid persisting a lethal fall distance when the entity is dead to prevent death loops - let fall_distance = if self.dead.load(Relaxed) { - 0.0 - } else { - self.fall_distance.load() + pub fn write_living_nbt(&self, nbt: &mut NbtCompound) { + nbt.put("Health", NbtTag::Float(self.health.load())); + // Avoid persisting a lethal fall distance when the entity is dead to prevent death loops + let fall_distance = if self.dead.load(Relaxed) { + 0.0 + } else { + self.fall_distance.load() + }; + // Persist current absorption amount + nbt.put("AbsorptionAmount", NbtTag::Float(self.absorption.load())); + nbt.put("FallDistance", NbtTag::Float(fall_distance)); + nbt.put_short("HurtTime", self.hurt_cooldown.load(Relaxed).max(0) as i16); + nbt.put_short("DeathTime", i16::from(self.death_time.load(Relaxed))); + nbt.put_bool("FallFlying", self.entity.is_fall_flying()); + { + let effects_vec: Vec = { + let effects = self + .active_effects + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + effects.values().cloned().collect() }; - // Persist current absorption amount - nbt.put("AbsorptionAmount", NbtTag::Float(self.absorption.load())); - nbt.put("FallDistance", NbtTag::Float(fall_distance)); - nbt.put_short("HurtTime", self.hurt_cooldown.load(Relaxed).max(0) as i16); - nbt.put_short("DeathTime", i16::from(self.death_time.load(Relaxed))); - nbt.put_bool("FallFlying", self.entity.is_fall_flying()); - { - let effects_vec: Vec = { - let effects = self + if !effects_vec.is_empty() { + // Iterate effects and create Box<[NbtTag]> + let mut effects_list = Vec::with_capacity(effects_vec.len()); + for effect in effects_vec { + let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); + effect.write_nbt(&mut effect_nbt); + effects_list.push(NbtTag::Compound(effect_nbt)); + } + nbt.put("active_effects", NbtTag::List(effects_list)); + } + } + //TODO: write equipment + // todo more... + } + + pub fn read_living_nbt_non_mut(&self, nbt: &NbtCompound) { + self.health.store(nbt.get_float("Health").unwrap_or(20.0)); + + // Clamp any persisted absorption to the entity's configured max + let raw_abs = nbt.get_float("AbsorptionAmount").unwrap_or(0.0); + let max_abs = self.get_attribute_value(&Attributes::MAX_ABSORPTION) as f32; + let clamped_abs = raw_abs.max(0.0).min(max_abs); + self.absorption.store(clamped_abs); + + // Load fall distance, but if this entity is currently marked dead ensure we don't restore + // a lethal fall distance that would immediately re-kill on spawn. + let fd = nbt + .get_float("FallDistance") + .or_else(|| nbt.get_float("fall_distance")) + .unwrap_or(0.0); + if self.dead.load(Relaxed) { + self.fall_distance.store(0.0); + } else { + self.fall_distance.store(fd); + } + if let Some(hurt_time) = nbt.get_short("HurtTime") { + self.hurt_cooldown.store(i32::from(hurt_time), Relaxed); + } + if let Some(death_time) = nbt.get_short("DeathTime") { + self.death_time.store(death_time as u8, Relaxed); + } + self.entity + .fall_flying + .store(nbt.get_bool("FallFlying").unwrap_or(false), Relaxed); + { + let nbt_effects = nbt.get_list("active_effects"); + if let Some(nbt_effects) = nbt_effects { + let mut read_effects = Vec::new(); + for effect in nbt_effects { + if let NbtTag::Compound(effect_nbt) = effect { + if let Some(mut effect) = Effect::create_from_nbt(&mut effect_nbt.clone()) { + effect.blend = true; // TODO: change, is taken from effect give command + read_effects.push(effect); + } else { + warn!("Unable to read effect from nbt"); + } + } + } + if !read_effects.is_empty() { + let mut active_effects = self .active_effects .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - effects.values().cloned().collect() - }; - if !effects_vec.is_empty() { - // Iterate effects and create Box<[NbtTag]> - let mut effects_list = Vec::with_capacity(effects_vec.len()); - for effect in effects_vec { - let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); - effect.write_nbt(&mut effect_nbt).await; - effects_list.push(NbtTag::Compound(effect_nbt)); - } - nbt.put("active_effects", NbtTag::List(effects_list)); - } - } - //TODO: write equipment - // todo more... - }) - } - - pub fn read_living_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - self.health.store(nbt.get_float("Health").unwrap_or(20.0)); - - // Clamp any persisted absorption to the entity's configured max - let raw_abs = nbt.get_float("AbsorptionAmount").unwrap_or(0.0); - let max_abs = self.get_attribute_value(&Attributes::MAX_ABSORPTION) as f32; - let clamped_abs = raw_abs.max(0.0).min(max_abs); - self.absorption.store(clamped_abs); - - // Load fall distance, but if this entity is currently marked dead ensure we don't restore - // a lethal fall distance that would immediately re-kill on spawn. - let fd = nbt - .get_float("FallDistance") - .or_else(|| nbt.get_float("fall_distance")) - .unwrap_or(0.0); - if self.dead.load(Relaxed) { - self.fall_distance.store(0.0); - } else { - self.fall_distance.store(fd); - } - if let Some(hurt_time) = nbt.get_short("HurtTime") { - self.hurt_cooldown.store(i32::from(hurt_time), Relaxed); - } - if let Some(death_time) = nbt.get_short("DeathTime") { - self.death_time.store(death_time as u8, Relaxed); - } - self.entity - .fall_flying - .store(nbt.get_bool("FallFlying").unwrap_or(false), Relaxed); - { - let nbt_effects = nbt.get_list("active_effects"); - if let Some(nbt_effects) = nbt_effects { - let mut read_effects = Vec::new(); - for effect in nbt_effects { - if let NbtTag::Compound(effect_nbt) = effect { - if let Some(mut effect) = - Effect::create_from_nbt(&mut effect_nbt.clone()).await - { - effect.blend = true; // TODO: change, is taken from effect give command - read_effects.push(effect); - } else { - warn!("Unable to read effect from nbt"); - } - } - } - if !read_effects.is_empty() { - let mut active_effects = self - .active_effects - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - for effect in read_effects { - active_effects.insert(effect.effect_type, effect); - } + for effect in read_effects { + active_effects.insert(effect.effect_type, effect); } } } - }) + } // todo more... } @@ -2828,9 +2815,7 @@ impl EntityBase for LivingEntity { .cooldown_group .clone() .unwrap_or_else(|| item.item.registry_key.to_string()); - player - .start_cooldown(group, (cooldown.seconds * 20.0) as i32) - .await; + player.start_cooldown(group, (cooldown.seconds * 20.0) as i32); } } @@ -2932,7 +2917,12 @@ impl LivingEntity { } ConsumeEffect::TeleportRandomly(diameter) => { // 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 { vehicle .get_entity() @@ -2959,7 +2949,7 @@ impl LivingEntity { self.fall_distance.store(0.0); // Vanilla broadcasts entity event 46 (teleport particles) on success. 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( Sound::ItemChorusFruitTeleport, SoundCategory::Players, diff --git a/crates/pumpkin/src/entity/marker.rs b/crates/pumpkin/src/entity/marker.rs index 3fe1dbc8f..59283c9b2 100644 --- a/crates/pumpkin/src/entity/marker.rs +++ b/crates/pumpkin/src/entity/marker.rs @@ -1,8 +1,7 @@ -use std::sync::{Arc, atomic::Ordering}; -use tokio::sync::Mutex; +use std::sync::{Arc, Mutex, atomic::Ordering}; use crate::{ - entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity}, + entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}, net::{bedrock::BedrockClient, java::JavaClient}, server::Server, }; @@ -26,21 +25,23 @@ impl MarkerEntity { } impl EntityBase for MarkerEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let data = self.data.lock().await; - if !data.is_empty() { - nbt.put("data", NbtTag::Compound(data.clone())); - } - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + let data = self + .data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !data.is_empty() { + nbt.put("data", NbtTag::Compound(data.clone())); + } } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(data) = nbt.get_compound("data") { - *self.data.lock().await = data.clone(); - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + if let Some(data) = nbt.get_compound("data") { + *self + .data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = data.clone(); + } } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) {} diff --git a/crates/pumpkin/src/entity/mob/bat.rs b/crates/pumpkin/src/entity/mob/bat.rs index adb79cb7f..960d3c44c 100644 --- a/crates/pumpkin/src/entity/mob/bat.rs +++ b/crates/pumpkin/src/entity/mob/bat.rs @@ -14,7 +14,7 @@ use pumpkin_world::chunk::ChunkHeightmapType; use rand::RngExt; use crate::entity::mob::{Mob, MobEntity}; -use crate::entity::{Entity, EntityBase, NbtFuture}; +use crate::entity::{Entity, EntityBase}; use crate::world::World; 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, ()> { - Box::pin(async move { - let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; - nbt.put_byte("BatFlags", i8::try_from(flags).unwrap_or(0)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; + nbt.put_byte("BatFlags", i8::try_from(flags).unwrap_or(0)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let flags = u8::try_from(nbt.get_byte("BatFlags").unwrap_or(0)).unwrap_or(0); - let roosting = (flags & ROOSTING_FLAG) != 0; - self.set_roosting(roosting); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + let flags = u8::try_from(nbt.get_byte("BatFlags").unwrap_or(0)).unwrap_or(0); + let roosting = (flags & ROOSTING_FLAG) != 0; + self.set_roosting(roosting); } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/mob/creaking.rs b/crates/pumpkin/src/entity/mob/creaking.rs index 1e9cf382c..35444824b 100644 --- a/crates/pumpkin/src/entity/mob/creaking.rs +++ b/crates/pumpkin/src/entity/mob/creaking.rs @@ -18,7 +18,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::block::entities::creaking_heart::CreakingHeartBlockEntity; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal, @@ -397,28 +397,24 @@ impl CreakingEntity { } impl Mob for CreakingEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(pos) = self.get_home_pos() { - let mut sub = NbtCompound::new(); - sub.put_int("x", pos.0.x); - sub.put_int("y", pos.0.y); - sub.put_int("z", pos.0.z); - nbt.put_compound("home_pos", sub); - } - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + if let Some(pos) = self.get_home_pos() { + let mut sub = NbtCompound::new(); + sub.put_int("x", pos.0.x); + sub.put_int("y", pos.0.y); + sub.put_int("z", pos.0.z); + nbt.put_compound("home_pos", sub); + } } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(sub) = nbt.get_compound("home_pos") - && let (Some(x), Some(y), Some(z)) = - (sub.get_int("x"), sub.get_int("y"), sub.get_int("z")) - { - let pos = BlockPos::new(x, y, z); - self.set_transient(pos); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(sub) = nbt.get_compound("home_pos") + && let (Some(x), Some(y), Some(z)) = + (sub.get_int("x"), sub.get_int("y"), sub.get_int("z")) + { + let pos = BlockPos::new(x, y, z); + self.set_transient(pos); + } } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/mob/creeper.rs b/crates/pumpkin/src/entity/mob/creeper.rs index 80795334a..752dae5d5 100644 --- a/crates/pumpkin/src/entity/mob/creeper.rs +++ b/crates/pumpkin/src/entity/mob/creeper.rs @@ -13,7 +13,7 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, creeper_ignite::CreeperIgniteGoal, 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 radius = self.explosion_radius.load(Ordering::Relaxed) as f32; let multiplier = if self.charged.load(Ordering::Relaxed) { @@ -115,75 +115,66 @@ impl CreeperEntity { .store(true, Ordering::Relaxed); let world = entity.world.load(); let pos = entity.pos.load(); - world - .explode( - pos, - radius * multiplier, - crate::world::ExplosionInteraction::Mob, - ) - .await; + world.explode( + pos, + radius * multiplier, + crate::world::ExplosionInteraction::Mob, + ); // TODO: spawn area effect cloud with potion effects entity.remove(); } } impl Mob for CreeperEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_bool("powered", self.charged.load(Ordering::Relaxed)); - nbt.put_short("Fuse", self.fuse_time.load(Ordering::Relaxed) as i16); - nbt.put_byte( - "ExplosionRadius", - self.explosion_radius.load(Ordering::Relaxed) as i8, - ); - nbt.put_bool("ignited", self.ignited.load(Ordering::Relaxed)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("powered", self.charged.load(Ordering::Relaxed)); + nbt.put_short("Fuse", self.fuse_time.load(Ordering::Relaxed) as i16); + nbt.put_byte( + "ExplosionRadius", + self.explosion_radius.load(Ordering::Relaxed) as i8, + ); + nbt.put_bool("ignited", self.ignited.load(Ordering::Relaxed)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(powered) = nbt.get_bool("powered") { - self.charged.store(powered, Ordering::Relaxed); - } - if let Some(fuse) = nbt.get_short("Fuse") { - self.fuse_time.store(i32::from(fuse), Ordering::Relaxed); - } - if let Some(radius) = nbt.get_byte("ExplosionRadius") { - self.explosion_radius - .store(i32::from(radius), Ordering::Relaxed); - } - if let Some(ignited) = nbt.get_bool("ignited") { - self.ignited.store(ignited, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(powered) = nbt.get_bool("powered") { + self.charged.store(powered, Ordering::Relaxed); + } + if let Some(fuse) = nbt.get_short("Fuse") { + self.fuse_time.store(i32::from(fuse), Ordering::Relaxed); + } + if let Some(radius) = nbt.get_byte("ExplosionRadius") { + self.explosion_radius + .store(i32::from(radius), Ordering::Relaxed); + } + if let Some(ignited) = nbt.get_bool("ignited") { + self.ignited.store(ignited, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { &self.mob_entity } - fn mob_on_lightning_strike<'a>( - &'a self, - caller: &'a dyn EntityBase, - lightning: &'a crate::entity::lightning::LightningBoltEntity, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.charged.store(true, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::CHARGED, - true, - )], - None, - ); - self.mob_entity - .living_entity - .on_lightning_strike(caller, lightning) - .await; - }) + fn mob_on_lightning_strike( + &self, + caller: &dyn EntityBase, + lightning: &crate::entity::lightning::LightningBoltEntity, + ) { + self.charged.store(true, Ordering::Relaxed); + self.mob_entity.living_entity.entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::creeper::CHARGED, + true, + )], + None, + ); + self.mob_entity + .living_entity + .on_lightning_strike(caller, lightning); } - fn mob_tick<'a>(&'a self, caller: &'a Arc) { + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { let entity = &self.mob_entity.living_entity.entity; if !entity.is_alive() { return; @@ -218,52 +209,41 @@ impl Mob for CreeperEntity { if new_fuse >= fuse_time { self.current_fuse_time.store(fuse_time, Ordering::Relaxed); - let caller_clone = caller.clone(); - tokio::spawn(async move { - if let Some(creeper) = caller_clone.cast_any().downcast_ref::() { - creeper.explode().await; - } - }); + self.explode(); } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if item_stack.item.id != Item::FLINT_AND_STEEL.id { - return self.mob_entity.mob_interact(player, item_stack); - } + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if item_stack.item.id != Item::FLINT_AND_STEEL.id { + return self.mob_entity.mob_interact(player, item_stack); + } - let entity = &self.mob_entity.living_entity.entity; - let world = entity.world.load(); - let pos = entity.pos.load(); + let entity = &self.mob_entity.living_entity.entity; + let world = entity.world.load(); + let pos = entity.pos.load(); - world.play_sound_fine( - Sound::ItemFlintandsteelUse, - SoundCategory::Hostile, - &pos, - 1.0, - rand::random::() * 0.4 + 0.8, - ); + world.play_sound_fine( + Sound::ItemFlintandsteelUse, + SoundCategory::Hostile, + &pos, + 1.0, + rand::random::() * 0.4 + 0.8, + ); - self.ignited.store(true, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::IS_IGNITED, - true, - )], - None, - ); + self.ignited.store(true, Ordering::Relaxed); + entity.send_meta_data( + &[Metadata::new( + pumpkin_data::tracked_data::creeper::IS_IGNITED, + true, + )], + None, + ); - if player.gamemode.load() != pumpkin_util::GameMode::Creative { - // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. - let _ = item_stack.damage_item(1); - } + if player.gamemode.load() != pumpkin_util::GameMode::Creative { + // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. + let _ = item_stack.damage_item(1); + } - true - }) + true } } diff --git a/crates/pumpkin/src/entity/mob/enderman.rs b/crates/pumpkin/src/entity/mob/enderman.rs index 76b3006cf..d734bff7c 100644 --- a/crates/pumpkin/src/entity/mob/enderman.rs +++ b/crates/pumpkin/src/entity/mob/enderman.rs @@ -27,7 +27,7 @@ use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3:: use rand::RngExt; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::{ goal::{ active_target::ActiveTargetGoal, chase_player::ChasePlayerGoal, @@ -412,20 +412,16 @@ impl EndermanEntity { } impl Mob for EndermanEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(block_state) = self.carried_block.load() { - nbt.put_int("carriedBlockState", block_state.as_u16() as i32); - } - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + if let Some(block_state) = self.carried_block.load() { + nbt.put_int("carriedBlockState", block_state.as_u16() as i32); + } } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(block_state) = nbt.get_int("carriedBlockState") { - self.set_carried_block(BlockStateId::new(block_state as u16)); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(block_state) = nbt.get_int("carriedBlockState") { + self.set_carried_block(BlockStateId::new(block_state as u16)); + } } 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 - fn mob_tick<'a>(&'a self, _caller: &'a Arc) { + fn mob_tick<'a>(&'a self, caller: &'a Arc) { let entity = &self.mob_entity.living_entity.entity; if !entity.is_alive() { return; @@ -447,13 +443,7 @@ impl Mob for EndermanEntity { let raining_at_feet = world.is_raining_at(&entity.block_pos.load()); let raining_at_head = world.is_raining_at(&entity.bounding_box.load().max_block_pos()); if entity.touching_water.load(Ordering::SeqCst) || raining_at_feet || raining_at_head { - let entity_id = entity.entity_id; - let world_full = entity.world.load_full(); - tokio::spawn(async move { - if let Some(entity) = world_full.get_entity_by_id(entity_id) { - entity.damage(entity.as_ref(), 1.0, DamageType::DROWN); - } - }); + caller.damage(caller.as_ref(), 1.0, DamageType::DROWN); } } diff --git a/crates/pumpkin/src/entity/mob/evoker.rs b/crates/pumpkin/src/entity/mob/evoker.rs index af3bdf6e4..fac168bcf 100644 --- a/crates/pumpkin/src/entity/mob/evoker.rs +++ b/crates/pumpkin/src/entity/mob/evoker.rs @@ -10,7 +10,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{ Controls, Goal, active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, @@ -161,20 +161,16 @@ impl Mob for EvokerEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - nbt.put_int("SpellTicks", self.get_spell_casting_time()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.write_raider_nbt(nbt); + nbt.put_int("SpellTicks", self.get_spell_casting_time()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - if let Some(ticks) = nbt.get_int("SpellTicks") { - self.set_spell_casting_time(ticks); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.read_raider_nbt(nbt); + if let Some(ticks) = nbt.get_int("SpellTicks") { + self.set_spell_casting_time(ticks); + } } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/mob/ghast.rs b/crates/pumpkin/src/entity/mob/ghast.rs index a549ecc46..03c822ac0 100644 --- a/crates/pumpkin/src/entity/mob/ghast.rs +++ b/crates/pumpkin/src/entity/mob/ghast.rs @@ -15,7 +15,7 @@ use crate::entity::ai::goal::active_target::ActiveTargetGoal; use crate::entity::living::LivingEntity; use crate::entity::projectile::fireball::FireballEntity; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{Controls, Goal}, mob::{Mob, MobEntity}, }; @@ -151,21 +151,15 @@ impl Mob for GhastEntity { } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let power = self.get_explosion_power(); - nbt.put_byte("ExplosionPower", i8::try_from(power).unwrap_or(1)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + let power = self.get_explosion_power(); + nbt.put_byte("ExplosionPower", i8::try_from(power).unwrap_or(1)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(power) = nbt.get_byte("ExplosionPower") { - self.set_explosion_power( - u8::try_from(power).unwrap_or(Self::DEFAULT_EXPLOSION_POWER), - ); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(power) = nbt.get_byte("ExplosionPower") { + self.set_explosion_power(u8::try_from(power).unwrap_or(Self::DEFAULT_EXPLOSION_POWER)); + } } fn modify_incoming_damage(&self, amount: f32, damage_type: DamageType) -> f32 { diff --git a/crates/pumpkin/src/entity/mob/illusioner.rs b/crates/pumpkin/src/entity/mob/illusioner.rs index a540d49ed..9adb0470a 100644 --- a/crates/pumpkin/src/entity/mob/illusioner.rs +++ b/crates/pumpkin/src/entity/mob/illusioner.rs @@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound; use pumpkin_nbt::compound::NbtCompound; use crate::entity::{ - Entity, NbtFuture, + Entity, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, @@ -96,16 +96,12 @@ impl Mob for IllusionerEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.write_raider_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.read_raider_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/mod.rs b/crates/pumpkin/src/entity/mob/mod.rs index 9bd61eaaa..9a559c939 100644 --- a/crates/pumpkin/src/entity/mob/mod.rs +++ b/crates/pumpkin/src/entity/mob/mod.rs @@ -1,5 +1,4 @@ -use super::{Entity, EntityBase, NbtFuture, ai::pathfinder::Navigator, living::LivingEntity}; -use crate::entity::EntityBaseFuture; +use super::{Entity, EntityBase, ai::pathfinder::Navigator, living::LivingEntity}; use crate::entity::ai::control::MoveControlTrait; use crate::entity::ai::control::look_control::LookControl; 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. - fn mob_java_spawn_metadata( - &self, - _version: JavaMinecraftVersion, - ) -> EntityBaseFuture<'_, Option>> { - Box::pin(async { None }) + fn mob_java_spawn_metadata(&self, _version: JavaMinecraftVersion) -> Option> { + None } /// Metadata which must accompany this mob whenever it is spawned for a Bedrock client. fn mob_bedrock_spawn_metadata( &self, - ) -> EntityBaseFuture< - '_, - Option, - > { - Box::pin(async { None }) + ) -> Option { + None } fn get_job_site(&self) -> Option { @@ -699,115 +692,105 @@ pub trait Mob: EntityBase + Send + Sync { None } - fn populate_default_equipment_slots<'a>( - &'a self, - _world: &'a Arc, - difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if rand::random::() - < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier - { - let mut armor_type = rand::random_range(0..3); - for _ in 1..=3 { - if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { - armor_type += 1; - } - } - - let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { - 0.1f32 - } else { - 0.25f32 - }; - - let living = &self.get_mob_entity().living_entity; - let mut equipment = living - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut first = true; - - for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { - let current = equipment.get(slot); - if !first && rand::random::() < partial_chance { - break; - } - first = false; - if current.is_empty() - && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) - { - equipment.put(slot, ItemStack::new(1, item)); - } + fn populate_default_equipment_slots( + &self, + _world: &Arc, + difficulty: &crate::entity::mob::equipment::RegionalDifficulty, + ) { + if rand::random::() + < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier + { + let mut armor_type = rand::random_range(0..3); + for _ in 1..=3 { + if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { + armor_type += 1; } } - }) - } - fn populate_default_equipment_enchantments<'a>( - &'a self, - difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.enchant_spawned_weapon(difficulty).await; - for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { - self.enchant_spawned_armor(slot, difficulty).await; - } - }) - } + let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { + 0.1f32 + } else { + 0.25f32 + }; - fn enchant_spawned_weapon<'a>( - &'a self, - difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - self.enchant_spawned_equipment( - &EquipmentSlot::MAIN_HAND, - MobEntity::MAX_ENCHANTED_WEAPON_CHANCE, - difficulty, - ) - } - - fn enchant_spawned_armor<'a>( - &'a self, - slot: &'a EquipmentSlot, - difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - self.enchant_spawned_equipment(slot, MobEntity::MAX_ENCHANTED_ARMOR_CHANCE, difficulty) - } - - fn enchant_spawned_equipment<'a>( - &'a self, - slot: &'a EquipmentSlot, - chance: f32, - difficulty: &'a crate::entity::mob::equipment::RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { let living = &self.get_mob_entity().living_entity; let mut equipment = living .entity_equipment .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(stack) = equipment.equipment.get_mut(slot) - && !stack.is_empty() - && rand::random::() < chance * difficulty.special_multiplier - { - crate::entity::mob::equipment::apply_vanilla_enchantments( - stack, - slot, - difficulty.special_multiplier, - ); + let mut first = true; + + for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { + let current = equipment.get(slot); + if !first && rand::random::() < partial_chance { + break; + } + first = false; + if current.is_empty() + && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) + { + equipment.put(slot, ItemStack::new(1, item)); + } } - }) + } } - fn mob_write_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) + fn populate_default_equipment_enchantments( + &self, + difficulty: &crate::entity::mob::equipment::RegionalDifficulty, + ) { + self.enchant_spawned_weapon(difficulty); + for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { + self.enchant_spawned_armor(slot, difficulty); + } } - fn mob_read_nbt<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) + fn enchant_spawned_weapon( + &self, + difficulty: &crate::entity::mob::equipment::RegionalDifficulty, + ) { + self.enchant_spawned_equipment( + &EquipmentSlot::MAIN_HAND, + MobEntity::MAX_ENCHANTED_WEAPON_CHANCE, + difficulty, + ); } + fn enchant_spawned_armor( + &self, + slot: &EquipmentSlot, + difficulty: &crate::entity::mob::equipment::RegionalDifficulty, + ) { + self.enchant_spawned_equipment(slot, MobEntity::MAX_ENCHANTED_ARMOR_CHANCE, difficulty); + } + + fn enchant_spawned_equipment( + &self, + slot: &EquipmentSlot, + chance: f32, + difficulty: &crate::entity::mob::equipment::RegionalDifficulty, + ) { + let living = &self.get_mob_entity().living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(stack) = equipment.equipment.get_mut(slot) + && !stack.is_empty() + && rand::random::() < chance * difficulty.special_multiplier + { + crate::entity::mob::equipment::apply_vanilla_enchantments( + stack, + slot, + difficulty.special_multiplier, + ); + } + } + + fn mob_write_nbt(&self, _nbt: &mut NbtCompound) {} + + fn mob_read_nbt(&self, _nbt: &NbtCompound) {} + /// Set or clear the mob's target. Override to add side effects when targeting changes. fn set_mob_target(&self, target: Option>) { let mob = self.get_mob_entity(); @@ -817,155 +800,127 @@ pub trait Mob: EntityBase + Send + Sync { .unwrap_or_else(std::sync::PoisonError::into_inner) = target; let world = mob.living_entity.entity.world.load_full(); let entity_id = mob.living_entity.entity.entity_id; - tokio::spawn(async move { + if let Some(server) = world.server.upgrade() { let mut event = crate::plugin::api::events::entity::entity_target::EntityTargetEvent::new( entity_id, target_id, ); - if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }); + server.plugin_manager.fire_blocking(&server, &mut event); + } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { self.get_mob_entity().mob_interact(player, item_stack) }) + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + self.get_mob_entity().mob_interact(player, item_stack) } - fn tame<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = crate::plugin::api::events::entity::entity_tame::EntityTameEvent::new( - mob.living_entity.entity.entity_id, - player.clone(), - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) + fn tame(&self, player: &Arc) { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_tame::EntityTameEvent::new( + mob.living_entity.entity.entity_id, + player.clone(), + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } } - fn breed(&self, father_id: i32, mother_id: i32, child_id: i32) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = crate::plugin::api::events::entity::entity_breed::EntityBreedEvent::new( - father_id, mother_id, child_id, - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) + fn breed(&self, father_id: i32, mother_id: i32, child_id: i32) { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_breed::EntityBreedEvent::new( + father_id, mother_id, child_id, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } } - fn dye<'a>( - &'a self, - color: crate::plugin::api::events::entity::entity_dye::DyeColor, - player: Option<&'a Arc>, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = crate::plugin::api::events::entity::entity_dye::EntityDyeEvent::new( - mob.living_entity.entity.entity_id, - color, - player.cloned(), - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) - } - - fn enter_love_mode( + fn dye( &self, - human_entity_id: Option, - ticks_in_love: i32, - ) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent::new( + color: crate::plugin::api::events::entity::entity_dye::DyeColor, + player: Option<&Arc>, + ) { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_dye::EntityDyeEvent::new( + mob.living_entity.entity.entity_id, + color, + player.cloned(), + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + } + + fn enter_love_mode(&self, human_entity_id: Option, ticks_in_love: i32) { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent::new( + mob.living_entity.entity.entity_id, + human_entity_id, + ticks_in_love, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + } + + fn transform(&self, new_entity_id: i32, transform_reason: String) { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_transform::EntityTransformEvent::new( mob.living_entity.entity.entity_id, - human_entity_id, - ticks_in_love, + new_entity_id, + transform_reason, ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } } - fn transform(&self, new_entity_id: i32, transform_reason: String) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = - crate::plugin::api::events::entity::entity_transform::EntityTransformEvent::new( - mob.living_entity.entity.entity_id, - new_entity_id, - transform_reason, - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) - } - - fn break_door(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = - crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new( - mob.living_entity.entity.entity_id, - block_pos, - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) - } - - fn enter_block(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = - crate::plugin::api::events::entity::entity_enter_block::EntityEnterBlockEvent::new( - mob.living_entity.entity.entity_id, - block_pos, - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) - } - - fn interact(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = - crate::plugin::api::events::entity::entity_interact::EntityInteractEvent::new( - mob.living_entity.entity.entity_id, - block_pos, - ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) - } - - fn place_block(&self, block_pos: BlockPos, block_name: String) -> EntityBaseFuture<'_, ()> { - Box::pin(async move { - let mob = self.get_mob_entity(); - let mut event = crate::plugin::api::events::entity::entity_place::EntityPlaceEvent::new( + fn break_door(&self, block_pos: BlockPos) { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new( mob.living_entity.entity.entity_id, block_pos, - block_name, ); - if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; - } - }) + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + } + + fn enter_block(&self, block_pos: BlockPos) { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_enter_block::EntityEnterBlockEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + } + + fn interact(&self, block_pos: BlockPos) { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_interact::EntityInteractEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } + } + + fn place_block(&self, block_pos: BlockPos, block_name: String) { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_place::EntityPlaceEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + block_name, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire_blocking(&server, &mut event); + } } fn mob_player_collision(&self, _player: &Arc) {} @@ -1006,17 +961,14 @@ pub trait Mob: EntityBase + Send + Sync { None } - fn mob_on_lightning_strike<'a>( - &'a self, - caller: &'a dyn EntityBase, - lightning: &'a crate::entity::lightning::LightningBoltEntity, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.get_mob_entity() - .living_entity - .on_lightning_strike(caller, lightning) - .await; - }) + fn mob_on_lightning_strike( + &self, + caller: &dyn EntityBase, + lightning: &crate::entity::lightning::LightningBoltEntity, + ) { + self.get_mob_entity() + .living_entity + .on_lightning_strike(caller, lightning); } } impl EntityBase for T { @@ -1024,14 +976,12 @@ impl EntityBase for T { Some(self) } - fn on_lightning_strike<'a>( - &'a self, - caller: &'a dyn EntityBase, - lightning: &'a crate::entity::lightning::LightningBoltEntity, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.mob_on_lightning_strike(caller, lightning).await; - }) + fn on_lightning_strike( + &self, + caller: &dyn EntityBase, + lightning: &crate::entity::lightning::LightningBoltEntity, + ) { + self.mob_on_lightning_strike(caller, lightning); } fn get_item_steerable(&self) -> Option<&dyn crate::entity::item_steerable::ItemSteerable> { @@ -1238,12 +1188,8 @@ impl EntityBase for T { damaged } - fn interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { self.mob_interact(player, item_stack).await }) + fn interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + self.mob_interact(player, item_stack) } fn on_player_collision(&self, player: &Arc) { @@ -1293,36 +1239,32 @@ impl EntityBase for T { ::get_home(self) } - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.get_mob_entity().write_mob_nbt(nbt); - if let Some(ageable) = self.as_ageable() { - ageable.write_ageable_nbt(nbt); - } - if let Some(animal) = self.as_animal() { - animal.write_animal_nbt(nbt); - } - if let Some(tamable) = self.as_tamable() { - tamable.write_tamable_nbt(nbt); - } - self.mob_write_nbt(nbt).await; - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + self.get_mob_entity().write_mob_nbt(nbt); + if let Some(ageable) = self.as_ageable() { + ageable.write_ageable_nbt(nbt); + } + if let Some(animal) = self.as_animal() { + animal.write_animal_nbt(nbt); + } + if let Some(tamable) = self.as_tamable() { + tamable.write_tamable_nbt(nbt); + } + self.mob_write_nbt(nbt); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.get_mob_entity().read_mob_nbt(nbt); - if let Some(ageable) = self.as_ageable() { - ageable.read_ageable_nbt(nbt); - } - if let Some(animal) = self.as_animal() { - animal.read_animal_nbt(nbt); - } - if let Some(tamable) = self.as_tamable() { - tamable.read_tamable_nbt(nbt); - } - self.mob_read_nbt(nbt).await; - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + self.get_mob_entity().read_mob_nbt(nbt); + if let Some(ageable) = self.as_ageable() { + ageable.read_ageable_nbt(nbt); + } + if let Some(animal) = self.as_animal() { + animal.read_animal_nbt(nbt); + } + if let Some(tamable) = self.as_tamable() { + tamable.read_tamable_nbt(nbt); + } + self.mob_read_nbt(nbt); } fn get_gravity(&self) -> f64 { diff --git a/crates/pumpkin/src/entity/mob/piglin.rs b/crates/pumpkin/src/entity/mob/piglin.rs index 08e4e9b3f..9f744f89e 100644 --- a/crates/pumpkin/src/entity/mob/piglin.rs +++ b/crates/pumpkin/src/entity/mob/piglin.rs @@ -1,5 +1,5 @@ use std::sync::{ - Arc, Weak, + Arc, Mutex, Weak, atomic::{AtomicBool, AtomicI32, Ordering}, }; @@ -16,12 +16,11 @@ use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::boundingbox::EntityDimensions; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; use crate::entity::living::LivingEntity; use crate::entity::player::Player; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, @@ -274,10 +273,13 @@ impl PiglinEntity { 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 .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 .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 mut guard = self.admiring_item.lock().await; + let mut guard = self + .admiring_item + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); guard.take() }; @@ -329,31 +334,34 @@ impl PiglinEntity { outcomes, ); 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 { PiglinAi::throw_items(self, event.outcome, None); } } else if !is_barter { - let remainder = self.add_to_inventory(item).await; + let remainder = self.add_to_inventory(item); if let Some(rem) = remainder { PiglinAi::throw_items(self, vec![rem], None); } } } else { - let remainder = self.add_to_inventory(item).await; + let remainder = self.add_to_inventory(item); if let Some(rem) = remainder { PiglinAi::throw_items(self, vec![rem], None); } } } - pub async fn cancel_admiring(&self) { + pub fn cancel_admiring(&self) { if self.is_admiring() { self.admire_timer.store(0, Ordering::Relaxed); 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() }; if let Some(item) = item { @@ -371,8 +379,8 @@ impl PiglinEntity { } } - pub async fn was_hurt_by(&self, attacker: Option<&dyn EntityBase>) { - self.cancel_admiring().await; + pub fn was_hurt_by(&self, attacker: Option<&dyn EntityBase>) { + self.cancel_admiring(); self.set_dancing(false); self.celebration_timer.store(0, Ordering::Relaxed); @@ -384,8 +392,11 @@ impl PiglinEntity { } } - pub async fn add_to_inventory(&self, item: ItemStack) -> Option { - let mut inv = self.inventory.lock().await; + pub fn add_to_inventory(&self, item: ItemStack) -> Option { + let mut inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if inv.len() < Self::INVENTORY_SIZE { inv.push(item); None @@ -483,53 +494,51 @@ impl Mob for PiglinEntity { &self.mob_entity } - fn populate_default_equipment_slots<'a>( - &'a self, - _world: &'a Arc, - _difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if !self.is_baby.load(Ordering::Relaxed) { - let living = &self.mob_entity.living_entity; - let mut equipment = living - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + fn populate_default_equipment_slots( + &self, + _world: &Arc, + _difficulty: &RegionalDifficulty, + ) { + if !self.is_baby.load(Ordering::Relaxed) { + let living = &self.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - // Spawn weapon: 50% crossbow, 5% golden spear (10% of remaining 50%), 45% golden sword - let weapon = if rand::random::() < 0.5 { - &Item::CROSSBOW - } else if rand::random_range(0..10) == 0 { - &Item::GOLDEN_SPEAR - } else { - &Item::GOLDEN_SWORD - }; - equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, weapon)); + // Spawn weapon: 50% crossbow, 5% golden spear (10% of remaining 50%), 45% golden sword + let weapon = if rand::random::() < 0.5 { + &Item::CROSSBOW + } else if rand::random_range(0..10) == 0 { + &Item::GOLDEN_SPEAR + } else { + &Item::GOLDEN_SWORD + }; + equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, weapon)); - // Armor: 10% chance per piece for golden armor - if rand::random::() < 0.1 { - equipment.put( - &EquipmentSlot::HEAD, - ItemStack::new(1, &Item::GOLDEN_HELMET), - ); - } - if rand::random::() < 0.1 { - equipment.put( - &EquipmentSlot::CHEST, - ItemStack::new(1, &Item::GOLDEN_CHESTPLATE), - ); - } - if rand::random::() < 0.1 { - equipment.put( - &EquipmentSlot::LEGS, - ItemStack::new(1, &Item::GOLDEN_LEGGINGS), - ); - } - if rand::random::() < 0.1 { - equipment.put(&EquipmentSlot::FEET, ItemStack::new(1, &Item::GOLDEN_BOOTS)); - } + // Armor: 10% chance per piece for golden armor + if rand::random::() < 0.1 { + equipment.put( + &EquipmentSlot::HEAD, + ItemStack::new(1, &Item::GOLDEN_HELMET), + ); } - }) + if rand::random::() < 0.1 { + equipment.put( + &EquipmentSlot::CHEST, + ItemStack::new(1, &Item::GOLDEN_CHESTPLATE), + ); + } + if rand::random::() < 0.1 { + equipment.put( + &EquipmentSlot::LEGS, + ItemStack::new(1, &Item::GOLDEN_LEGGINGS), + ); + } + if rand::random::() < 0.1 { + equipment.put(&EquipmentSlot::FEET, ItemStack::new(1, &Item::GOLDEN_BOOTS)); + } + } } fn mob_init_data_tracker(&self) { @@ -558,100 +567,96 @@ impl Mob for PiglinEntity { } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if self.is_immune_to_zombification() { - nbt.put_bool("IsImmuneToZombification", true); - } - let time_in_overworld = self.time_in_overworld.load(Ordering::Relaxed); - if time_in_overworld > 0 { - nbt.put_int("TimeInOverworld", time_in_overworld); - } - nbt.put_bool("CanPickUpLoot", true); - if self.is_baby() { - nbt.put_bool("IsBaby", true); - } - if !self.can_hunt() { - nbt.put_bool("CannotHunt", true); - } - if self.is_charging_crossbow() { - nbt.put_bool("IsChargingCrossbow", true); - } - if self.is_dancing() { - nbt.put_bool("IsDancing", true); - } + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + if self.is_immune_to_zombification() { + nbt.put_bool("IsImmuneToZombification", true); + } + let time_in_overworld = self.time_in_overworld.load(Ordering::Relaxed); + if time_in_overworld > 0 { + nbt.put_int("TimeInOverworld", time_in_overworld); + } + nbt.put_bool("CanPickUpLoot", true); + if self.is_baby() { + nbt.put_bool("IsBaby", true); + } + if !self.can_hunt() { + nbt.put_bool("CannotHunt", true); + } + if self.is_charging_crossbow() { + nbt.put_bool("IsChargingCrossbow", true); + } + if self.is_dancing() { + nbt.put_bool("IsDancing", true); + } - let inv = self.inventory.lock().await; - if !inv.is_empty() { - let mut items_tag = Vec::new(); - for item in inv.iter() { - if !item.is_empty() { - let mut item_nbt = NbtCompound::new(); - item.write_item_stack(&mut item_nbt); - items_tag.push(NbtTag::Compound(item_nbt)); - } - } - if !items_tag.is_empty() { - nbt.put_list("Inventory", items_tag); + let inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !inv.is_empty() { + let mut items_tag = Vec::new(); + for item in inv.iter() { + if !item.is_empty() { + let mut item_nbt = NbtCompound::new(); + item.write_item_stack(&mut item_nbt); + items_tag.push(NbtTag::Compound(item_nbt)); } } - }) + if !items_tag.is_empty() { + nbt.put_list("Inventory", items_tag); + } + } } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { - self.set_immune_to_zombification(immune); - } - if let Some(time) = nbt.get_int("TimeInOverworld") { - self.time_in_overworld.store(time, Ordering::Relaxed); - } - if let Some(baby) = nbt.get_bool("IsBaby") { - self.set_baby(baby); - } - if let Some(cannot_hunt) = nbt.get_bool("CannotHunt") { - self.set_cannot_hunt(cannot_hunt); - } - if let Some(charging) = nbt.get_bool("IsChargingCrossbow") { - self.set_charging_crossbow(charging); - } - if let Some(dancing) = nbt.get_bool("IsDancing") { - self.set_dancing(dancing); - } - if let Some(inv_list) = nbt.get_list("Inventory") { - let mut inv = self.inventory.lock().await; - inv.clear(); - for tag in inv_list { - if let Some(compound) = tag.extract_compound() - && let Some(stack) = ItemStack::read_item_stack(compound) - { - inv.push(stack); - } + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { + self.set_immune_to_zombification(immune); + } + if let Some(time) = nbt.get_int("TimeInOverworld") { + self.time_in_overworld.store(time, Ordering::Relaxed); + } + if let Some(baby) = nbt.get_bool("IsBaby") { + self.set_baby(baby); + } + if let Some(cannot_hunt) = nbt.get_bool("CannotHunt") { + self.set_cannot_hunt(cannot_hunt); + } + if let Some(charging) = nbt.get_bool("IsChargingCrossbow") { + self.set_charging_crossbow(charging); + } + if let Some(dancing) = nbt.get_bool("IsDancing") { + self.set_dancing(dancing); + } + if let Some(inv_list) = nbt.get_list("Inventory") { + let mut inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + inv.clear(); + for tag in inv_list { + if let Some(compound) = tag.extract_compound() + && let Some(stack) = ItemStack::read_item_stack(compound) + { + inv.push(stack); } } - }) + } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if PiglinAi::can_admire(self, item_stack) { - let mut given = item_stack.clone(); - given.item_count = 1; - if player.gamemode.load() != pumpkin_util::GameMode::Creative { - item_stack.item_count -= 1; - } - self.start_admiring(given).await; - return true; + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if PiglinAi::can_admire(self, item_stack) { + let mut given = item_stack.clone(); + given.item_count = 1; + if player.gamemode.load() != pumpkin_util::GameMode::Creative { + item_stack.item_count -= 1; } - self.mob_entity.mob_interact(player, item_stack) - }) + self.start_admiring(given); + return true; + } + self.mob_entity.mob_interact(player, item_stack) } - fn mob_tick<'a>(&'a self, caller: &'a Arc) { + fn mob_tick<'a>(&'a self, _caller: &'a Arc) { let entity = &self.mob_entity.living_entity.entity; if !entity.is_alive() { return; @@ -686,12 +691,7 @@ impl Mob for PiglinEntity { if self.admire_timer.load(Ordering::Relaxed) > 0 { let remaining = self.admire_timer.fetch_sub(1, Ordering::Relaxed) - 1; if remaining <= 0 { - let caller_clone = caller.clone(); - tokio::spawn(async move { - if let Some(piglin) = caller_clone.cast_any().downcast_ref::() { - piglin.stop_holding_off_hand_item(true).await; - } - }); + self.stop_holding_off_hand_item(true); } } } diff --git a/crates/pumpkin/src/entity/mob/piglin_brute.rs b/crates/pumpkin/src/entity/mob/piglin_brute.rs index 990bde14e..af49cfa83 100644 --- a/crates/pumpkin/src/entity/mob/piglin_brute.rs +++ b/crates/pumpkin/src/entity/mob/piglin_brute.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, @@ -196,28 +196,24 @@ impl Mob for PiglinBruteEntity { } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if self.is_immune_to_zombification() { - nbt.put_bool("IsImmuneToZombification", true); - } - let time_in_overworld = self.time_in_overworld.load(Ordering::Relaxed); - if time_in_overworld > 0 { - nbt.put_int("TimeInOverworld", time_in_overworld); - } - nbt.put_bool("CanPickUpLoot", true); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + if self.is_immune_to_zombification() { + nbt.put_bool("IsImmuneToZombification", true); + } + let time_in_overworld = self.time_in_overworld.load(Ordering::Relaxed); + if time_in_overworld > 0 { + nbt.put_int("TimeInOverworld", time_in_overworld); + } + nbt.put_bool("CanPickUpLoot", true); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { - self.set_immune_to_zombification(immune); - } - if let Some(time) = nbt.get_int("TimeInOverworld") { - self.time_in_overworld.store(time, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(immune) = nbt.get_bool("IsImmuneToZombification") { + self.set_immune_to_zombification(immune); + } + if let Some(time) = nbt.get_int("TimeInOverworld") { + self.time_in_overworld.store(time, Ordering::Relaxed); + } } fn mob_tick<'a>(&'a self, _caller: &'a Arc) { diff --git a/crates/pumpkin/src/entity/mob/pillager.rs b/crates/pumpkin/src/entity/mob/pillager.rs index e5d2a11ed..59c625407 100644 --- a/crates/pumpkin/src/entity/mob/pillager.rs +++ b/crates/pumpkin/src/entity/mob/pillager.rs @@ -1,5 +1,5 @@ use std::sync::{ - Arc, Weak, + Arc, Mutex, Weak, atomic::{AtomicBool, Ordering}, }; @@ -10,10 +10,9 @@ use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::java::client::play::Metadata; -use tokio::sync::Mutex; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, ranged_crossbow_attack::RangedCrossbowAttackGoal, @@ -115,8 +114,11 @@ impl PillagerEntity { ); } - pub async fn add_to_inventory(&self, item: ItemStack) -> Option { - let mut inv = self.inventory.lock().await; + pub fn add_to_inventory(&self, item: ItemStack) -> Option { + let mut inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if inv.len() < Self::INVENTORY_SIZE { inv.push(item); None @@ -163,56 +165,49 @@ impl Mob for PillagerEntity { } fn mob_init_data_tracker(&self) { - let entity = self.get_entity(); - if self.is_charging_crossbow() { - entity.send_meta_data( - &[Metadata::new( - tracked_data::pillager::IS_CHARGING_CROSSBOW, - true, - )], - None, - ); + self.set_charging_crossbow(self.is_charging_crossbow.load(Ordering::Relaxed)); + } + + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.write_raider_nbt(nbt); + nbt.put_bool("CanPickUpLoot", true); + + let inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !inv.is_empty() { + let mut items_tag = Vec::new(); + for item in inv.iter() { + if !item.is_empty() { + let mut item_nbt = NbtCompound::new(); + item.write_item_stack(&mut item_nbt); + items_tag.push(NbtTag::Compound(item_nbt)); + } + } + if !items_tag.is_empty() { + nbt.put_list("Inventory", items_tag); + } } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - nbt.put_bool("CanPickUpLoot", true); + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.read_raider_nbt(nbt); - let inv = self.inventory.lock().await; - if !inv.is_empty() { - let mut items_tag = Vec::new(); - for item in inv.iter() { - if !item.is_empty() { - let mut item_nbt = NbtCompound::new(); - item.write_item_stack(&mut item_nbt); - items_tag.push(NbtTag::Compound(item_nbt)); - } - } - if !items_tag.is_empty() { - nbt.put_list("Inventory", items_tag); + if let Some(inv_list) = nbt.get_list("Inventory") { + let mut inv = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + inv.clear(); + for tag in inv_list { + if let Some(compound) = tag.extract_compound() + && let Some(stack) = ItemStack::read_item_stack(compound) + { + inv.push(stack); } } - }) - } - - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - - if let Some(inv_list) = nbt.get_list("Inventory") { - let mut inv = self.inventory.lock().await; - inv.clear(); - for tag in inv_list { - if let Some(compound) = tag.extract_compound() - && let Some(stack) = ItemStack::read_item_stack(compound) - { - inv.push(stack); - } - } - } - }) + } } fn on_damage( diff --git a/crates/pumpkin/src/entity/mob/ravager.rs b/crates/pumpkin/src/entity/mob/ravager.rs index eaab3fcfa..79603cdb5 100644 --- a/crates/pumpkin/src/entity/mob/ravager.rs +++ b/crates/pumpkin/src/entity/mob/ravager.rs @@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound; use pumpkin_nbt::compound::NbtCompound; use crate::entity::{ - Entity, NbtFuture, + Entity, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, swim::SwimGoal, @@ -97,16 +97,12 @@ impl Mob for RavagerEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.write_raider_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.read_raider_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/shulker.rs b/crates/pumpkin/src/entity/mob/shulker.rs index 46861e1c4..05a8ae2a1 100644 --- a/crates/pumpkin/src/entity/mob/shulker.rs +++ b/crates/pumpkin/src/entity/mob/shulker.rs @@ -21,7 +21,7 @@ use crate::entity::ai::goal::revenge::RevengeGoal; use crate::entity::ai::goal::{Controls, Goal}; use crate::entity::mob::{Mob, MobEntity}; use crate::entity::projectile::shulker_bullet::ShulkerBulletEntity; -use crate::entity::{Entity, EntityBase, NbtFuture}; +use crate::entity::{Entity, EntityBase}; const DEFAULT_ATTACH_FACE: BlockDirection = BlockDirection::Down; const NO_COLOR: u8 = 16; @@ -322,26 +322,22 @@ impl ShulkerEntity { } impl Mob for ShulkerEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - 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("Color", self.color.load(Ordering::Relaxed) as i8); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + 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("Color", self.color.load(Ordering::Relaxed) as i8); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(face) = nbt.get_byte("AttachFace") { - self.attach_face.store(face as u8, Ordering::Relaxed); - } - if let Some(peek) = nbt.get_byte("PeekAmount") { - self.peek_amount.store(peek as u8, Ordering::Relaxed); - } - if let Some(color) = nbt.get_byte("Color") { - self.color.store(color as u8, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(face) = nbt.get_byte("AttachFace") { + self.attach_face.store(face as u8, Ordering::Relaxed); + } + if let Some(peek) = nbt.get_byte("PeekAmount") { + self.peek_amount.store(peek as u8, Ordering::Relaxed); + } + if let Some(color) = nbt.get_byte("Color") { + self.color.store(color as u8, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/mob/skeleton/mod.rs b/crates/pumpkin/src/entity/mob/skeleton/mod.rs index d0f42c8ad..28fefcd15 100644 --- a/crates/pumpkin/src/entity/mob/skeleton/mod.rs +++ b/crates/pumpkin/src/entity/mob/skeleton/mod.rs @@ -7,7 +7,7 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_util::Difficulty; use crate::entity::{ - Entity, EntityBaseFuture, + Entity, ai::goal::{ active_target::ActiveTargetGoal, bow_attack::BowAttackGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, @@ -76,57 +76,55 @@ impl Mob for SkeletonEntityBase { &self.mob_entity } - fn populate_default_equipment_slots<'a>( - &'a self, - _world: &'a Arc, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Default armor slots (super.populateDefaultEquipmentSlots) - if rand::random::() - < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier - { - let mut armor_type = rand::random_range(0..3); - for _ in 1..=3 { - if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { - armor_type += 1; - } - } - - let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { - 0.1f32 - } else { - 0.25f32 - }; - - let living = &self.mob_entity.living_entity; - let mut equipment = living - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut first = true; - - for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { - let current = equipment.get(slot); - if !first && rand::random::() < partial_chance { - break; - } - first = false; - if current.is_empty() - && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) - { - equipment.put(slot, ItemStack::new(1, item)); - } + fn populate_default_equipment_slots( + &self, + _world: &Arc, + difficulty: &RegionalDifficulty, + ) { + // Default armor slots (super.populateDefaultEquipmentSlots) + if rand::random::() + < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier + { + let mut armor_type = rand::random_range(0..3); + for _ in 1..=3 { + if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { + armor_type += 1; } } - // AbstractSkeleton sets BOW on MAIN_HAND + let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { + 0.1f32 + } else { + 0.25f32 + }; + let living = &self.mob_entity.living_entity; let mut equipment = living .entity_equipment .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, &Item::BOW)); - }) + let mut first = true; + + for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { + let current = equipment.get(slot); + if !first && rand::random::() < partial_chance { + break; + } + first = false; + if current.is_empty() + && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) + { + equipment.put(slot, ItemStack::new(1, item)); + } + } + } + + // AbstractSkeleton sets BOW on MAIN_HAND + let living = &self.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, &Item::BOW)); } } diff --git a/crates/pumpkin/src/entity/mob/skeleton/skeleton.rs b/crates/pumpkin/src/entity/mob/skeleton/skeleton.rs index 873b19093..ac2924ebd 100644 --- a/crates/pumpkin/src/entity/mob/skeleton/skeleton.rs +++ b/crates/pumpkin/src/entity/mob/skeleton/skeleton.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::entity::{ - Entity, EntityBaseFuture, + Entity, mob::{Mob, MobEntity, equipment::RegionalDifficulty, skeleton::SkeletonEntityBase}, }; use crate::world::World; @@ -23,20 +23,17 @@ impl Mob for SkeletonEntity { &self.entity.mob_entity } - fn populate_default_equipment_slots<'a>( - &'a self, - world: &'a Arc, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { + fn populate_default_equipment_slots( + &self, + world: &Arc, + difficulty: &RegionalDifficulty, + ) { self.entity - .populate_default_equipment_slots(world, difficulty) + .populate_default_equipment_slots(world, difficulty); } - fn populate_default_equipment_enchantments<'a>( - &'a self, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { + fn populate_default_equipment_enchantments(&self, difficulty: &RegionalDifficulty) { self.entity - .populate_default_equipment_enchantments(difficulty) + .populate_default_equipment_enchantments(difficulty); } } diff --git a/crates/pumpkin/src/entity/mob/slime.rs b/crates/pumpkin/src/entity/mob/slime.rs index b892c00ad..c31c712ed 100644 --- a/crates/pumpkin/src/entity/mob/slime.rs +++ b/crates/pumpkin/src/entity/mob/slime.rs @@ -12,7 +12,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ai::control::{Control, MoveControlTrait}, ai::goal::{Goal, active_target::ActiveTargetGoal}, mob::{Mob, MobEntity}, @@ -265,21 +265,17 @@ impl SlimeEntity { } impl Mob for SlimeEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("Size", self.get_size() - 1); - nbt.put_bool("wasOnGround", self.was_on_ground.load(Ordering::Relaxed)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("Size", self.get_size() - 1); + nbt.put_bool("wasOnGround", self.was_on_ground.load(Ordering::Relaxed)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.set_size(nbt.get_int("Size").unwrap_or(0) + 1, false); - self.was_on_ground.store( - nbt.get_bool("wasOnGround").unwrap_or(false), - Ordering::Relaxed, - ); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.set_size(nbt.get_int("Size").unwrap_or(0) + 1, false); + self.was_on_ground.store( + nbt.get_bool("wasOnGround").unwrap_or(false), + Ordering::Relaxed, + ); } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/mob/vindicator.rs b/crates/pumpkin/src/entity/mob/vindicator.rs index 2cc1c7f96..6d5e642ef 100644 --- a/crates/pumpkin/src/entity/mob/vindicator.rs +++ b/crates/pumpkin/src/entity/mob/vindicator.rs @@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound; use pumpkin_nbt::compound::NbtCompound; use crate::entity::{ - Entity, NbtFuture, + Entity, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, open_door::OpenDoorGoal, @@ -99,16 +99,12 @@ impl Mob for VindicatorEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.write_raider_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.read_raider_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/witch.rs b/crates/pumpkin/src/entity/mob/witch.rs index 8d3c39465..8d4168f80 100644 --- a/crates/pumpkin/src/entity/mob/witch.rs +++ b/crates/pumpkin/src/entity/mob/witch.rs @@ -199,22 +199,12 @@ impl Mob for WitchEntity { Some(self) } - fn mob_write_nbt<'a>( - &'a self, - nbt: &'a mut pumpkin_nbt::compound::NbtCompound, - ) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - self.write_raider_nbt(nbt); - }) + fn mob_write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { + self.write_raider_nbt(nbt); } - fn mob_read_nbt<'a>( - &'a self, - nbt: &'a pumpkin_nbt::compound::NbtCompound, - ) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - self.read_raider_nbt(nbt); - }) + fn mob_read_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) { + self.read_raider_nbt(nbt); } fn pre_damage(&self, _damage_type: DamageType, source: Option<&dyn EntityBase>) -> bool { diff --git a/crates/pumpkin/src/entity/mob/zombie/drowned.rs b/crates/pumpkin/src/entity/mob/zombie/drowned.rs index 62f47884d..4dd1976ee 100644 --- a/crates/pumpkin/src/entity/mob/zombie/drowned.rs +++ b/crates/pumpkin/src/entity/mob/zombie/drowned.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::{ - Entity, NbtFuture, + Entity, mob::{Mob, MobEntity}, }; use pumpkin_nbt::compound::NbtCompound; @@ -47,15 +47,11 @@ impl Mob for DrownedEntity { &self.entity.mob_entity } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_write_nbt(nbt).await; - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.entity.mob_write_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_read_nbt(nbt).await; - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.entity.mob_read_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/zombie/husk.rs b/crates/pumpkin/src/entity/mob/zombie/husk.rs index 934ee61e2..5b6d77208 100644 --- a/crates/pumpkin/src/entity/mob/zombie/husk.rs +++ b/crates/pumpkin/src/entity/mob/zombie/husk.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::{ - Entity, NbtFuture, + Entity, mob::{Mob, MobEntity}, }; use pumpkin_nbt::compound::NbtCompound; @@ -31,15 +31,11 @@ impl Mob for HuskEntity { &self.entity.mob_entity } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_write_nbt(nbt).await; - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.entity.mob_write_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_read_nbt(nbt).await; - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.entity.mob_read_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/zombie/mod.rs b/crates/pumpkin/src/entity/mob/zombie/mod.rs index 05a28afdd..d3b3190e5 100644 --- a/crates/pumpkin/src/entity/mob/zombie/mod.rs +++ b/crates/pumpkin/src/entity/mob/zombie/mod.rs @@ -1,5 +1,4 @@ use super::{Mob, MobEntity}; -use crate::entity::NbtFuture; use crate::entity::ai::goal::break_door::BreakDoorGoal; use crate::entity::ai::goal::destroy_egg::DestroyEggGoal; use crate::entity::ai::goal::look_around::RandomLookAroundGoal; @@ -7,10 +6,12 @@ use crate::entity::ai::goal::revenge::RevengeGoal; use crate::entity::ai::goal::swim::SwimGoal; use crate::entity::ai::goal::wander_around::WanderAroundGoal; use crate::entity::ai::goal::zombie_attack::ZombieAttackGoal; +use crate::entity::mob::equipment::RegionalDifficulty; use crate::entity::{ Entity, ai::goal::{Goal, active_target::ActiveTargetGoal, look_at_entity::LookAtEntityGoal}, }; +use crate::world::World; use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; @@ -20,10 +21,6 @@ use pumpkin_util::Difficulty; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; -use crate::entity::EntityBaseFuture; -use crate::entity::mob::equipment::RegionalDifficulty; -use crate::world::World; - pub mod drowned; pub mod husk; #[allow(clippy::module_inception)] @@ -135,85 +132,79 @@ impl Mob for ZombieEntityBase { &self.mob_entity } - fn populate_default_equipment_slots<'a>( - &'a self, - _world: &'a Arc, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - // Default armor slots (super.populateDefaultEquipmentSlots) - if rand::random::() - < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier - { - let mut armor_type = rand::random_range(0..3); - for _ in 1..=3 { - if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { - armor_type += 1; - } - } - - let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { - 0.1f32 - } else { - 0.25f32 - }; - - let living = &self.mob_entity.living_entity; - let mut equipment = living - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut first = true; - - for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { - let current = equipment.get(slot); - if !first && rand::random::() < partial_chance { - break; - } - first = false; - if current.is_empty() - && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) - { - equipment.put(slot, ItemStack::new(1, item)); - } + fn populate_default_equipment_slots( + &self, + _world: &Arc, + difficulty: &RegionalDifficulty, + ) { + // Default armor slots (super.populateDefaultEquipmentSlots) + if rand::random::() + < MobEntity::MAX_WEARING_ARMOR_CHANCE * difficulty.special_multiplier + { + let mut armor_type = rand::random_range(0..3); + for _ in 1..=3 { + if rand::random::() < MobEntity::WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE { + armor_type += 1; } } - let weapon_chance = if difficulty.base_difficulty == Difficulty::Hard { - 0.05f32 + let partial_chance = if difficulty.base_difficulty == Difficulty::Hard { + 0.1f32 } else { - 0.01f32 + 0.25f32 }; - if rand::random::() < weapon_chance { - let r = rand::random_range(0..6); - let weapon_item = match r { - 0 => &Item::IRON_SWORD, - 1 => &Item::IRON_SPEAR, - _ => &Item::IRON_SHOVEL, - }; - let living = &self.mob_entity.living_entity; - let mut equipment = living - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, weapon_item)); + + let living = &self.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut first = true; + + for slot in &MobEntity::EQUIPMENT_POPULATION_ORDER { + let current = equipment.get(slot); + if !first && rand::random::() < partial_chance { + break; + } + first = false; + if current.is_empty() + && let Some(item) = MobEntity::get_equipment_for_slot(slot, armor_type) + { + equipment.put(slot, ItemStack::new(1, item)); + } } - }) + } + + let weapon_chance = if difficulty.base_difficulty == Difficulty::Hard { + 0.05f32 + } else { + 0.01f32 + }; + if rand::random::() < weapon_chance { + let r = rand::random_range(0..6); + let weapon_item = match r { + 0 => &Item::IRON_SWORD, + 1 => &Item::IRON_SPEAR, + _ => &Item::IRON_SHOVEL, + }; + let living = &self.mob_entity.living_entity; + let mut equipment = living + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + equipment.put(&EquipmentSlot::MAIN_HAND, ItemStack::new(1, weapon_item)); + } } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if self.can_break_doors() { - nbt.put_bool("CanBreakDoors", true); - } - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + if self.can_break_doors() { + nbt.put_bool("CanBreakDoors", true); + } } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(can_break_doors) = nbt.get_bool("CanBreakDoors") { - self.set_can_break_doors(can_break_doors, self); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(can_break_doors) = nbt.get_bool("CanBreakDoors") { + self.set_can_break_doors(can_break_doors, self); + } } } diff --git a/crates/pumpkin/src/entity/mob/zombie/zombie.rs b/crates/pumpkin/src/entity/mob/zombie/zombie.rs index 26bfdadb6..412038a54 100644 --- a/crates/pumpkin/src/entity/mob/zombie/zombie.rs +++ b/crates/pumpkin/src/entity/mob/zombie/zombie.rs @@ -1,7 +1,7 @@ +use crate::entity::Entity; use crate::entity::mob::equipment::RegionalDifficulty; use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::mob::{Mob, MobEntity}; -use crate::entity::{Entity, EntityBaseFuture, NbtFuture}; use crate::world::World; use pumpkin_nbt::compound::NbtCompound; use std::sync::Arc; @@ -30,32 +30,25 @@ impl Mob for ZombieEntity { &self.entity.mob_entity } - fn populate_default_equipment_slots<'a>( - &'a self, - world: &'a Arc, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { + fn populate_default_equipment_slots( + &self, + world: &Arc, + difficulty: &RegionalDifficulty, + ) { self.entity - .populate_default_equipment_slots(world, difficulty) + .populate_default_equipment_slots(world, difficulty); } - fn populate_default_equipment_enchantments<'a>( - &'a self, - difficulty: &'a RegionalDifficulty, - ) -> EntityBaseFuture<'a, ()> { + fn populate_default_equipment_enchantments(&self, difficulty: &RegionalDifficulty) { self.entity - .populate_default_equipment_enchantments(difficulty) + .populate_default_equipment_enchantments(difficulty); } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_write_nbt(nbt).await; - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.entity.mob_write_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.entity.mob_read_nbt(nbt).await; - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.entity.mob_read_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mob/zombie/zombie_villager.rs b/crates/pumpkin/src/entity/mob/zombie/zombie_villager.rs index 0e42db918..c6f1bce5f 100644 --- a/crates/pumpkin/src/entity/mob/zombie/zombie_villager.rs +++ b/crates/pumpkin/src/entity/mob/zombie/zombie_villager.rs @@ -1,6 +1,6 @@ +use crate::entity::Entity; use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::mob::{Mob, MobEntity}; -use crate::entity::{Entity, NbtFuture}; use pumpkin_nbt::compound::NbtCompound; use std::sync::Arc; @@ -28,15 +28,11 @@ impl Mob for ZombieVillagerEntity { &self.mob_entity.mob_entity } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.mob_entity.mob_write_nbt(nbt).await; - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + self.mob_entity.mob_write_nbt(nbt); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.mob_entity.mob_read_nbt(nbt).await; - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.mob_entity.mob_read_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs index 8b3baa90c..17970749b 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -137,33 +137,25 @@ pub type EntityBaseFuture<'a, T> = Pin + Send + 'a>>; pub type TeleportFuture = Pin + Send>>; pub trait EntityBase: Send + Sync + std::any::Any { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.get_entity().write_nbt(nbt).await; - if let Some(living) = self.get_living_entity() { - living.write_living_nbt(nbt).await; - } - self.write_custom_nbt(nbt).await; - }) + fn write_nbt(&self, nbt: &mut NbtCompound) { + self.get_entity().write_nbt(nbt); + if let Some(living) = self.get_living_entity() { + living.write_living_nbt(nbt); + } + self.write_custom_nbt(nbt); } - fn write_custom_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) + fn write_custom_nbt(&self, _nbt: &mut NbtCompound) {} + + fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + self.get_entity().read_nbt_non_mut(nbt); + if let Some(living) = self.get_living_entity() { + living.read_living_nbt_non_mut(nbt); + } + self.read_custom_nbt(nbt); } - fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.get_entity().read_nbt_non_mut(nbt).await; - if let Some(living) = self.get_living_entity() { - living.read_living_nbt_non_mut(nbt).await; - } - self.read_custom_nbt(nbt).await; - }) - } - - fn read_custom_nbt<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) - } + fn read_custom_nbt(&self, _nbt: &NbtCompound) {} /// Called every tick for this entity. /// /// The `caller` parameter is a reference to the entity that initiated the tick. @@ -266,25 +258,23 @@ pub trait EntityBase: Send + Sync + std::any::Any { caller.damage_with_context(caller, amount, damage_type, None, None, None) } - fn on_lightning_strike<'a>( - &'a self, - caller: &'a dyn EntityBase, - lightning: &'a lightning::LightningBoltEntity, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.get_living_entity().is_some() { - self.set_on_fire_for(8.0); - let cause = lightning.get_cause(); - self.damage_with_context( - caller, - 5.0, - DamageType::LIGHTNING_BOLT, - None, - Some(lightning), - cause.as_deref().map(|p| p as &dyn EntityBase), - ); - } - }) + fn on_lightning_strike( + &self, + caller: &dyn EntityBase, + lightning: &lightning::LightningBoltEntity, + ) { + if self.get_living_entity().is_some() { + self.set_on_fire_for(8.0); + let cause = lightning.get_cause(); + self.damage_with_context( + caller, + 5.0, + DamageType::LIGHTNING_BOLT, + None, + Some(lightning), + cause.as_deref().map(|p| p as &dyn EntityBase), + ); + } } fn is_spectator(&self) -> bool { @@ -322,7 +312,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { .unwrap_or(entity.entity_type.resource_name); let mut metadata = entity.bedrock_metadata(); if let Some(mob) = self.get_mob() - && let Some(mob_metadata) = mob.mob_bedrock_spawn_metadata().await + && let Some(mob_metadata) = mob.mob_bedrock_spawn_metadata() { metadata.0.extend(mob_metadata.0); } @@ -356,7 +346,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { let is_mob = entity.entity_type.mob || self.get_mob().is_some(); if version < JavaMinecraftVersion::V_1_19 && is_mob { let metadata = if let Some(mob) = self.get_mob() { - mob.mob_java_spawn_metadata(version).await + mob.mob_java_spawn_metadata(version) } else { None }; @@ -378,7 +368,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { client.enqueue_packet(data).await; } if let Some(mob) = self.get_mob() - && let Some(metadata) = mob.mob_java_spawn_metadata(version).await + && let Some(metadata) = mob.mob_java_spawn_metadata(version) { let meta_packet = CSetEntityMetadata::new(entity.entity_id.into(), metadata); if let Ok(meta_data) = client.serialize_packet(&meta_packet) { @@ -411,14 +401,11 @@ pub trait EntityBase: Send + Sync + std::any::Any { false } + /// Called when a player right-clicks this entity with an item. /// Called when a player right-clicks this entity with an item. /// Returns true if the interaction was handled. - fn interact<'a>( - &'a self, - _player: &'a Arc, - _item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async { false }) + fn interact(&self, _player: &Arc, _item_stack: &mut ItemStack) -> bool { + false } fn set_on_fire_for(&self, seconds: f32) { @@ -450,23 +437,17 @@ pub trait EntityBase: Send + Sync + std::any::Any { /// Called when a player collides with an entity fn on_player_collision(&self, _player: &Arc) {} - fn is_passenger(&self) -> EntityBaseFuture<'_, bool> { - Box::pin(async move { self.get_entity().has_vehicle() }) + fn is_passenger(&self) -> bool { + self.get_entity().has_vehicle() } - fn is_vehicle(&self) -> EntityBaseFuture<'_, bool> { - Box::pin(async move { self.get_entity().has_passengers() }) + fn is_vehicle(&self) -> bool { + self.get_entity().has_passengers() } - fn has_passenger<'a>(&'a self, other: &'a Arc) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - self.get_entity() - .passengers - .lock() - .await - .iter() - .any(|p| p.get_entity().entity_id == other.get_entity().entity_id) - }) + fn has_passenger(&self, other: &Arc) -> bool { + self.get_entity() + .has_passenger(other.get_entity().entity_id) } fn move_entity(&self, caller: &Arc, motion: Vector3) { @@ -477,198 +458,175 @@ pub trait EntityBase: Send + Sync + std::any::Any { false } - fn push<'a>(&'a self, entity: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let self_entity = self.get_entity(); - let other_entity = entity.get_entity(); + fn push(&self, entity: &Arc) { + let self_entity = self.get_entity(); + let other_entity = entity.get_entity(); - if self_entity.no_physics.load(Ordering::Relaxed) - || other_entity.no_physics.load(Ordering::Relaxed) - { - return; + if self_entity.no_physics.load(Ordering::Relaxed) + || other_entity.no_physics.load(Ordering::Relaxed) + { + return; + } + + if self_entity.has_passenger(other_entity.entity_id) + || other_entity.has_passenger(self_entity.entity_id) + { + return; + } + + let mut dx = other_entity.pos.load().x - self_entity.pos.load().x; + let mut dz = other_entity.pos.load().z - self_entity.pos.load().z; + let mut d = dx.abs().max(dz.abs()); + if d >= 0.01 { + d = d.sqrt(); + dx /= d; + dz /= d; + let mut d2 = 1.0 / d; + if d2 > 1.0 { + d2 = 1.0; + } + dx *= d2; + dz *= d2; + dx *= 0.05; + dz *= 0.05; + + if !self_entity.has_passengers() && self.is_pushable() { + let mut vel = self_entity.velocity.load(); + vel.x -= dx; + vel.z -= dz; + self_entity.velocity.store(vel); + self_entity.send_velocity(); } - { - let passengers = self_entity.passengers.lock().await; - if passengers - .iter() - .any(|p| p.get_entity().entity_id == other_entity.entity_id) - { - return; - } + if !other_entity.has_passengers() && entity.is_pushable() { + let mut vel = other_entity.velocity.load(); + vel.x += dx; + vel.z += dz; + other_entity.velocity.store(vel); + other_entity.send_velocity(); } - { - let passengers = other_entity.passengers.lock().await; - if passengers - .iter() - .any(|p| p.get_entity().entity_id == self_entity.entity_id) - { - return; - } - } - - let mut dx = other_entity.pos.load().x - self_entity.pos.load().x; - let mut dz = other_entity.pos.load().z - self_entity.pos.load().z; - let mut d = dx.abs().max(dz.abs()); - if d >= 0.01 { - d = d.sqrt(); - dx /= d; - dz /= d; - let mut d2 = 1.0 / d; - if d2 > 1.0 { - d2 = 1.0; - } - dx *= d2; - dz *= d2; - dx *= 0.05; - dz *= 0.05; - - if !self_entity.has_passengers() && self.is_pushable() { - let mut vel = self_entity.velocity.load(); - vel.x -= dx; - vel.z -= dz; - self_entity.velocity.store(vel); - self_entity.send_velocity(); - } - - if !other_entity.has_passengers() && entity.is_pushable() { - let mut vel = other_entity.velocity.load(); - vel.x += dx; - vel.z += dz; - other_entity.velocity.store(vel); - other_entity.send_velocity(); - } - } - }) + } } #[allow(clippy::too_many_lines)] - fn push_entities<'a>( - &'a self, - dyn_self: &'a Arc, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let mut picked_up = false; - let mut pushed = false; - let self_entity = self.get_entity(); - let entity_bb = self_entity.bounding_box.load(); + fn push_entities(&self, dyn_self: &Arc) -> bool { + let mut picked_up = false; + let mut pushed = false; + let self_entity = self.get_entity(); + let entity_bb = self_entity.bounding_box.load(); - if !self.is_pushable() { - return false; - } + if !self.is_pushable() { + return false; + } - let world = self_entity.world.load(); + let world = self_entity.world.load(); - let is_rideable_minecart = self_entity.entity_type.id == EntityType::MINECART.id; - let is_abstract_minecart = is_rideable_minecart - || self_entity.entity_type.id == EntityType::CHEST_MINECART.id - || self_entity.entity_type.id == EntityType::COMMAND_BLOCK_MINECART.id - || self_entity.entity_type.id == EntityType::FURNACE_MINECART.id - || self_entity.entity_type.id == EntityType::HOPPER_MINECART.id - || self_entity.entity_type.id == EntityType::SPAWNER_MINECART.id - || self_entity.entity_type.id == EntityType::TNT_MINECART.id; + let is_rideable_minecart = self_entity.entity_type.id == EntityType::MINECART.id; + let is_abstract_minecart = is_rideable_minecart + || self_entity.entity_type.id == EntityType::CHEST_MINECART.id + || self_entity.entity_type.id == EntityType::COMMAND_BLOCK_MINECART.id + || self_entity.entity_type.id == EntityType::FURNACE_MINECART.id + || self_entity.entity_type.id == EntityType::HOPPER_MINECART.id + || self_entity.entity_type.id == EntityType::SPAWNER_MINECART.id + || self_entity.entity_type.id == EntityType::TNT_MINECART.id; - let is_minecart_fn = |id| -> bool { - id == EntityType::MINECART.id - || id == EntityType::CHEST_MINECART.id - || id == EntityType::COMMAND_BLOCK_MINECART.id - || id == EntityType::FURNACE_MINECART.id - || id == EntityType::HOPPER_MINECART.id - || id == EntityType::SPAWNER_MINECART.id - || id == EntityType::TNT_MINECART.id - }; + let is_minecart_fn = |id| -> bool { + id == EntityType::MINECART.id + || id == EntityType::CHEST_MINECART.id + || id == EntityType::COMMAND_BLOCK_MINECART.id + || id == EntityType::FURNACE_MINECART.id + || id == EntityType::HOPPER_MINECART.id + || id == EntityType::SPAWNER_MINECART.id + || id == EntityType::TNT_MINECART.id + }; - if is_abstract_minecart { - let is_vehicle = self.is_vehicle().await; + if is_abstract_minecart { + let is_vehicle = self.is_vehicle(); - if is_rideable_minecart && !is_vehicle { - let pickup_bb = entity_bb.expand(0.2, 0.0, 0.2); - let other_entities = world.get_entities_at_box(&pickup_bb); + if is_rideable_minecart && !is_vehicle { + let pickup_bb = entity_bb.expand(0.2, 0.0, 0.2); + let other_entities = world.get_entities_at_box(&pickup_bb); - for other in other_entities { - if other.get_entity().entity_id != self_entity.entity_id { - let other_type = other.get_entity().entity_type.id; - let is_iron_golem = other_type == EntityType::IRON_GOLEM.id; - let is_other_minecart = is_minecart_fn(other_type); - - if !is_iron_golem - && !is_other_minecart - && !other.is_passenger().await - && other.is_pushable() - && other.get_entity().riding_cooldown.load(Relaxed) == 0 - { - dyn_self - .get_entity() - .add_passenger(dyn_self.clone(), other.clone()) - .await; - picked_up = true; - break; - } - } - } - } - - let push_bb = entity_bb.expand(1.0e-7, 1.0e-7, 1.0e-7); - - let other_entities = world.get_entities_at_box(&push_bb); for other in other_entities { if other.get_entity().entity_id != self_entity.entity_id { let other_type = other.get_entity().entity_type.id; - let is_other_minecart = is_minecart_fn(other_type); let is_iron_golem = other_type == EntityType::IRON_GOLEM.id; + let is_other_minecart = is_minecart_fn(other_type); - if is_rideable_minecart { - if (is_iron_golem - || is_other_minecart - || is_vehicle - || !other.get_entity().has_vehicle()) - && other.is_pushable() - { - dyn_self.push(&other).await; - pushed = true; - } - } else if !self.has_passenger(&other).await + if !is_iron_golem + && !is_other_minecart + && !other.is_passenger() && other.is_pushable() - && is_other_minecart + && other.get_entity().riding_cooldown.load(Relaxed) == 0 { - dyn_self.push(&other).await; - pushed = true; + dyn_self + .get_entity() + .add_passenger(dyn_self.clone(), other.clone()); + picked_up = true; + break; } } } + } - let players = world.get_players_at_box(&push_bb); - for player in players { - if player.get_entity().entity_id != self_entity.entity_id - && is_rideable_minecart + let push_bb = entity_bb.expand(1.0e-7, 1.0e-7, 1.0e-7); + + let other_entities = world.get_entities_at_box(&push_bb); + for other in other_entities { + if other.get_entity().entity_id != self_entity.entity_id { + let other_type = other.get_entity().entity_type.id; + let is_other_minecart = is_minecart_fn(other_type); + let is_iron_golem = other_type == EntityType::IRON_GOLEM.id; + + if is_rideable_minecart { + if (is_iron_golem + || is_other_minecart + || is_vehicle + || !other.get_entity().has_vehicle()) + && other.is_pushable() + { + dyn_self.push(&other); + pushed = true; + } + } else if !self.has_passenger(&other) + && other.is_pushable() + && is_other_minecart { - let player_base: Arc = player.clone(); - dyn_self.push(&player_base).await; - pushed = true; - // Non-rideable minecarts (hoppers, chests) do not push players in vanilla. - } - } - } else { - let other_entities = world.get_entities_at_box(&entity_bb); - for other in other_entities { - if other.get_entity().entity_id != self_entity.entity_id { - dyn_self.push(&other).await; - pushed = true; - } - } - - let players = world.get_players_at_box(&entity_bb); - for player in players { - if player.get_entity().entity_id != self_entity.entity_id { - let player_base: Arc = player.clone(); - dyn_self.push(&player_base).await; + dyn_self.push(&other); pushed = true; } } } - picked_up && !pushed - }) + let players = world.get_players_at_box(&push_bb); + for player in players { + if player.get_entity().entity_id != self_entity.entity_id && is_rideable_minecart { + let player_base: Arc = player.clone(); + dyn_self.push(&player_base); + pushed = true; + // Non-rideable minecarts (hoppers, chests) do not push players in vanilla. + } + } + } else { + let other_entities = world.get_entities_at_box(&entity_bb); + for other in other_entities { + if other.get_entity().entity_id != self_entity.entity_id { + dyn_self.push(&other); + pushed = true; + } + } + + let players = world.get_players_at_box(&entity_bb); + for player in players { + if player.get_entity().entity_id != self_entity.entity_id { + let player_base: Arc = player.clone(); + dyn_self.push(&player_base); + pushed = true; + } + } + } + + picked_up && !pushed } fn on_hit(&self, _hit: crate::entity::projectile::ProjectileHit) {} @@ -720,38 +678,38 @@ pub trait EntityBase: Send + Sync + std::any::Any { )) } - fn get_display_name(&self) -> EntityBaseFuture<'_, TextComponent> { - Box::pin(async move { - // TODO: team color - let entity = self.get_entity(); - let mut name = entity.custom_name.load().as_ref().clone().unwrap_or( - TextComponent::translate_cross( + fn get_display_name(&self) -> TextComponent { + // TODO: team color + let entity = self.get_entity(); + let mut name = + entity + .custom_name + .load() + .as_ref() + .clone() + .unwrap_or(TextComponent::translate_cross( format!("entity.minecraft.{}", entity.entity_type.resource_name), format!("entity.minecraft.{}", entity.entity_type.resource_name), [], - ), - ); - let name_clone = name.clone(); - name = name.hover_event(HoverEvent::show_entity( - entity.entity_uuid.to_string(), - entity.entity_type.resource_name.into(), - Some(name_clone), - )); - name = name.insertion(entity.entity_uuid.to_string()); - name - }) + )); + let name_clone = name.clone(); + name = name.hover_event(HoverEvent::show_entity( + entity.entity_uuid.to_string(), + entity.entity_type.resource_name.into(), + Some(name_clone), + )); + name = name.insertion(entity.entity_uuid.to_string()); + name } /// Kills the Entity. - fn kill<'a>(&'a self, caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - if self.get_living_entity().is_some() { - caller.damage(caller, f32::MAX, DamageType::GENERIC_KILL); - } else { - // TODO this should be removed once all entities are implemented - self.get_entity().remove(); - } - }) + fn kill(&self, caller: &dyn EntityBase) { + if self.get_living_entity().is_some() { + caller.damage(caller, f32::MAX, DamageType::GENERIC_KILL); + } else { + // TODO this should be removed once all entities are implemented + self.get_entity().remove(); + } } fn get_experience_reward(&self, _killer: Option<&dyn EntityBase>) -> u32 { @@ -875,9 +833,9 @@ pub struct Entity { pub was_in_powder_snow: AtomicBool, pub removal_reason: AtomicCell>, // The passengers that entity has - pub passengers: Mutex>>, + pub passengers: std::sync::Mutex>>, /// The vehicle that entity is in - pub vehicle: Mutex>>, + pub vehicle: std::sync::Mutex>>, /// The entity this entity is attached/leashed to (if any) pub leashed_to: std::sync::Mutex>>, /// Cooldown before entity can mount again after dismounting @@ -899,7 +857,7 @@ pub struct Entity { pub has_no_gravity: AtomicBool, /// Scoreboard tags attached to this entity, managed with `/tag`. /// Vanilla allows at most [`MAX_SCOREBOARD_TAGS`] tags per entity. - pub scoreboard_tags: Mutex>, + pub scoreboard_tags: std::sync::Mutex>, /// The data send in the Entity Spawn packet pub data: AtomicI32, /// Stores entity boolean flags (on fire, sneaking, invisible, glowing, etc.) @@ -925,7 +883,7 @@ pub struct Entity { /// Cache for the last sent head yaw byte pub last_sent_head_yaw: AtomicU8, /// Persistent custom data container for plugins (matching Bukkit's `PersistentDataHolder`) - pub custom_data: Mutex, + pub custom_data: std::sync::Mutex, } impl Entity { @@ -1029,8 +987,8 @@ impl Entity { is_in_powder_snow: AtomicBool::new(false), was_in_powder_snow: AtomicBool::new(false), removal_reason: AtomicCell::new(None), - passengers: Mutex::new(Vec::new()), - vehicle: Mutex::new(None), + passengers: std::sync::Mutex::new(Vec::new()), + vehicle: std::sync::Mutex::new(None), leashed_to: std::sync::Mutex::new(None), riding_cooldown: AtomicI32::new(0), @@ -1043,7 +1001,7 @@ impl Entity { custom_name_visible: AtomicBool::new(false), silent: AtomicBool::new(false), has_no_gravity: AtomicBool::new(false), - scoreboard_tags: Mutex::new(HashSet::new()), + scoreboard_tags: std::sync::Mutex::new(HashSet::new()), no_physics: AtomicBool::new(false), movement_multiplier: AtomicCell::new(Vector3::default()), velocity_dirty: AtomicBool::new(true), @@ -1052,7 +1010,7 @@ impl Entity { last_sent_pitch: AtomicU8::new(0), last_sent_head_yaw: AtomicU8::new(0), last_sent_pos: AtomicCell::new(position), - custom_data: Mutex::new(NbtCompound::new()), + custom_data: std::sync::Mutex::new(NbtCompound::new()), } } @@ -1137,16 +1095,22 @@ impl Entity { /// /// Returns `false` if the entity already has the tag or already carries /// [`MAX_SCOREBOARD_TAGS`] tags. - pub async fn add_scoreboard_tag(&self, tag: &str) -> bool { - let mut tags = self.scoreboard_tags.lock().await; + pub fn add_scoreboard_tag(&self, tag: &str) -> bool { + let mut tags = self + .scoreboard_tags + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); tags.len() < MAX_SCOREBOARD_TAGS && tags.insert(tag.to_owned()) } /// Removes a scoreboard tag from this entity. /// /// Returns `false` if the entity did not have the tag. - pub async fn remove_scoreboard_tag(&self, tag: &str) -> bool { - self.scoreboard_tags.lock().await.remove(tag) + pub fn remove_scoreboard_tag(&self, tag: &str) -> bool { + self.scoreboard_tags + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .remove(tag) } /// Sets a custom name for the entity, typically used with nametags @@ -2391,15 +2355,13 @@ impl Entity { let yaw = self.yaw.load(); tokio::spawn(async move { - let transition = portal_type - .get_portal_destination( - &world_clone, - dest_world_opt, - &caller_clone, - entry_pos, - src_portal, - ) - .await; + let transition = portal_type.get_portal_destination( + &world_clone, + dest_world_opt, + &caller_clone, + entry_pos, + src_portal, + ); if let Some(transition) = transition { let dest_world = transition.new_world.clone(); @@ -2436,13 +2398,17 @@ impl Entity { /// Recursively teleports all passengers (and their passengers) to the destination fn teleport_passengers_recursive<'a>( - entity: &'a Self, + entity: &'a Entity, position: Vector3, yaw_delta: Option, dest_world: &'a Arc, ) -> std::pin::Pin + Send + 'a>> { Box::pin(async move { - let passengers = entity.passengers.lock().await.clone(); + let passengers = entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); for passenger in passengers { let passenger_entity = passenger.get_entity(); let passenger_yaw = yaw_delta.map(|delta| passenger_entity.yaw.load() + delta); @@ -2452,7 +2418,11 @@ impl Entity { ); // Get nested passengers before teleporting - let nested_passengers = passenger_entity.passengers.lock().await.clone(); + let nested_passengers = passenger_entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); passenger .teleport(position, passenger_yaw, None, dest_world.clone()) @@ -3391,22 +3361,33 @@ impl Entity { } pub fn has_passengers(&self) -> bool { - self.passengers.try_lock().is_ok_and(|p| !p.is_empty()) + !self + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() } pub fn has_passenger(&self, id: i32) -> bool { - self.passengers.try_lock().is_ok_and(|p| { - p.iter() - .any(|passenger| passenger.get_entity().entity_id == id) - }) + self.passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .any(|passenger| passenger.get_entity().entity_id == id) } pub fn has_vehicle(&self) -> bool { - self.vehicle.try_lock().is_ok_and(|v| v.is_some()) + self.vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() } pub fn get_vehicle(&self) -> Option> { - self.vehicle.try_lock().ok().and_then(|v| v.clone()) + self.vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } pub fn is_leashed(&self) -> bool { @@ -3416,11 +3397,7 @@ impl Entity { .is_some() } - pub async fn add_passenger( - &self, - vehicle: Arc, - passenger: Arc, - ) { + pub fn add_passenger(&self, vehicle: Arc, passenger: Arc) { let mut mount_event = crate::plugin::api::events::entity::entity_mount::EntityMountEvent::new( passenger.get_entity().entity_id, @@ -3432,20 +3409,27 @@ impl Entity { passenger.get_entity().entity_id, ); if let Some(server) = self.world.load().server.upgrade() { - server.plugin_manager.fire(&server, &mut mount_event).await; server .plugin_manager - .fire(&server, &mut vehicle_enter) - .await; + .fire_blocking(&server, &mut mount_event); + server + .plugin_manager + .fire_blocking(&server, &mut vehicle_enter); } if mount_event.cancelled || vehicle_enter.cancelled { return; } let passenger_entity = passenger.get_entity(); - *passenger_entity.vehicle.lock().await = Some(vehicle); + *passenger_entity + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(vehicle); - let mut passengers = self.passengers.lock().await; + let mut passengers = self + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); passengers.push(passenger); let passenger_ids: Vec = passengers @@ -3461,14 +3445,21 @@ impl Entity { ); } - pub(crate) async fn remove_passenger_on_disconnect(&self, passenger_id: i32) { - let mut passengers = self.passengers.lock().await; + pub(crate) fn remove_passenger_on_disconnect(&self, passenger_id: i32) { + let mut passengers = self + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(index) = passengers .iter() .position(|passenger| passenger.get_entity().entity_id == passenger_id) { let passenger = passengers.remove(index); - *passenger.get_entity().vehicle.lock().await = None; + *passenger + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; } let passenger_ids: Vec = passengers @@ -3514,23 +3505,32 @@ impl Entity { return; } - let mut passengers = self.passengers.lock().await; - let removed_passenger = if let Some(idx) = passengers - .iter() - .position(|p| p.get_entity().entity_id == passenger_id) - { - let passenger = passengers.remove(idx); - *passenger.get_entity().vehicle.lock().await = None; - Some(passenger) - } else { - None - }; + let (removed_passenger, passenger_ids) = { + let mut passengers = self + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed_passenger = if let Some(idx) = passengers + .iter() + .position(|p| p.get_entity().entity_id == passenger_id) + { + let passenger = passengers.remove(idx); + *passenger + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + Some(passenger) + } else { + None + }; - let passenger_ids: Vec = passengers - .iter() - .map(|p| VarInt(p.get_entity().entity_id)) - .collect(); - drop(passengers); + let passenger_ids: Vec = passengers + .iter() + .map(|p| VarInt(p.get_entity().entity_id)) + .collect(); + (removed_passenger, passenger_ids) + }; let chunk_pos = self.chunk_pos.load(); @@ -3841,8 +3841,11 @@ impl Entity { self.movement_multiplier.store(multiplier); } - pub async fn set_custom_data(&self, namespace: &str, key: &str, value: NbtTag) { - let mut custom_data = self.custom_data.lock().await; + pub fn set_custom_data(&self, namespace: &str, key: &str, value: NbtTag) { + let mut custom_data = self + .custom_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut namespace_data = custom_data .child_tags @@ -3859,8 +3862,11 @@ impl Entity { .insert(namespace.into(), NbtTag::Compound(namespace_data)); } - pub async fn get_custom_data(&self, namespace: &str, key: &str) -> Option { - let custom_data = self.custom_data.lock().await; + pub fn get_custom_data(&self, namespace: &str, key: &str) -> Option { + let custom_data = self + .custom_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); custom_data .get(namespace)? .extract_compound()? @@ -3868,8 +3874,11 @@ impl Entity { .cloned() } - pub async fn remove_custom_data(&self, namespace: &str, key: &str) { - let mut custom_data = self.custom_data.lock().await; + pub fn remove_custom_data(&self, namespace: &str, key: &str) { + let mut custom_data = self + .custom_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let Some(NbtTag::Compound(mut namespace_data)) = custom_data.child_tags.remove(namespace) else { @@ -3884,151 +3893,159 @@ impl Entity { } } - pub async fn has_custom_data(&self, namespace: &str, key: &str) -> bool { - self.get_custom_data(namespace, key).await.is_some() + pub fn has_custom_data(&self, namespace: &str, key: &str) -> bool { + self.get_custom_data(namespace, key).is_some() } } impl Entity { - pub fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - let position = self.pos.load(); - nbt.put_string( - "id", - format!("minecraft:{}", self.entity_type.resource_name), - ); - nbt.put_uuid("UUID", self.entity_uuid); - nbt.put( - "Pos", - NbtTag::List(vec![ - position.x.into(), - position.y.into(), - position.z.into(), - ]), - ); - let velocity = self.velocity.load(); - nbt.put( - "Motion", - NbtTag::List(vec![ - velocity.x.into(), - velocity.y.into(), - velocity.z.into(), - ]), - ); - nbt.put( - "Rotation", - NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]), - ); - nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16); - nbt.put_bool("OnGround", self.on_ground.load(Relaxed)); - nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed)); - nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32); - if self.has_visual_fire.load(Relaxed) { - nbt.put_bool("HasVisualFire", true); - } - nbt.put_int("TicksFrozen", self.frozen_ticks.load(Relaxed)); - if let Some(custom_name) = &**self.custom_name.load() - && let Ok(name_json) = pumpkin_util::serde_json::to_string(custom_name) - { - nbt.put_string("CustomName", name_json); - } - nbt.put_bool("CustomNameVisible", self.custom_name_visible.load(Relaxed)); + pub fn write_nbt(&self, nbt: &mut NbtCompound) { + let position = self.pos.load(); + nbt.put_string( + "id", + format!("minecraft:{}", self.entity_type.resource_name), + ); + nbt.put_uuid("UUID", self.entity_uuid); + nbt.put( + "Pos", + NbtTag::List(vec![ + position.x.into(), + position.y.into(), + position.z.into(), + ]), + ); + let velocity = self.velocity.load(); + nbt.put( + "Motion", + NbtTag::List(vec![ + velocity.x.into(), + velocity.y.into(), + velocity.z.into(), + ]), + ); + nbt.put( + "Rotation", + NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]), + ); + nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16); + nbt.put_bool("OnGround", self.on_ground.load(Relaxed)); + nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed)); + nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32); + if self.has_visual_fire.load(Relaxed) { + nbt.put_bool("HasVisualFire", true); + } + nbt.put_int("TicksFrozen", self.frozen_ticks.load(Relaxed)); + if let Some(custom_name) = &**self.custom_name.load() + && let Ok(name_json) = pumpkin_util::serde_json::to_string(custom_name) + { + nbt.put_string("CustomName", name_json); + } + nbt.put_bool("CustomNameVisible", self.custom_name_visible.load(Relaxed)); - let tags = self.scoreboard_tags.lock().await; - if !tags.is_empty() { - nbt.put( - "Tags", - NbtTag::List( - tags.iter() - .map(|tag| NbtTag::String(tag.as_str().into())) - .collect(), - ), - ); - } + let tags = self + .scoreboard_tags + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !tags.is_empty() { + nbt.put( + "Tags", + NbtTag::List( + tags.iter() + .map(|tag| NbtTag::String(tag.as_str().into())) + .collect(), + ), + ); + } - let custom_data = self.custom_data.lock().await; - if !custom_data.is_empty() { - nbt.put_compound("PumpkinCustomData", custom_data.clone()); - } + let custom_data = self + .custom_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !custom_data.is_empty() { + nbt.put_compound("PumpkinCustomData", custom_data.clone()); + } - // todo more... - }) + // todo more... } - pub fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(position) = nbt.get_list("Pos") - && position.len() >= 3 - { - let x = position[0].extract_double().unwrap_or(0.0); - let y = position[1].extract_double().unwrap_or(0.0); - let z = position[2].extract_double().unwrap_or(0.0); - let pos = Vector3::new(x, y, z); - self.set_pos(pos); - self.last_sent_pos.store(pos); - } - if let Some(velocity) = nbt.get_list("Motion") - && velocity.len() >= 3 - { - let x = velocity[0].extract_double().unwrap_or(0.0); - let y = velocity[1].extract_double().unwrap_or(0.0); - let z = velocity[2].extract_double().unwrap_or(0.0); - self.velocity.store(Vector3::new(x, y, z)); - } - if let Some(rotation) = nbt.get_list("Rotation") - && rotation.len() >= 2 - { - let yaw = rotation[0].extract_float().unwrap_or(0.0); - let pitch = rotation[1].extract_float().unwrap_or(0.0); - self.set_rotation(yaw, pitch); - let yaw_byte = (yaw * 256.0 / 360.0).rem_euclid(256.0) as u8; - let pitch_byte = (pitch * 256.0 / 360.0).rem_euclid(256.0) as u8; - self.last_sent_yaw.store(yaw_byte, Relaxed); - self.last_sent_pitch.store(pitch_byte, Relaxed); - self.head_yaw.store(yaw); - self.last_sent_head_yaw.store(yaw_byte, Relaxed); - } - self.fire_ticks - .store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed); - self.on_ground - .store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed); - self.invulnerable - .store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed); - self.portal_cooldown - .store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed); - self.has_visual_fire - .store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed); - self.frozen_ticks - .store(nbt.get_int("TicksFrozen").unwrap_or(0), Relaxed); - if let Some(name_json) = nbt.get_string("CustomName") - && let Ok(component) = pumpkin_util::serde_json::from_str(name_json) - { - self.custom_name.store(Arc::new(Some(component))); - } - self.custom_name_visible - .store(nbt.get_bool("CustomNameVisible").unwrap_or(false), Relaxed); + pub fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + if let Some(position) = nbt.get_list("Pos") + && position.len() >= 3 + { + let x = position[0].extract_double().unwrap_or(0.0); + let y = position[1].extract_double().unwrap_or(0.0); + let z = position[2].extract_double().unwrap_or(0.0); + let pos = Vector3::new(x, y, z); + self.set_pos(pos); + self.last_sent_pos.store(pos); + } + if let Some(velocity) = nbt.get_list("Motion") + && velocity.len() >= 3 + { + let x = velocity[0].extract_double().unwrap_or(0.0); + let y = velocity[1].extract_double().unwrap_or(0.0); + let z = velocity[2].extract_double().unwrap_or(0.0); + self.velocity.store(Vector3::new(x, y, z)); + } + if let Some(rotation) = nbt.get_list("Rotation") + && rotation.len() >= 2 + { + let yaw = rotation[0].extract_float().unwrap_or(0.0); + let pitch = rotation[1].extract_float().unwrap_or(0.0); + self.set_rotation(yaw, pitch); + let yaw_byte = (yaw * 256.0 / 360.0).rem_euclid(256.0) as u8; + let pitch_byte = (pitch * 256.0 / 360.0).rem_euclid(256.0) as u8; + self.last_sent_yaw.store(yaw_byte, Relaxed); + self.last_sent_pitch.store(pitch_byte, Relaxed); + self.head_yaw.store(yaw); + self.last_sent_head_yaw.store(yaw_byte, Relaxed); + } + self.fire_ticks + .store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed); + self.on_ground + .store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed); + self.invulnerable + .store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed); + self.portal_cooldown + .store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed); + self.has_visual_fire + .store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed); + self.frozen_ticks + .store(nbt.get_int("TicksFrozen").unwrap_or(0), Relaxed); + if let Some(name_json) = nbt.get_string("CustomName") + && let Ok(component) = pumpkin_util::serde_json::from_str(name_json) + { + self.custom_name.store(Arc::new(Some(component))); + } + self.custom_name_visible + .store(nbt.get_bool("CustomNameVisible").unwrap_or(false), Relaxed); - if let Some(tag_list) = nbt.get_list("Tags") { - let mut tags = self.scoreboard_tags.lock().await; - tags.clear(); - tags.extend( - tag_list - .iter() - .filter_map(|tag| tag.extract_string().map(str::to_owned)) - .take(MAX_SCOREBOARD_TAGS), - ); - } + if let Some(tag_list) = nbt.get_list("Tags") { + let mut tags = self + .scoreboard_tags + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + tags.clear(); + tags.extend( + tag_list + .iter() + .filter_map(|tag| tag.extract_string().map(str::to_owned)) + .take(MAX_SCOREBOARD_TAGS), + ); + } - if let Some(custom_data) = nbt - .get_compound("PumpkinCustomData") - .or_else(|| nbt.get_compound("BukkitValues")) - { - let mut data = self.custom_data.lock().await; - *data = custom_data.clone(); - } + if let Some(custom_data) = nbt + .get_compound("PumpkinCustomData") + .or_else(|| nbt.get_compound("BukkitValues")) + { + let mut data = self + .custom_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *data = custom_data.clone(); + } - // todo more... - }) + // todo more... } } @@ -4101,41 +4118,28 @@ impl EntityBase for Entity { } impl NBTStorage for T { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - EntityBase::write_nbt(self, nbt) + fn write_nbt(&self, nbt: &mut NbtCompound) { + EntityBase::write_nbt(self, nbt); } - fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - EntityBase::read_nbt_non_mut(self, nbt) + fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + EntityBase::read_nbt_non_mut(self, nbt); } } -pub type NbtFuture<'a, T> = Pin + Send + 'a>>; - pub trait NBTStorage: Send + Sync { - fn write_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) + fn write_nbt(&self, _nbt: &mut NbtCompound) {} + + fn read_nbt(&mut self, nbt: &mut NbtCompound) { + self.read_nbt_non_mut(nbt); } - fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_nbt_non_mut(nbt).await; - }) - } - - fn read_nbt_non_mut<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async {}) - } + fn read_nbt_non_mut(&self, _nbt: &NbtCompound) {} } -pub type NBTInitFuture<'a, T> = Pin> + Send + 'a>>; - pub trait NBTStorageInit: Send + Sync + Sized { - fn create_from_nbt<'a>(_nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self> - where - Self: 'a, - { - Box::pin(async move { None }) + fn create_from_nbt(_nbt: &mut NbtCompound) -> Option { + None } } diff --git a/crates/pumpkin/src/entity/passive/animal.rs b/crates/pumpkin/src/entity/passive/animal.rs index 2795fe643..dbd4227b1 100644 --- a/crates/pumpkin/src/entity/passive/animal.rs +++ b/crates/pumpkin/src/entity/passive/animal.rs @@ -4,7 +4,7 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; -use crate::entity::{EntityBaseFuture, mob::Mob, player::Player}; +use crate::entity::{mob::Mob, player::Player}; use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; use pumpkin_util::math::vector3::Vector3; @@ -36,72 +36,70 @@ pub trait Animal: Mob { mob_entity.set_love_ticks(in_love, love_cause); } - fn animal_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, + fn animal_interact( + &self, + player: &Arc, + item_stack: &mut ItemStack, ambient_sound: Sound, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let mob_entity = self.get_mob_entity(); - if self.is_food(item_stack) { - let age = mob_entity + ) -> bool { + let mob_entity = self.get_mob_entity(); + if self.is_food(item_stack) { + let age = mob_entity + .living_entity + .entity + .age + .load(std::sync::atomic::Ordering::Relaxed); + + if age >= 0 && mob_entity.is_breeding_ready() && !mob_entity.is_in_love() { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); + + mob_entity.set_love_ticks(600, Some(player.gameprofile.id)); + let entity = &mob_entity.living_entity.entity; + let world = entity.world.load(); + let pos = entity.pos.load(); + + world.send_entity_status( + entity, + pumpkin_data::entity::EntityStatus::InLoveHearts, + Some(ActorEventID::InLoveHearts), + ); + + world.spawn_particle( + pos + Vector3::new(0.0, f64::from(entity.height()), 0.0), + Vector3::new(0.5, 0.5, 0.5), + 1.0, + 7, + Particle::Heart, + ); + world.play_sound(ambient_sound, SoundCategory::Neutral, &entity.pos.load()); + return true; + } + + if age < 0 { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); + let speedup = (-age / 10).max(1); + mob_entity .living_entity .entity .age - .load(std::sync::atomic::Ordering::Relaxed); + .fetch_add(speedup, std::sync::atomic::Ordering::Relaxed); - if age >= 0 && mob_entity.is_breeding_ready() && !mob_entity.is_in_love() { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); + let entity = &mob_entity.living_entity.entity; + let world = entity.world.load(); + let pos = entity.pos.load(); - mob_entity.set_love_ticks(600, Some(player.gameprofile.id)); - let entity = &mob_entity.living_entity.entity; - let world = entity.world.load(); - let pos = entity.pos.load(); - - world.send_entity_status( - entity, - pumpkin_data::entity::EntityStatus::InLoveHearts, - Some(ActorEventID::InLoveHearts), - ); - - world.spawn_particle( - pos + Vector3::new(0.0, f64::from(entity.height()), 0.0), - Vector3::new(0.5, 0.5, 0.5), - 1.0, - 7, - Particle::Heart, - ); - world.play_sound(ambient_sound, SoundCategory::Neutral, &entity.pos.load()); - return true; - } - - if age < 0 { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); - let speedup = (-age / 10).max(1); - mob_entity - .living_entity - .entity - .age - .fetch_add(speedup, std::sync::atomic::Ordering::Relaxed); - - let entity = &mob_entity.living_entity.entity; - let world = entity.world.load(); - let pos = entity.pos.load(); - - world.spawn_particle( - pos + Vector3::new(0.0, f64::from(entity.height()), 0.0), - Vector3::new(0.5, 0.5, 0.5), - 1.0, - 7, - Particle::HappyVillager, - ); - self.play_eating_sound(ambient_sound); - return true; - } + world.spawn_particle( + pos + Vector3::new(0.0, f64::from(entity.height()), 0.0), + Vector3::new(0.5, 0.5, 0.5), + 1.0, + 7, + Particle::HappyVillager, + ); + self.play_eating_sound(ambient_sound); + return true; } + } - mob_entity.mob_interact(player, item_stack) - }) + mob_entity.mob_interact(player, item_stack) } } diff --git a/crates/pumpkin/src/entity/passive/armadillo.rs b/crates/pumpkin/src/entity/passive/armadillo.rs index 3d58aee92..49e0e269b 100644 --- a/crates/pumpkin/src/entity/passive/armadillo.rs +++ b/crates/pumpkin/src/entity/passive/armadillo.rs @@ -14,7 +14,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -231,23 +231,21 @@ impl ArmadilloEntity { } } - pub fn brush_off_scute<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_baby() { - return false; - } - let entity = self.get_entity(); - let world = entity.world.load(); - let pos = entity.pos.load(); - let item_entity = Arc::new(ItemEntity::new( - Entity::new(world.clone(), pos, &EntityType::ITEM), - ItemStack::new(1, &Item::ARMADILLO_SCUTE), - )); - world.spawn_entity(item_entity); - world.play_sound(Sound::EntityArmadilloBrush, SoundCategory::Neutral, &pos); - player.damage_held_item(16).await; - true - }) + pub fn brush_off_scute(&self, player: &Arc) -> bool { + if self.is_baby() { + return false; + } + let entity = self.get_entity(); + let world = entity.world.load(); + let pos = entity.pos.load(); + let item_entity = Arc::new(ItemEntity::new( + Entity::new(world.clone(), pos, &EntityType::ITEM), + ItemStack::new(1, &Item::ARMADILLO_SCUTE), + )); + world.spawn_entity(item_entity); + world.play_sound(Sound::EntityArmadilloBrush, SoundCategory::Neutral, &pos); + player.damage_held_item(16); + true } pub fn is_scared_by(&self, living_entity: &dyn EntityBase) -> bool { @@ -309,24 +307,20 @@ impl Mob for ArmadilloEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_string("state", self.get_state().name().to_string()); - nbt.put_int("scute_time", self.scute_time.load(Ordering::Relaxed)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_string("state", self.get_state().name().to_string()); + nbt.put_int("scute_time", self.scute_time.load(Ordering::Relaxed)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(state_name) = nbt.get_string("state") { - self.switch_to_state(ArmadilloState::from_name(state_name)); - } else if let Some(state_id) = nbt.get_int("state") { - self.switch_to_state(ArmadilloState::from_id(state_id)); - } - if let Some(scute_time) = nbt.get_int("scute_time") { - self.scute_time.store(scute_time, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(state_name) = nbt.get_string("state") { + self.switch_to_state(ArmadilloState::from_name(state_name)); + } else if let Some(state_id) = nbt.get_int("state") { + self.switch_to_state(ArmadilloState::from_id(state_id)); + } + if let Some(scute_time) = nbt.get_int("scute_time") { + self.scute_time.store(scute_time, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -435,20 +429,13 @@ impl Mob for ArmadilloEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if item_stack.item == &Item::BRUSH && self.brush_off_scute(player).await { - return true; - } - if self.is_scared() { - return false; - } - self.animal_interact(player, item_stack, Sound::EntityArmadilloAmbient) - .await - }) + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if item_stack.item == &Item::BRUSH && self.brush_off_scute(player) { + return true; + } + if self.is_scared() { + return false; + } + self.animal_interact(player, item_stack, Sound::EntityArmadilloAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/cat.rs b/crates/pumpkin/src/entity/passive/cat.rs index 17247d94c..a3691dd8a 100644 --- a/crates/pumpkin/src/entity/passive/cat.rs +++ b/crates/pumpkin/src/entity/passive/cat.rs @@ -15,7 +15,7 @@ use rand::RngExt; use uuid::Uuid; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, avoid_entity::AvoidEntityGoal, breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_owner::FollowOwnerGoal, @@ -297,57 +297,53 @@ impl Mob for CatEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let variant_str = match self.variant.load(Ordering::Relaxed) { - 0 => "minecraft:all_black", - 1 => "minecraft:black", - 2 => "minecraft:british_shorthair", - 3 => "minecraft:calico", - 4 => "minecraft:jellie", - 5 => "minecraft:persian", - 6 => "minecraft:ragdoll", - 7 => "minecraft:red", - 8 => "minecraft:siamese", - 10 => "minecraft:white", - _ => "minecraft:tabby", - }; - nbt.put_string("variant", variant_str.to_string()); - nbt.put_string("sound_variant", "minecraft:classic".to_string()); - nbt.put_byte( - "CollarColor", - self.collar_color.load(Ordering::Relaxed) as i8, - ); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + let variant_str = match self.variant.load(Ordering::Relaxed) { + 0 => "minecraft:all_black", + 1 => "minecraft:black", + 2 => "minecraft:british_shorthair", + 3 => "minecraft:calico", + 4 => "minecraft:jellie", + 5 => "minecraft:persian", + 6 => "minecraft:ragdoll", + 7 => "minecraft:red", + 8 => "minecraft:siamese", + 10 => "minecraft:white", + _ => "minecraft:tabby", + }; + nbt.put_string("variant", variant_str.to_string()); + nbt.put_string("sound_variant", "minecraft:classic".to_string()); + nbt.put_byte( + "CollarColor", + self.collar_color.load(Ordering::Relaxed) as i8, + ); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(variant_str) = nbt.get_string("variant") { - let variant = match variant_str - .strip_prefix("minecraft:") - .unwrap_or(variant_str) - { - "all_black" => 0, - "black" => 1, - "british_shorthair" => 2, - "calico" => 3, - "jellie" => 4, - "persian" => 5, - "ragdoll" => 6, - "red" => 7, - "siamese" => 8, - "white" => 10, - _ => 9, - }; - self.variant.store(variant, Ordering::Relaxed); - } - if let Some(collar) = nbt.get_byte("CollarColor") { - self.collar_color.store(collar as u8, Ordering::Relaxed); - } else if let Some(collar_int) = nbt.get_int("CollarColor") { - self.collar_color.store(collar_int as u8, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(variant_str) = nbt.get_string("variant") { + let variant = match variant_str + .strip_prefix("minecraft:") + .unwrap_or(variant_str) + { + "all_black" => 0, + "black" => 1, + "british_shorthair" => 2, + "calico" => 3, + "jellie" => 4, + "persian" => 5, + "ragdoll" => 6, + "red" => 7, + "siamese" => 8, + "white" => 10, + _ => 9, + }; + self.variant.store(variant, Ordering::Relaxed); + } + if let Some(collar) = nbt.get_byte("CollarColor") { + self.collar_color.store(collar as u8, Ordering::Relaxed); + } else if let Some(collar_int) = nbt.get_int("CollarColor") { + self.collar_color.store(collar_int as u8, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -434,69 +430,63 @@ impl Mob for CatEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let item = item_stack.get_item(); - let is_food = self.is_food(item_stack); + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + let item = item_stack.get_item(); + let is_food = self.is_food(item_stack); - if self.is_tame() { - if self.get_owner_uuid() == Some(player.gameprofile.id) { - if item.has_tag(&tag::Item::MINECRAFT_CAT_COLLAR_DYES) - || item.has_tag(&tag::Item::C_DYES) - { - if let Some(color) = get_dye_color_from_item(item) - && color != self.get_collar_color() - { - self.set_collar_color(color); - item_stack.decrement_unless_creative(player.gamemode.load(), 1); - return true; - } - } else if is_food - && self.mob_entity.living_entity.health.load() - < self.mob_entity.living_entity.get_max_health() + if self.is_tame() { + if self.get_owner_uuid() == Some(player.gameprofile.id) { + if item.has_tag(&tag::Item::MINECRAFT_CAT_COLLAR_DYES) + || item.has_tag(&tag::Item::C_DYES) + { + if let Some(color) = get_dye_color_from_item(item) + && color != self.get_collar_color() { + self.set_collar_color(color); item_stack.decrement_unless_creative(player.gamemode.load(), 1); - self.mob_entity.living_entity.heal(2.0); - self.play_eating_sound(); return true; } - - let parent_interaction = self.mob_entity.mob_interact(player, item_stack); - if !parent_interaction { - self.set_sitting(!self.is_sitting()); - return true; - } - return parent_interaction; - } - } else if is_food { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); - self.play_eating_sound(); - - let mut rng = rand::rng(); - if rng.random_range(0..3) == 0 { - self.set_tame(true, Some(player.gameprofile.id)); - self.set_sitting(true); - self.get_entity().world.load().send_entity_status( - self.get_entity(), - EntityStatus::TamingSucceeded, - Some(ActorEventID::TamingSucceeded), - ); - } else { - self.get_entity().world.load().send_entity_status( - self.get_entity(), - EntityStatus::TamingFailed, - Some(ActorEventID::TamingFailed), - ); + } else if is_food + && self.mob_entity.living_entity.health.load() + < self.mob_entity.living_entity.get_max_health() + { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); + self.mob_entity.living_entity.heal(2.0); + self.play_eating_sound(); + return true; } - return true; + let parent_interaction = self.mob_entity.mob_interact(player, item_stack); + if !parent_interaction { + self.set_sitting(!self.is_sitting()); + return true; + } + return parent_interaction; + } + } else if is_food { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); + self.play_eating_sound(); + + let mut rng = rand::rng(); + if rng.random_range(0..3) == 0 { + self.set_tame(true, Some(player.gameprofile.id)); + self.set_sitting(true); + self.get_entity().world.load().send_entity_status( + self.get_entity(), + EntityStatus::TamingSucceeded, + Some(ActorEventID::TamingSucceeded), + ); + } else { + self.get_entity().world.load().send_entity_status( + self.get_entity(), + EntityStatus::TamingFailed, + Some(ActorEventID::TamingFailed), + ); } - self.mob_entity.mob_interact(player, item_stack) - }) + return true; + } + + self.mob_entity.mob_interact(player, item_stack) } } diff --git a/crates/pumpkin/src/entity/passive/chicken.rs b/crates/pumpkin/src/entity/passive/chicken.rs index cd3f91981..c6147d365 100644 --- a/crates/pumpkin/src/entity/passive/chicken.rs +++ b/crates/pumpkin/src/entity/passive/chicken.rs @@ -10,7 +10,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use rand::RngExt; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::AgeableMob, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -107,34 +107,30 @@ impl Mob for ChickenEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_int("EggLayTime", self.egg_lay_time.load(Ordering::Relaxed)); - let variant_str = match self.variant.load(Ordering::Relaxed) { - 0 => "minecraft:cold", - 2 => "minecraft:warm", - _ => "minecraft:temperate", - }; - nbt.put_string("variant", variant_str.to_string()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("EggLayTime", self.egg_lay_time.load(Ordering::Relaxed)); + let variant_str = match self.variant.load(Ordering::Relaxed) { + 0 => "minecraft:cold", + 2 => "minecraft:warm", + _ => "minecraft:temperate", + }; + nbt.put_string("variant", variant_str.to_string()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - self.egg_lay_time - .store(nbt.get_int("EggLayTime").unwrap_or(6000), Ordering::Relaxed); - if let Some(variant_str) = nbt.get_string("variant") { - let variant = match variant_str - .strip_prefix("minecraft:") - .unwrap_or(variant_str) - { - "cold" => 0, - "warm" => 2, - _ => 1, - }; - self.variant.store(variant, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + self.egg_lay_time + .store(nbt.get_int("EggLayTime").unwrap_or(6000), Ordering::Relaxed); + if let Some(variant_str) = nbt.get_string("variant") { + let variant = match variant_str + .strip_prefix("minecraft:") + .unwrap_or(variant_str) + { + "cold" => 0, + "warm" => 2, + _ => 1, + }; + self.variant.store(variant, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -206,11 +202,7 @@ impl Mob for ChickenEntity { } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { use super::animal::Animal; self.animal_interact(player, item_stack, Sound::EntityChickenAmbient) } diff --git a/crates/pumpkin/src/entity/passive/copper_golem.rs b/crates/pumpkin/src/entity/passive/copper_golem.rs index 0f06230ea..1687b3eb0 100644 --- a/crates/pumpkin/src/entity/passive/copper_golem.rs +++ b/crates/pumpkin/src/entity/passive/copper_golem.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, @@ -203,43 +203,36 @@ impl CopperGolemEntity { } impl Mob for CopperGolemEntity { - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_long( - "next_weather_age", - self.next_weathering_tick.load(Ordering::Relaxed), - ); - nbt.put_int("weather_state", self.get_weather_state().id()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_long( + "next_weather_age", + self.next_weathering_tick.load(Ordering::Relaxed), + ); + nbt.put_int("weather_state", self.get_weather_state().id()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(next) = nbt.get_long("next_weather_age") { - self.next_weathering_tick.store(next, Ordering::Relaxed); - } - if let Some(state) = nbt.get_int("weather_state") { - self.set_weather_state(WeatherState::from_id(state)); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(next) = nbt.get_long("next_weather_age") { + self.next_weathering_tick.store(next, Ordering::Relaxed); + } + if let Some(state) = nbt.get_int("weather_state") { + self.set_weather_state(WeatherState::from_id(state)); + } } fn get_mob_entity(&self) -> &MobEntity { &self.mob_entity } - fn mob_on_lightning_strike<'a>( - &'a self, - caller: &'a dyn EntityBase, - lightning: &'a crate::entity::lightning::LightningBoltEntity, - ) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - self.set_weather_state(WeatherState::Unaffected); - self.mob_entity - .living_entity - .on_lightning_strike(caller, lightning) - .await; - }) + fn mob_on_lightning_strike( + &self, + caller: &dyn EntityBase, + lightning: &crate::entity::lightning::LightningBoltEntity, + ) { + self.set_weather_state(WeatherState::Unaffected); + self.mob_entity + .living_entity + .on_lightning_strike(caller, lightning); } fn mob_init_data_tracker(&self) { @@ -259,46 +252,40 @@ impl Mob for CopperGolemEntity { ); } - fn mob_interact<'a>( - &'a self, - _player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let entity = self.get_entity(); - let world = entity.world.load(); + fn mob_interact(&self, _player: &Arc, item_stack: &mut ItemStack) -> bool { + let entity = self.get_entity(); + let world = entity.world.load(); - // Honeycomb waxing - if item_stack.item.id == Item::HONEYCOMB.id - && self.next_weathering_tick.load(Ordering::Relaxed) != -2 - { - self.next_weathering_tick.store(-2, Ordering::Relaxed); + // Honeycomb waxing + if item_stack.item.id == Item::HONEYCOMB.id + && self.next_weathering_tick.load(Ordering::Relaxed) != -2 + { + self.next_weathering_tick.store(-2, Ordering::Relaxed); + let pos = entity.pos.load(); + world.play_sound(Sound::ItemHoneycombWaxOn, SoundCategory::Blocks, &pos); + return true; + } + + // Axe scraping + if item_stack.item.has_tag(&tag::Item::MINECRAFT_AXES) { + let current_next = self.next_weathering_tick.load(Ordering::Relaxed); + if current_next == -2 { + self.next_weathering_tick.store(-1, Ordering::Relaxed); let pos = entity.pos.load(); - world.play_sound(Sound::ItemHoneycombWaxOn, SoundCategory::Blocks, &pos); + world.play_sound(Sound::ItemAxeScrape, SoundCategory::Blocks, &pos); return true; } - // Axe scraping - if item_stack.item.has_tag(&tag::Item::MINECRAFT_AXES) { - let current_next = self.next_weathering_tick.load(Ordering::Relaxed); - if current_next == -2 { - self.next_weathering_tick.store(-1, Ordering::Relaxed); - let pos = entity.pos.load(); - world.play_sound(Sound::ItemAxeScrape, SoundCategory::Blocks, &pos); - return true; - } - - let weather_state = self.get_weather_state(); - if weather_state != WeatherState::Unaffected { - self.set_weather_state(weather_state.previous()); - self.next_weathering_tick.store(-1, Ordering::Relaxed); - let pos = entity.pos.load(); - world.play_sound(Sound::ItemAxeScrape, SoundCategory::Blocks, &pos); - return true; - } + let weather_state = self.get_weather_state(); + if weather_state != WeatherState::Unaffected { + self.set_weather_state(weather_state.previous()); + self.next_weathering_tick.store(-1, Ordering::Relaxed); + let pos = entity.pos.load(); + world.play_sound(Sound::ItemAxeScrape, SoundCategory::Blocks, &pos); + return true; } + } - false - }) + false } } diff --git a/crates/pumpkin/src/entity/passive/cow.rs b/crates/pumpkin/src/entity/passive/cow.rs index 0d1f0f51e..97b7fa2f5 100644 --- a/crates/pumpkin/src/entity/passive/cow.rs +++ b/crates/pumpkin/src/entity/passive/cow.rs @@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound; use pumpkin_data::{entity::EntityType, item::Item}; use crate::entity::{ - Entity, EntityBaseFuture, + Entity, ageable::AgeableMob, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -93,11 +93,7 @@ impl Mob for CowEntity { &self.mob_entity } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { self.animal_interact(player, item_stack, Sound::EntityCowAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/fox.rs b/crates/pumpkin/src/entity/passive/fox.rs index 50a6e45cc..b686f5c9a 100644 --- a/crates/pumpkin/src/entity/passive/fox.rs +++ b/crates/pumpkin/src/entity/passive/fox.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -248,30 +248,26 @@ impl Mob for FoxEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_string("Type", self.get_variant().as_str().to_string()); - nbt.put_bool("Sleeping", self.is_sleeping()); - nbt.put_bool("Sitting", self.is_sitting()); - nbt.put_bool("Crouching", self.is_crouching()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_string("Type", self.get_variant().as_str().to_string()); + nbt.put_bool("Sleeping", self.is_sleeping()); + nbt.put_bool("Sitting", self.is_sitting()); + nbt.put_bool("Crouching", self.is_crouching()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(variant_str) = nbt.get_string("Type") { - self.set_variant(FoxVariant::from_name(variant_str)); - } - if let Some(sleeping) = nbt.get_bool("Sleeping") { - self.set_sleeping(sleeping); - } - if let Some(sitting) = nbt.get_bool("Sitting") { - self.set_sitting(sitting); - } - if let Some(crouching) = nbt.get_bool("Crouching") { - self.set_crouching(crouching); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(variant_str) = nbt.get_string("Type") { + self.set_variant(FoxVariant::from_name(variant_str)); + } + if let Some(sleeping) = nbt.get_bool("Sleeping") { + self.set_sleeping(sleeping); + } + if let Some(sitting) = nbt.get_bool("Sitting") { + self.set_sitting(sitting); + } + if let Some(crouching) = nbt.get_bool("Crouching") { + self.set_crouching(crouching); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -314,14 +310,7 @@ impl Mob for FoxEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - self.animal_interact(player, item_stack, Sound::EntityFoxAmbient) - .await - }) + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + self.animal_interact(player, item_stack, Sound::EntityFoxAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/frog.rs b/crates/pumpkin/src/entity/passive/frog.rs index c4dd9cd34..6ea373a79 100644 --- a/crates/pumpkin/src/entity/passive/frog.rs +++ b/crates/pumpkin/src/entity/passive/frog.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, @@ -154,18 +154,14 @@ impl Mob for FrogEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_string("variant", self.get_variant().as_str().to_string()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_string("variant", self.get_variant().as_str().to_string()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(variant_str) = nbt.get_string("variant") { - self.set_variant(FrogVariant::from_name(variant_str)); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(variant_str) = nbt.get_string("variant") { + self.set_variant(FrogVariant::from_name(variant_str)); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -201,14 +197,7 @@ impl Mob for FrogEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - self.animal_interact(player, item_stack, Sound::EntityFrogAmbient) - .await - }) + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + self.animal_interact(player, item_stack, Sound::EntityFrogAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/happy_ghast.rs b/crates/pumpkin/src/entity/passive/happy_ghast.rs index 02920f1cc..aba9fceb7 100644 --- a/crates/pumpkin/src/entity/passive/happy_ghast.rs +++ b/crates/pumpkin/src/entity/passive/happy_ghast.rs @@ -12,7 +12,7 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, @@ -136,21 +136,17 @@ impl Mob for HappyGhastEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int( - "still_timeout", - self.server_still_timeout.load(Ordering::Relaxed), - ); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int( + "still_timeout", + self.server_still_timeout.load(Ordering::Relaxed), + ); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(timeout) = nbt.get_int("still_timeout") { - self.set_server_still_timeout(timeout); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(timeout) = nbt.get_int("still_timeout") { + self.set_server_still_timeout(timeout); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -219,20 +215,11 @@ impl Mob for HappyGhastEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_baby() { - return self - .animal_interact(player, item_stack, Sound::EntityGhastlingAmbient) - .await; - } + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if self.is_baby() { + return self.animal_interact(player, item_stack, Sound::EntityGhastlingAmbient); + } - self.animal_interact(player, item_stack, Sound::EntityHappyGhastAmbient) - .await - }) + self.animal_interact(player, item_stack, Sound::EntityHappyGhastAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/iron_golem.rs b/crates/pumpkin/src/entity/passive/iron_golem.rs index 699040a01..86cd7b81a 100644 --- a/crates/pumpkin/src/entity/passive/iron_golem.rs +++ b/crates/pumpkin/src/entity/passive/iron_golem.rs @@ -12,7 +12,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::GameMode; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, @@ -122,18 +122,14 @@ impl Mob for IronGolemEntity { fn as_iron_golem(&self) -> Option<&IronGolemEntity> { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_bool("PlayerCreated", self.is_player_created()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("PlayerCreated", self.is_player_created()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(created) = nbt.get_bool("PlayerCreated") { - self.set_player_created(created); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(created) = nbt.get_bool("PlayerCreated") { + self.set_player_created(created); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -164,29 +160,23 @@ impl Mob for IronGolemEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if item_stack.item.id == Item::IRON_INGOT.id { - let living = &self.mob_entity.living_entity; - let current_health = living.health.load(); - let max_health = living.get_max_health(); - if current_health < max_health { - living.set_health((current_health + 25.0).min(max_health)); - let entity = self.get_entity(); - let world = entity.world.load(); - let pos = entity.pos.load(); - world.play_sound(Sound::EntityIronGolemRepair, SoundCategory::Neutral, &pos); - if player.gamemode.load() != GameMode::Creative { - item_stack.item_count = item_stack.item_count.saturating_sub(1); - } - return true; + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if item_stack.item.id == Item::IRON_INGOT.id { + let living = &self.mob_entity.living_entity; + let current_health = living.health.load(); + let max_health = living.get_max_health(); + if current_health < max_health { + living.set_health((current_health + 25.0).min(max_health)); + let entity = self.get_entity(); + let world = entity.world.load(); + let pos = entity.pos.load(); + world.play_sound(Sound::EntityIronGolemRepair, SoundCategory::Neutral, &pos); + if player.gamemode.load() != GameMode::Creative { + item_stack.item_count = item_stack.item_count.saturating_sub(1); } + return true; } - false - }) + } + false } } diff --git a/crates/pumpkin/src/entity/passive/nautilus.rs b/crates/pumpkin/src/entity/passive/nautilus.rs index 890d916a9..da99dd9b5 100644 --- a/crates/pumpkin/src/entity/passive/nautilus.rs +++ b/crates/pumpkin/src/entity/passive/nautilus.rs @@ -1,9 +1,8 @@ use crossbeam::atomic::AtomicCell; use std::sync::{ - Arc, + Arc, Mutex, atomic::{AtomicBool, AtomicI32, Ordering}, }; -use tokio::sync::Mutex; use uuid::Uuid; use pumpkin_data::{ @@ -19,7 +18,7 @@ use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, mob::{Mob, MobEntity}, passive::animal::Animal, player::Player, @@ -230,32 +229,28 @@ impl Mob for NautilusEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_bool("IsTame", self.is_tame.load(Ordering::Relaxed)); - nbt.put_bool("Saddled", self.is_saddled.load(Ordering::Relaxed)); - nbt.put_int("DashCooldown", self.dash_cooldown.load(Ordering::Relaxed)); - if let Some(owner) = self.owner.load() { - nbt.put_uuid("Owner", owner); - } - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("IsTame", self.is_tame.load(Ordering::Relaxed)); + nbt.put_bool("Saddled", self.is_saddled.load(Ordering::Relaxed)); + nbt.put_int("DashCooldown", self.dash_cooldown.load(Ordering::Relaxed)); + if let Some(owner) = self.owner.load() { + nbt.put_uuid("Owner", owner); + } } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(is_tame) = nbt.get_bool("IsTame") { - self.is_tame.store(is_tame, Ordering::Relaxed); - } - if let Some(saddled) = nbt.get_bool("Saddled") { - self.is_saddled.store(saddled, Ordering::Relaxed); - } - if let Some(dash) = nbt.get_int("DashCooldown") { - self.dash_cooldown.store(dash, Ordering::Relaxed); - } - if let Some(owner) = nbt.get_uuid("Owner") { - self.owner.store(Some(owner)); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(is_tame) = nbt.get_bool("IsTame") { + self.is_tame.store(is_tame, Ordering::Relaxed); + } + if let Some(saddled) = nbt.get_bool("Saddled") { + self.is_saddled.store(saddled, Ordering::Relaxed); + } + if let Some(dash) = nbt.get_int("DashCooldown") { + self.dash_cooldown.store(dash, Ordering::Relaxed); + } + if let Some(owner) = nbt.get_uuid("Owner") { + self.owner.store(Some(owner)); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -335,63 +330,54 @@ impl Mob for NautilusEntity { } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let mob_entity = &self.mob_entity; - let entity = &mob_entity.living_entity.entity; + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + let mob_entity = &self.mob_entity; + let entity = &mob_entity.living_entity.entity; - if !self.is_tame() && self.is_food(item_stack) { + if !self.is_tame() && self.is_food(item_stack) { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); + if rand::random::().is_multiple_of(3) { + self.set_tame(true, Some(player.gameprofile.id)); + let world = entity.world.load(); + world.send_entity_status(entity, EntityStatus::TamingSucceeded, None); + } else { + let world = entity.world.load(); + world.send_entity_status(entity, EntityStatus::TamingFailed, None); + } + let world = entity.world.load(); + world.play_sound( + self.get_eat_sound(), + SoundCategory::Neutral, + &entity.pos.load(), + ); + return true; + } + + if self.is_tame() && !player.get_entity().is_sneaking() { + if !self.is_saddled.load(Ordering::Relaxed) + && item_stack.item == &pumpkin_data::item::Item::SADDLE + { item_stack.decrement_unless_creative(player.gamemode.load(), 1); - if rand::random::().is_multiple_of(3) { - self.set_tame(true, Some(player.gameprofile.id)); - let world = entity.world.load(); - world.send_entity_status(entity, EntityStatus::TamingSucceeded, None); - } else { - let world = entity.world.load(); - world.send_entity_status(entity, EntityStatus::TamingFailed, None); - } + self.is_saddled.store(true, Ordering::Relaxed); let world = entity.world.load(); world.play_sound( - self.get_eat_sound(), + Sound::ItemNautilusSaddleEquip, SoundCategory::Neutral, &entity.pos.load(), ); return true; } - if self.is_tame() && !player.get_entity().is_sneaking() { - if !self.is_saddled.load(Ordering::Relaxed) - && item_stack.item == &pumpkin_data::item::Item::SADDLE - { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); - self.is_saddled.store(true, Ordering::Relaxed); - let world = entity.world.load(); - world.play_sound( - Sound::ItemNautilusSaddleEquip, - SoundCategory::Neutral, - &entity.pos.load(), - ); - return true; - } - - let world = player.world(); - if let Some(vehicle) = world.get_entity_by_id(entity.entity_id) - && let Some(passenger) = world.get_player_by_id(player.entity_id()) - { - entity - .add_passenger(vehicle, passenger as Arc) - .await; - return true; - } + let world = player.world(); + if let Some(vehicle) = world.get_entity_by_id(entity.entity_id) + && let Some(passenger) = world.get_player_by_id(player.entity_id()) + { + entity.add_passenger(vehicle, passenger as Arc); + return true; } + } - self.animal_interact(player, item_stack, self.get_ambient_sound()) - .await - }) + self.animal_interact(player, item_stack, self.get_ambient_sound()) } fn is_saddled(&self) -> bool { diff --git a/crates/pumpkin/src/entity/passive/ocelot.rs b/crates/pumpkin/src/entity/passive/ocelot.rs index f301fa00a..95e1026c1 100644 --- a/crates/pumpkin/src/entity/passive/ocelot.rs +++ b/crates/pumpkin/src/entity/passive/ocelot.rs @@ -13,7 +13,7 @@ use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ai::goal::{ active_target::ActiveTargetGoal, avoid_entity::AvoidEntityGoal, breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -133,18 +133,14 @@ impl Mob for OcelotEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_bool("Trusting", self.is_trusting.load(Ordering::Relaxed)); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("Trusting", self.is_trusting.load(Ordering::Relaxed)); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(trusting) = nbt.get_bool("Trusting") { - self.is_trusting.store(trusting, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(trusting) = nbt.get_bool("Trusting") { + self.is_trusting.store(trusting, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -172,47 +168,40 @@ impl Mob for OcelotEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - let is_food = self.is_food(item_stack); - let dist_sqr = self - .get_entity() - .pos - .load() - .squared_distance_to_vec(&player.get_entity().pos.load()); + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + let is_food = self.is_food(item_stack); + let dist_sqr = self + .get_entity() + .pos + .load() + .squared_distance_to_vec(&player.get_entity().pos.load()); - if !self.is_trusting() && is_food && dist_sqr < 9.0 { - item_stack.decrement_unless_creative(player.gamemode.load(), 1); + if !self.is_trusting() && is_food && dist_sqr < 9.0 { + item_stack.decrement_unless_creative(player.gamemode.load(), 1); - let mut rng = rand::rng(); - if rng.random_range(0..3) == 0 { - self.set_trusting(true); - self.get_entity().world.load().send_entity_status( - self.get_entity(), - EntityStatus::TrustingSucceeded, - Some(ActorEventID::TamingSucceeded), - ); - } else { - self.get_entity().world.load().send_entity_status( - self.get_entity(), - EntityStatus::TrustingFailed, - Some(ActorEventID::TamingFailed), - ); - } - - return true; + let mut rng = rand::rng(); + if rng.random_range(0..3) == 0 { + self.set_trusting(true); + self.get_entity().world.load().send_entity_status( + self.get_entity(), + EntityStatus::TrustingSucceeded, + Some(ActorEventID::TamingSucceeded), + ); + } else { + self.get_entity().world.load().send_entity_status( + self.get_entity(), + EntityStatus::TrustingFailed, + Some(ActorEventID::TamingFailed), + ); } - self.animal_interact( - player, - item_stack, - pumpkin_data::sound::Sound::EntityOcelotAmbient, - ) - .await - }) + return true; + } + + self.animal_interact( + player, + item_stack, + pumpkin_data::sound::Sound::EntityOcelotAmbient, + ) } } diff --git a/crates/pumpkin/src/entity/passive/parrot.rs b/crates/pumpkin/src/entity/passive/parrot.rs index 5649256af..4003af230 100644 --- a/crates/pumpkin/src/entity/passive/parrot.rs +++ b/crates/pumpkin/src/entity/passive/parrot.rs @@ -7,7 +7,7 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::{self, Taggable}; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, + Entity, EntityBase, ai::goal::{ look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, wander_around::WanderAroundGoal, @@ -92,25 +92,19 @@ impl Mob for ParrotEntity { &self.mob_entity } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - // Vanilla checks the poisonous food tag last, after taming, which isn't - // implemented yet. Nothing in `parrot_food` is also in - // `parrot_poisonous_food`, so the two branches can't be confused. - if !item_stack - .get_item() - .has_tag(&tag::Item::MINECRAFT_PARROT_POISONOUS_FOOD) - { - return self.mob_entity.mob_interact(player, item_stack); - } + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + // Vanilla checks the poisonous food tag last, after taming, which isn't + // implemented yet. Nothing in `parrot_food` is also in + // `parrot_poisonous_food`, so the two branches can't be confused. + if !item_stack + .get_item() + .has_tag(&tag::Item::MINECRAFT_PARROT_POISONOUS_FOOD) + { + return self.mob_entity.mob_interact(player, item_stack); + } - self.eat_cookie(player, item_stack); - true - }) + self.eat_cookie(player, item_stack); + true } } diff --git a/crates/pumpkin/src/entity/passive/pig.rs b/crates/pumpkin/src/entity/passive/pig.rs index f3464938a..4e9449fd7 100644 --- a/crates/pumpkin/src/entity/passive/pig.rs +++ b/crates/pumpkin/src/entity/passive/pig.rs @@ -5,7 +5,7 @@ use pumpkin_data::sound::Sound; use pumpkin_data::{entity::EntityType, item::Item}; use crate::entity::{ - Entity, EntityBaseFuture, NbtFuture, + Entity, ageable::AgeableMob, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, @@ -102,18 +102,14 @@ impl Mob for PigEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_bool("Saddle", self.is_saddled()); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("Saddle", self.is_saddled()); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(saddle) = nbt.get_byte("Saddle") { - self.set_saddled(saddle == 1); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(saddle) = nbt.get_byte("Saddle") { + self.set_saddled(saddle == 1); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -138,27 +134,19 @@ impl Mob for PigEntity { .store(saddled, std::sync::atomic::Ordering::Relaxed); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { use super::animal::Animal; - Box::pin(async move { - if self.is_saddled() && !self.is_food(item_stack) { - let world = player.world(); - if let Some(vehicle) = world.get_entity_by_id(self.get_entity().entity_id) - && let Some(passenger) = world.get_player_by_id(player.entity_id()) - { - self.get_entity() - .add_passenger(vehicle, passenger as Arc) - .await; - return true; - } + if self.is_saddled() && !self.is_food(item_stack) { + let world = player.world(); + if let Some(vehicle) = world.get_entity_by_id(self.get_entity().entity_id) + && let Some(passenger) = world.get_player_by_id(player.entity_id()) + { + self.get_entity() + .add_passenger(vehicle, passenger as Arc); + return true; } - self.animal_interact(player, item_stack, Sound::EntityPigAmbient) - .await - }) + } + self.animal_interact(player, item_stack, Sound::EntityPigAmbient) } } diff --git a/crates/pumpkin/src/entity/passive/sheep.rs b/crates/pumpkin/src/entity/passive/sheep.rs index c41b694ec..727c0a820 100644 --- a/crates/pumpkin/src/entity/passive/sheep.rs +++ b/crates/pumpkin/src/entity/passive/sheep.rs @@ -8,7 +8,7 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBaseFuture, NbtFuture, + Entity, ageable::AgeableMob, ai::goal::{ breed::BreedGoal, eat_grass::EatGrassGoal, escape_danger::EscapeDangerGoal, @@ -133,23 +133,19 @@ impl Mob for SheepEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - nbt.put_bool("Sheared", self.is_sheared()); - nbt.put_byte("Color", self.get_color() as i8); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("Sheared", self.is_sheared()); + nbt.put_byte("Color", self.get_color() as i8); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let sheared = nbt - .get_bool("Sheared") - .or_else(|| nbt.get_byte("Sheared").map(|b| b == 1)) - .unwrap_or(false); - let color = nbt.get_byte("Color").unwrap_or(0) as u8; - let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 }; - self.color_and_sheared.store(byte, Ordering::Relaxed); - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + let sheared = nbt + .get_bool("Sheared") + .or_else(|| nbt.get_byte("Sheared").map(|b| b == 1)) + .unwrap_or(false); + let color = nbt.get_byte("Color").unwrap_or(0) as u8; + let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 }; + self.color_and_sheared.store(byte, Ordering::Relaxed); } fn get_mob_entity(&self) -> &MobEntity { @@ -164,11 +160,7 @@ impl Mob for SheepEntity { Some(self) } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { use super::animal::Animal; self.animal_interact(player, item_stack, Sound::EntitySheepAmbient) } diff --git a/crates/pumpkin/src/entity/passive/sniffer.rs b/crates/pumpkin/src/entity/passive/sniffer.rs index 5f9357823..547643d83 100644 --- a/crates/pumpkin/src/entity/passive/sniffer.rs +++ b/crates/pumpkin/src/entity/passive/sniffer.rs @@ -15,7 +15,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, + Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ breed::BreedGoal, follow_parent::FollowParentGoal, look_around::RandomLookAroundGoal, @@ -336,25 +336,21 @@ impl Mob for SnifferEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("State", self.get_state().id()); - nbt.put_int( - "DropSeedAtTick", - self.drop_seed_at_tick.load(Ordering::Relaxed), - ); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("State", self.get_state().id()); + nbt.put_int( + "DropSeedAtTick", + self.drop_seed_at_tick.load(Ordering::Relaxed), + ); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(state_id) = nbt.get_int("State") { - self.state.store(state_id, Ordering::Relaxed); - } - if let Some(drop_tick) = nbt.get_int("DropSeedAtTick") { - self.drop_seed_at_tick.store(drop_tick, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(state_id) = nbt.get_int("State") { + self.state.store(state_id, Ordering::Relaxed); + } + if let Some(drop_tick) = nbt.get_int("DropSeedAtTick") { + self.drop_seed_at_tick.store(drop_tick, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -411,14 +407,7 @@ impl Mob for SnifferEntity { ); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - self.animal_interact(player, item_stack, Sound::EntitySnifferEat) - .await - }) + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + self.animal_interact(player, item_stack, Sound::EntitySnifferEat) } } diff --git a/crates/pumpkin/src/entity/passive/strider.rs b/crates/pumpkin/src/entity/passive/strider.rs index 7ada05478..46af7efad 100644 --- a/crates/pumpkin/src/entity/passive/strider.rs +++ b/crates/pumpkin/src/entity/passive/strider.rs @@ -54,24 +54,14 @@ impl StriderEntity { } impl Mob for StriderEntity { - fn mob_write_nbt<'a>( - &'a self, - nbt: &'a mut pumpkin_nbt::compound::NbtCompound, - ) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_bool("Saddle", self.is_saddled()); - }) + fn mob_write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { + nbt.put_bool("Saddle", self.is_saddled()); } - fn mob_read_nbt<'a>( - &'a self, - nbt: &'a pumpkin_nbt::compound::NbtCompound, - ) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(saddle) = nbt.get_byte("Saddle") { - self.set_saddled(saddle == 1); - } - }) + fn mob_read_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) { + if let Some(saddle) = nbt.get_byte("Saddle") { + self.set_saddled(saddle == 1); + } } fn get_mob_entity(&self) -> &MobEntity { @@ -95,25 +85,22 @@ impl Mob for StriderEntity { .store(saddled, std::sync::atomic::Ordering::Relaxed); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - _item_stack: &'a mut pumpkin_data::item_stack::ItemStack, - ) -> crate::entity::EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_saddled() { - let world = player.world(); - let ent = &self.mob_entity.living_entity.entity; - if let Some(vehicle) = world.get_entity_by_id(ent.entity_id) - && let Some(passenger) = world.get_player_by_id(player.entity_id()) - { - ent.add_passenger(vehicle, passenger as Arc) - .await; - return true; - } + fn mob_interact( + &self, + player: &Arc, + _item_stack: &mut pumpkin_data::item_stack::ItemStack, + ) -> bool { + if self.is_saddled() { + let world = player.world(); + let ent = &self.mob_entity.living_entity.entity; + if let Some(vehicle) = world.get_entity_by_id(ent.entity_id) + && let Some(passenger) = world.get_player_by_id(player.entity_id()) + { + ent.add_passenger(vehicle, passenger as Arc); + return true; } - false - }) + } + false } } diff --git a/crates/pumpkin/src/entity/passive/villager/mod.rs b/crates/pumpkin/src/entity/passive/villager/mod.rs index 2de0c88a1..a4ba3abc1 100644 --- a/crates/pumpkin/src/entity/passive/villager/mod.rs +++ b/crates/pumpkin/src/entity/passive/villager/mod.rs @@ -265,10 +265,10 @@ pub struct VillagerEntity { pub restocks_today: AtomicI32, pub last_gossip_decay_time: AtomicI64, pub last_gossip_share_time: AtomicI64, - pub gossips: Mutex>>, - pub inventory: Arc>>>>, + pub gossips: std::sync::Mutex>>, + pub inventory: std::sync::Mutex>, pub merchant_inventory: Arc, - pub offers: Mutex>, + pub offers: std::sync::Mutex>, pub merchant_update_timer: AtomicI32, pub unhappy_counter: AtomicI32, pub trade_sound_cooldown: AtomicI32, @@ -321,11 +321,7 @@ impl VillagerEntity { pub fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let villager_data = VillagerData::new(VillagerType::Plains, VillagerProfession::None, 1); - let inventory = Arc::new(Mutex::new( - (0..8) - .map(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))) - .collect(), - )); + let inventory = std::sync::Mutex::new((0..8).map(|_| ItemStack::EMPTY.clone()).collect()); let villager = Self { mob_entity, @@ -338,10 +334,10 @@ impl VillagerEntity { restocks_today: AtomicI32::new(0), last_gossip_decay_time: AtomicI64::new(0), last_gossip_share_time: AtomicI64::new(0), - gossips: Mutex::new(HashMap::new()), + gossips: std::sync::Mutex::new(HashMap::new()), inventory, merchant_inventory: Arc::new(SimpleInventory::new(3)), - offers: Mutex::new(Vec::new()), + offers: std::sync::Mutex::new(Vec::new()), merchant_update_timer: AtomicI32::new(0), unhappy_counter: AtomicI32::new(0), trade_sound_cooldown: AtomicI32::new(0), @@ -449,11 +445,13 @@ impl VillagerEntity { mob_arc } - pub async fn count_food_points_in_inventory(&self) -> i32 { - let inventory = self.inventory.lock().await; + pub fn count_food_points_in_inventory(&self) -> i32 { + let inventory = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut total = 0; - for stack_mutex in inventory.iter() { - let stack = stack_mutex.lock().await; + for stack in inventory.iter() { if !stack.is_empty() { total += get_food_points(stack.get_item()) * stack.item_count as i32; } @@ -471,13 +469,15 @@ impl VillagerEntity { Some(owner) } - pub async fn eat_until_full(&self) { + pub fn eat_until_full(&self) { if self.food_level.load(Ordering::Relaxed) >= BREEDING_FOOD_THRESHOLD { return; } - let inventory = self.inventory.lock().await; - for stack_mutex in inventory.iter() { - let mut stack = stack_mutex.lock().await; + let mut inventory = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for stack in inventory.iter_mut() { if !stack.is_empty() { let points = get_food_points(stack.get_item()); if points > 0 { @@ -515,12 +515,15 @@ impl VillagerEntity { ); if old_profession != data.profession { - self.offers.lock().await.clear(); + self.offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clear(); } } #[expect(clippy::too_many_lines)] - async fn create_explorer_map(&self, destination: &str) -> Option { + fn create_explorer_map(&self, destination: &str) -> Option { use pumpkin_data::data_component::DataComponent; use pumpkin_data::data_component_impl::{DataComponentImpl, ItemNameImpl, MapIdImpl}; use pumpkin_data::structures::{StructureKeys, StructureSet}; @@ -608,8 +611,7 @@ impl VillagerEntity { target.0.z, 2, ); - map.lock() - .await + map.blocking_lock() .decorations .push(crate::world::map::MapDecoration { icon_type, @@ -631,7 +633,7 @@ impl VillagerEntity { Some(stack) } - pub async fn add_trades(&self, profession: VillagerProfession, level: i32) { + pub fn add_trades(&self, profession: VillagerProfession, level: i32) { use pumpkin_data::villager::VillagerTradeModifier; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; use rand::seq::IndexedRandom; @@ -643,7 +645,7 @@ impl VillagerEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .type_enum(); - let mut offers = self.offers.lock().await; + let mut new_offers = Vec::new(); if let Some(trade_set) = profession.trade_set(level) { let mut rng = StdRng::from_rng(&mut rand::rng()); @@ -687,7 +689,7 @@ impl VillagerEntity { base_cost_a.set_count(count as u8); } VillagerTradeModifier::ExplorationMap { destination } => { - let Some(map) = self.create_explorer_map(destination).await else { + let Some(map) = self.create_explorer_map(destination) else { continue; }; output = map; @@ -707,7 +709,7 @@ impl VillagerEntity { } VillagerTradeModifier::Potion(potion) => apply_potion(&mut output, potion), } - offers.push(pumpkin_protocol::java::client::play::MerchantOffer { + new_offers.push(pumpkin_protocol::java::client::play::MerchantOffer { base_cost_a: ItemStackSerializer(Cow::Owned(base_cost_a)), output: ItemStackSerializer(Cow::Owned(output)), cost_b: cost_b.map(|stack| ItemStackSerializer(Cow::Owned(stack))), @@ -722,11 +724,19 @@ impl VillagerEntity { added += 1; } } + + self.offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .extend(new_offers); } - pub async fn generate_trades(&self, profession: VillagerProfession, level: i32) { - self.offers.lock().await.clear(); - self.add_trades(profession, level).await; + pub fn generate_trades(&self, profession: VillagerProfession, level: i32) { + self.offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clear(); + self.add_trades(profession, level); } async fn update_special_prices(&self, player: &Player) { @@ -734,7 +744,7 @@ impl VillagerEntity { let reputation = self .gossips .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .get(&player_uuid) .map_or(0, |gossips| { gossips @@ -747,7 +757,10 @@ impl VillagerEntity { .get_effect(&StatusEffect::HERO_OF_THE_VILLAGE) .map(|effect| i32::from(effect.amplifier)); - let mut offers = self.offers.lock().await; + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for offer in offers.iter_mut() { offer.special_price = -((reputation as f32 * offer.price_multiplier).floor() as i32); if let Some(amplifier) = hero_amplifier { @@ -760,7 +773,12 @@ impl VillagerEntity { } async fn reset_special_prices(&self) { - for offer in self.offers.lock().await.iter_mut() { + for offer in self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter_mut() + { offer.special_price = 0; } } @@ -796,7 +814,10 @@ impl VillagerEntity { async fn complete_trade(&self, offer_index: usize, world: &Arc, player_uuid: Uuid) { let (xp_gain, reward_exp) = { - let mut offers = self.offers.lock().await; + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let Some(offer) = offers.get_mut(offer_index) else { return; }; @@ -817,44 +838,65 @@ impl VillagerEntity { )], Some(&bedrock_metadata), ); - let mut reward_xp = { - use rand::RngExt; - rand::rng().random_range(3..=6) - }; - let current_level = villager_data.level.0; - if current_level < 5 { - let max_xp = match current_level { - 1 => 10, - 2 => 70, - 3 => 150, - 4 => 250, - _ => 0, - }; - if current_xp >= max_xp { - self.merchant_update_timer.store(40, Ordering::Relaxed); - self.increase_profession_level_on_update - .store(true, Ordering::Relaxed); - reward_xp += 5; - } - } - self.get_entity() - .play_sound(pumpkin_data::sound::Sound::EntityVillagerYes); - self.trade_sound_cooldown.store(20, Ordering::Relaxed); - *self - .last_traded_player - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(player_uuid); if reward_exp { - let position = self.get_entity().pos.load().add_raw(0.0, 0.5, 0.0); - ExperienceOrbEntity::spawn(world, position, reward_xp); + ExperienceOrbEntity::spawn(&world, self.get_entity().pos.load(), xp_gain as u32); } if let Some(player) = world.get_player_by_uuid(player_uuid) { - trigger_trade_advancement(&player); + { + let mut gossips = self + .gossips + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let value = gossips + .entry(player_uuid) + .or_default() + .entry(GossipType::Trading) + .or_default(); + *value = (*value + 2).min(GossipType::Trading.max_value()); + } + self.resend_offers_to_player(&player).await; } } + async fn resend_offers_to_player(&self, player: &Arc) { + let trading_player = *self + .trading_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let Some((player_uuid, sync_id)) = trading_player else { + return; + }; + if player.get_entity().entity_uuid != player_uuid { + return; + } + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let villager_data = *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + let screen = player.current_screen_handler.lock().await.clone(); + let mut screen = screen.lock().await; + if screen.sync_id() != sync_id { + return; + } + if let Some(handler) = screen.as_any_mut().downcast_mut::() { + handler.offers.clone_from(&offers); + handler.update_result_slot().await; + } else { + return; + } + drop(screen); + self.send_trade_offers(player, sync_id, offers, villager_data) + .await; + } + async fn resend_offers_to_trading_player(&self) { let trading_player = *self .trading_player @@ -867,7 +909,11 @@ impl VillagerEntity { let Some(player) = world.get_player_by_uuid(player_uuid) else { return; }; - let offers = self.offers.lock().await.clone(); + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let villager_data = *self .villager_data .lock() @@ -900,7 +946,10 @@ impl VillagerEntity { return; } - let mut gossips = self.gossips.lock().await; + let mut gossips = self + .gossips + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for values in gossips.values_mut() { values.retain(|kind, value| { *value -= kind.daily_decay(); @@ -947,18 +996,22 @@ impl VillagerEntity { let last_check_day = self.last_restock_check_day.swap(day, Ordering::Relaxed); if game_time > last_restock + 12_000 || (last_check_day > 0 && day > last_check_day) { let missed_restock_count = (2 - self.restocks_today.load(Ordering::Relaxed)).max(0); - let mut offers = self.offers.lock().await; - if missed_restock_count > 0 { - for offer in offers.iter_mut() { - offer.reset_uses(); + { + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if missed_restock_count > 0 { + for offer in offers.iter_mut() { + offer.reset_uses(); + } + } + for _ in 0..missed_restock_count { + for offer in offers.iter_mut() { + offer.update_demand(); + } } } - for _ in 0..missed_restock_count { - for offer in offers.iter_mut() { - offer.update_demand(); - } - } - drop(offers); self.last_restock_time.store(game_time, Ordering::Relaxed); self.restocks_today.store(0, Ordering::Relaxed); self.resend_offers_to_trading_player().await; @@ -972,20 +1025,24 @@ impl VillagerEntity { return; } - let mut offers = self.offers.lock().await; - if !offers - .iter() - .any(pumpkin_protocol::java::client::play::MerchantOffer::needs_restock) { - return; - } - for offer in offers.iter_mut() { - offer.update_demand(); - offer.reset_uses(); + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !offers + .iter() + .any(pumpkin_protocol::java::client::play::MerchantOffer::needs_restock) + { + return; + } + for offer in offers.iter_mut() { + offer.update_demand(); + offer.reset_uses(); + } } self.last_restock_time.store(game_time, Ordering::Relaxed); self.restocks_today.fetch_add(1, Ordering::Relaxed); - drop(offers); self.resend_offers_to_trading_player().await; } @@ -1204,7 +1261,11 @@ impl VillagerEntity { pub async fn open_trading_screen(&self, player: &Arc) { // Open the merchant screen and then send the current offers packet if let Some(sync_id) = player.open_handled_screen(self, None).await { - let offers = self.offers.lock().await.clone(); + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let villager_data = *self .villager_data .lock() @@ -1375,14 +1436,18 @@ impl ScreenHandlerFactory for VillagerEntity { if let Some(player) = server_player { self.update_special_prices(player).await; } - let offers = self.offers.lock().await; + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let world = self.get_entity().world.load().clone(); let mut handler = MerchantScreenHandler::new( sync_id, player_inventory, self.merchant_inventory.clone(), - offers.clone(), + offers, ) .await; @@ -1484,7 +1549,10 @@ impl VillagerEntity { .unwrap_or_else(std::sync::PoisonError::into_inner) .take(); if let Some(player_uuid) = last_traded_player { - let mut gossips = self.gossips.lock().await; + let mut gossips = self + .gossips + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let value = gossips .entry(player_uuid) .or_default() @@ -1513,7 +1581,7 @@ impl VillagerEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); data.level.0 += 1; self.set_villager_data(data).await; - self.add_trades(data.profession_enum(), data.level.0).await; + self.add_trades(data.profession_enum(), data.level.0); } self.mob_entity.living_entity.add_effect(Effect { effect_type: &StatusEffect::REGENERATION, @@ -1771,295 +1839,305 @@ impl VillagerEntity { impl Mob for VillagerEntity { #[expect(clippy::too_many_lines)] - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - { - let data = self - .villager_data - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut villager_data_nbt = NbtCompound::new(); - villager_data_nbt.put_int("Type", data.r#type.0); - villager_data_nbt.put_int("Profession", data.profession.0); - villager_data_nbt.put_int("Level", data.level.0); - nbt.put_compound("VillagerData", villager_data_nbt); - }; + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + { + let data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut villager_data_nbt = NbtCompound::new(); + villager_data_nbt.put_int("Type", data.r#type.0); + villager_data_nbt.put_int("Profession", data.profession.0); + villager_data_nbt.put_int("Level", data.level.0); + nbt.put_compound("VillagerData", villager_data_nbt); + }; - nbt.put_int("FoodLevel", self.food_level.load(Ordering::Relaxed)); - nbt.put_int("Xp", self.xp.load(Ordering::Relaxed)); - nbt.put_long( - "LastRestock", - self.last_restock_time.load(Ordering::Relaxed), - ); - nbt.put_int("RestocksToday", self.restocks_today.load(Ordering::Relaxed)); - nbt.put_long( - "LastGossipDecay", - self.last_gossip_decay_time.load(Ordering::Relaxed), + nbt.put_int("FoodLevel", self.food_level.load(Ordering::Relaxed)); + nbt.put_int("Xp", self.xp.load(Ordering::Relaxed)); + nbt.put_long( + "LastRestock", + self.last_restock_time.load(Ordering::Relaxed), + ); + nbt.put_int("RestocksToday", self.restocks_today.load(Ordering::Relaxed)); + nbt.put_long( + "LastGossipDecay", + self.last_gossip_decay_time.load(Ordering::Relaxed), + ); + + let last_gossip_share = self.last_gossip_share_time.load(Ordering::Relaxed); + if last_gossip_share > 0 { + nbt.put_long("LastGossipShare", last_gossip_share); + } + + let last_worked = self.last_worked_at_poi.load(Ordering::Relaxed); + if last_worked > 0 { + nbt.put_long("LastWorkedAtPoi", last_worked); + } + + if let Some(pos) = self.get_job_site() { + nbt.put_int("JobSiteX", pos.0.x); + nbt.put_int("JobSiteY", pos.0.y); + nbt.put_int("JobSiteZ", pos.0.z); + nbt.put_bool( + "JobSitePending", + self.job_site_pending.load(Ordering::Relaxed), ); + } - let last_gossip_share = self.last_gossip_share_time.load(Ordering::Relaxed); - if last_gossip_share > 0 { - nbt.put_long("LastGossipShare", last_gossip_share); - } + if let Some(pos) = self.get_home() { + nbt.put_int("HomeX", pos.0.x); + nbt.put_int("HomeY", pos.0.y); + nbt.put_int("HomeZ", pos.0.z); + } - let last_worked = self.last_worked_at_poi.load(Ordering::Relaxed); - if last_worked > 0 { - nbt.put_long("LastWorkedAtPoi", last_worked); - } + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !offers.is_empty() { + let mut recipes = Vec::new(); + for offer in offers.iter() { + let mut recipe = NbtCompound::new(); + let mut buy = NbtCompound::new(); + let mut sell = NbtCompound::new(); - if let Some(pos) = self.get_job_site() { - nbt.put_int("JobSiteX", pos.0.x); - nbt.put_int("JobSiteY", pos.0.y); - nbt.put_int("JobSiteZ", pos.0.z); - nbt.put_bool( - "JobSitePending", - self.job_site_pending.load(Ordering::Relaxed), - ); - } + let item_stack: &ItemStack = offer.base_cost_a.0.as_ref(); + item_stack.write_item_stack(&mut buy); + recipe.put_compound("buy", buy); - if let Some(pos) = self.get_home() { - nbt.put_int("HomeX", pos.0.x); - nbt.put_int("HomeY", pos.0.y); - nbt.put_int("HomeZ", pos.0.z); - } + let item_stack: &ItemStack = offer.output.0.as_ref(); + item_stack.write_item_stack(&mut sell); + recipe.put_compound("sell", sell); - let offers = self.offers.lock().await; - if !offers.is_empty() { - let mut recipes = Vec::new(); - for offer in offers.iter() { - let mut recipe = NbtCompound::new(); - let mut buy = NbtCompound::new(); - let mut sell = NbtCompound::new(); - - let item_stack: &ItemStack = offer.base_cost_a.0.as_ref(); - item_stack.write_item_stack(&mut buy); - recipe.put_compound("buy", buy); - - let item_stack: &ItemStack = offer.output.0.as_ref(); - item_stack.write_item_stack(&mut sell); - recipe.put_compound("sell", sell); - - if let Some(cost_b) = &offer.cost_b { - let mut buy_b = NbtCompound::new(); - let item_stack: &ItemStack = cost_b.0.as_ref(); - item_stack.write_item_stack(&mut buy_b); - recipe.put_compound("buyB", buy_b); - } - - recipe.put_int("uses", offer.uses); - recipe.put_int("maxUses", offer.max_uses); - recipe.put_bool("rewardExp", offer.reward_exp); - recipe.put_int("xp", offer.xp); - recipe.put_float("priceMultiplier", offer.price_multiplier); - recipe.put_int("specialPrice", offer.special_price); - recipe.put_int("demand", offer.demand); - - recipes.push(NbtTag::Compound(recipe)); + if let Some(cost_b) = &offer.cost_b { + let mut buy_b = NbtCompound::new(); + let item_stack: &ItemStack = cost_b.0.as_ref(); + item_stack.write_item_stack(&mut buy_b); + recipe.put_compound("buyB", buy_b); } - let mut offers_compound = NbtCompound::new(); - offers_compound.put("Recipes", NbtTag::List(recipes)); - nbt.put_compound("Offers", offers_compound); - } - let inventory = self.inventory.lock().await; - if !inventory.is_empty() { - let mut inventory_list = Vec::new(); - for item in inventory.iter() { - let item = item.lock().await; - let mut item_compound = NbtCompound::new(); - item.write_item_stack(&mut item_compound); - inventory_list.push(NbtTag::Compound(item_compound)); - } - nbt.put("Inventory", NbtTag::List(inventory_list)); - } + recipe.put_int("uses", offer.uses); + recipe.put_int("maxUses", offer.max_uses); + recipe.put_bool("rewardExp", offer.reward_exp); + recipe.put_int("xp", offer.xp); + recipe.put_float("priceMultiplier", offer.price_multiplier); + recipe.put_int("specialPrice", offer.special_price); + recipe.put_int("demand", offer.demand); - let gossips = self.gossips.lock().await; - if !gossips.is_empty() { - let mut gossip_list = Vec::new(); - for (uuid, entries) in gossips.iter() { - for (gossip_type, val) in entries { - let mut gossip_nbt = NbtCompound::new(); - let (u1, u2) = uuid.as_u64_pair(); - let uuid_array = - vec![(u1 >> 32) as i32, u1 as i32, (u2 >> 32) as i32, u2 as i32]; - gossip_nbt.put("Target", NbtTag::IntArray(uuid_array)); - gossip_nbt.put_string("Type", gossip_type.name().to_string()); - gossip_nbt.put_int("Value", *val); - gossip_list.push(NbtTag::Compound(gossip_nbt)); - } - } - nbt.put("Gossips", NbtTag::List(gossip_list)); + recipes.push(NbtTag::Compound(recipe)); } - }) + let mut offers_compound = NbtCompound::new(); + offers_compound.put("Recipes", NbtTag::List(recipes)); + nbt.put_compound("Offers", offers_compound); + } + + let inventory = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !inventory.is_empty() { + let mut inventory_list = Vec::new(); + for item in inventory.iter() { + let mut item_compound = NbtCompound::new(); + item.write_item_stack(&mut item_compound); + inventory_list.push(NbtTag::Compound(item_compound)); + } + nbt.put("Inventory", NbtTag::List(inventory_list)); + } + + let gossips = self + .gossips + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !gossips.is_empty() { + let mut gossip_list = Vec::new(); + for (uuid, entries) in gossips.iter() { + for (gossip_type, val) in entries { + let mut gossip_nbt = NbtCompound::new(); + let (u1, u2) = uuid.as_u64_pair(); + let uuid_array = + vec![(u1 >> 32) as i32, u1 as i32, (u2 >> 32) as i32, u2 as i32]; + gossip_nbt.put("Target", NbtTag::IntArray(uuid_array)); + gossip_nbt.put_string("Type", gossip_type.name().to_string()); + gossip_nbt.put_int("Value", *val); + gossip_list.push(NbtTag::Compound(gossip_nbt)); + } + } + nbt.put("Gossips", NbtTag::List(gossip_list)); + } } #[allow(clippy::too_many_lines)] - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> crate::entity::NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(villager_data_nbt) = nbt.get_compound("VillagerData") { - let mut data = self - .villager_data - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(t) = villager_data_nbt.get_int("Type") { - data.r#type = VarInt(t); - } - if let Some(p) = villager_data_nbt.get_int("Profession") { - data.profession = VarInt(p); - } - if let Some(l) = villager_data_nbt.get_int("Level") { - data.level = VarInt(l); - } + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(villager_data_nbt) = nbt.get_compound("VillagerData") { + let mut data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(t) = villager_data_nbt.get_int("Type") { + data.r#type = VarInt(t); } + if let Some(p) = villager_data_nbt.get_int("Profession") { + data.profession = VarInt(p); + } + if let Some(l) = villager_data_nbt.get_int("Level") { + data.level = VarInt(l); + } + } - if let Some(food) = nbt.get_int("FoodLevel") { - self.food_level.store(food, Ordering::Relaxed); - } - if let Some(xp) = nbt.get_int("Xp") { - self.xp.store(xp, Ordering::Relaxed); - } - if let Some(restock) = nbt.get_long("LastRestock") { - self.last_restock_time.store(restock, Ordering::Relaxed); - } - if let Some(today) = nbt.get_int("RestocksToday") { - self.restocks_today.store(today, Ordering::Relaxed); - } - if let Some(last_decay) = nbt.get_long("LastGossipDecay") { - self.last_gossip_decay_time - .store(last_decay, Ordering::Relaxed); - } + if let Some(food) = nbt.get_int("FoodLevel") { + self.food_level.store(food, Ordering::Relaxed); + } + if let Some(xp) = nbt.get_int("Xp") { + self.xp.store(xp, Ordering::Relaxed); + } + if let Some(restock) = nbt.get_long("LastRestock") { + self.last_restock_time.store(restock, Ordering::Relaxed); + } + if let Some(today) = nbt.get_int("RestocksToday") { + self.restocks_today.store(today, Ordering::Relaxed); + } + if let Some(last_decay) = nbt.get_long("LastGossipDecay") { + self.last_gossip_decay_time + .store(last_decay, Ordering::Relaxed); + } - if let (Some(x), Some(y), Some(z)) = ( - nbt.get_int("JobSiteX"), - nbt.get_int("JobSiteY"), - nbt.get_int("JobSiteZ"), - ) { - *self - .job_site - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - Some(BlockPos::new(x, y, z)); - self.job_site_pending.store( - nbt.get_bool("JobSitePending").unwrap_or(false), - Ordering::Relaxed, - ); - } else { - *self - .job_site - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - self.job_site_pending.store(false, Ordering::Relaxed); - } + if let (Some(x), Some(y), Some(z)) = ( + nbt.get_int("JobSiteX"), + nbt.get_int("JobSiteY"), + nbt.get_int("JobSiteZ"), + ) { + *self + .job_site + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(BlockPos::new(x, y, z)); + self.job_site_pending.store( + nbt.get_bool("JobSitePending").unwrap_or(false), + Ordering::Relaxed, + ); + } else { + *self + .job_site + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + self.job_site_pending.store(false, Ordering::Relaxed); + } - if let (Some(x), Some(y), Some(z)) = ( - nbt.get_int("HomeX").or_else(|| nbt.get_int("BedX")), - nbt.get_int("HomeY").or_else(|| nbt.get_int("BedY")), - nbt.get_int("HomeZ").or_else(|| nbt.get_int("BedZ")), - ) { - *self - .home_pos - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - Some(BlockPos::new(x, y, z)); - } else { - *self - .home_pos - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - } + if let (Some(x), Some(y), Some(z)) = ( + nbt.get_int("HomeX").or_else(|| nbt.get_int("BedX")), + nbt.get_int("HomeY").or_else(|| nbt.get_int("BedY")), + nbt.get_int("HomeZ").or_else(|| nbt.get_int("BedZ")), + ) { + *self + .home_pos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(BlockPos::new(x, y, z)); + } else { + *self + .home_pos + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } - if let Some(offers_compound) = nbt.get_compound("Offers") - && let Some(recipes) = offers_compound.get_list("Recipes") - { - let mut offers = self.offers.lock().await; - offers.clear(); - for tag in recipes { - if let Some(recipe) = tag.extract_compound() { - let buy = recipe - .get_compound("buy") - .and_then(ItemStack::read_item_stack); - let buy_b = recipe - .get_compound("buyB") - .and_then(ItemStack::read_item_stack); - let sell_item = recipe - .get_compound("sell") - .and_then(ItemStack::read_item_stack); + if let Some(offers_compound) = nbt.get_compound("Offers") + && let Some(recipes) = offers_compound.get_list("Recipes") + { + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + offers.clear(); + for tag in recipes { + if let Some(recipe) = tag.extract_compound() { + let buy = recipe + .get_compound("buy") + .and_then(ItemStack::read_item_stack); + let buy_b = recipe + .get_compound("buyB") + .and_then(ItemStack::read_item_stack); + let sell_item = recipe + .get_compound("sell") + .and_then(ItemStack::read_item_stack); - if let (Some(buy), Some(sell_item)) = (buy, sell_item) - && !buy.is_empty() - && !sell_item.is_empty() - && buy_b.as_ref().is_none_or(|stack| !stack.is_empty()) - { - let uses = recipe.get_int("uses").unwrap_or(0); - let max_uses = recipe.get_int("maxUses").unwrap_or(12); - let reward_exp = recipe.get_bool("rewardExp").unwrap_or(true); - let xp = recipe.get_int("xp").unwrap_or(2); - let price_multiplier = - recipe.get_float("priceMultiplier").unwrap_or(0.05); - let special_price = recipe.get_int("specialPrice").unwrap_or(0); - let demand = recipe.get_int("demand").unwrap_or(0); - - offers.push(pumpkin_protocol::java::client::play::MerchantOffer { - base_cost_a: buy.into(), - output: sell_item.into(), - cost_b: buy_b.map(Into::into), - reward_exp, - uses, - max_uses, - xp, - special_price, - price_multiplier, - demand, - }); - } - } - } - } - - // Inventory - if let Some(inventory_list) = nbt.get_list("Inventory") { - let mut inventory = self.inventory.lock().await; - inventory.clear(); - for tag in inventory_list { - if let Some(item_compound) = tag.extract_compound() - && let Some(stack) = ItemStack::read_item_stack(item_compound) + if let (Some(buy), Some(sell_item)) = (buy, sell_item) + && !buy.is_empty() + && !sell_item.is_empty() + && buy_b.as_ref().is_none_or(|stack| !stack.is_empty()) { - inventory.push(Arc::new(Mutex::new(stack))); - } - } - } + let uses = recipe.get_int("uses").unwrap_or(0); + let max_uses = recipe.get_int("maxUses").unwrap_or(12); + let reward_exp = recipe.get_bool("rewardExp").unwrap_or(true); + let xp = recipe.get_int("xp").unwrap_or(2); + let price_multiplier = recipe.get_float("priceMultiplier").unwrap_or(0.05); + let special_price = recipe.get_int("specialPrice").unwrap_or(0); + let demand = recipe.get_int("demand").unwrap_or(0); - // Gossips - if let Some(gossip_list) = nbt.get_list("Gossips") { - let mut gossips = self.gossips.lock().await; - gossips.clear(); - for tag in gossip_list { - if let Some(gossip_nbt) = tag.extract_compound() { - let uuid = gossip_nbt.get_int_array("Target").map(|uuid_array| { - Uuid::from_u128( - (uuid_array[0] as u128) << 96 - | (uuid_array[1] as u128) << 64 - | (uuid_array[2] as u128) << 32 - | (uuid_array[3] as u128), - ) + offers.push(pumpkin_protocol::java::client::play::MerchantOffer { + base_cost_a: buy.into(), + output: sell_item.into(), + cost_b: buy_b.map(Into::into), + reward_exp, + uses, + max_uses, + xp, + special_price, + price_multiplier, + demand, }); - let gossip_type = gossip_nbt - .get_string("Type") - .and_then(GossipType::from_name) - .or_else(|| { - gossip_nbt - .get_int("Type") - .and_then(GossipType::from_legacy_id) - }); - if let (Some(uuid), Some(gossip_type), Some(val)) = - (uuid, gossip_type, gossip_nbt.get_int("Value")) - { - gossips.entry(uuid).or_default().insert(gossip_type, val); - } } } } - }) + } + + // Inventory + if let Some(inventory_list) = nbt.get_list("Inventory") { + let mut inventory = self + .inventory + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + inventory.clear(); + for tag in inventory_list { + if let Some(item_compound) = tag.extract_compound() + && let Some(stack) = ItemStack::read_item_stack(item_compound) + { + inventory.push(stack); + } + } + } + + // Gossips + if let Some(gossip_list) = nbt.get_list("Gossips") { + let mut gossips = self + .gossips + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + gossips.clear(); + for tag in gossip_list { + if let Some(gossip_nbt) = tag.extract_compound() { + let uuid = gossip_nbt.get_int_array("Target").map(|uuid_array| { + Uuid::from_u128( + (uuid_array[0] as u128) << 96 + | (uuid_array[1] as u128) << 64 + | (uuid_array[2] as u128) << 32 + | (uuid_array[3] as u128), + ) + }); + let gossip_type = gossip_nbt + .get_string("Type") + .and_then(GossipType::from_name) + .or_else(|| { + gossip_nbt + .get_int("Type") + .and_then(GossipType::from_legacy_id) + }); + if let (Some(uuid), Some(gossip_type), Some(val)) = + (uuid, gossip_type, gossip_nbt.get_int("Value")) + { + gossips.entry(uuid).or_default().insert(gossip_type, val); + } + } + } + } } fn get_mob_entity(&self) -> &MobEntity { @@ -2070,38 +2148,29 @@ impl Mob for VillagerEntity { Some("minecraft:villager_v2") } - fn mob_java_spawn_metadata( - &self, - version: JavaMinecraftVersion, - ) -> crate::entity::EntityBaseFuture<'_, Option>> { - Box::pin(async move { - let mut metadata = Vec::new(); - Metadata::new( - tracked_data::villager::VILLAGER_DATA, - *self - .villager_data - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner), - ) - .write(&mut metadata, &version) - .ok()?; - metadata.push(255); - Some(metadata.into_boxed_slice()) - }) + fn mob_java_spawn_metadata(&self, version: JavaMinecraftVersion) -> Option> { + let mut metadata = Vec::new(); + Metadata::new( + tracked_data::villager::VILLAGER_DATA, + *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ) + .write(&mut metadata, &version) + .ok()?; + metadata.push(255); + Some(metadata.into_boxed_slice()) } - fn mob_bedrock_spawn_metadata( - &self, - ) -> crate::entity::EntityBaseFuture<'_, Option> { - Box::pin(async move { - Some(Self::bedrock_metadata( - *self - .villager_data - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner), - self.xp.load(Ordering::Relaxed), - )) - }) + fn mob_bedrock_spawn_metadata(&self) -> Option { + Some(Self::bedrock_metadata( + *self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + self.xp.load(Ordering::Relaxed), + )) } fn get_job_site(&self) -> Option { @@ -2207,54 +2276,66 @@ impl Mob for VillagerEntity { }); } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - _item_stack: &'a mut ItemStack, - ) -> crate::entity::EntityBaseFuture<'a, bool> { - Box::pin(async move { - if self.is_trading.load(Ordering::Relaxed) { - return false; - } - if self.get_entity().age.load(Ordering::Relaxed) < 0 { - self.set_unhappy(); - return true; + fn mob_interact(&self, player: &Arc, _item_stack: &mut ItemStack) -> bool { + if self.is_trading.load(Ordering::Relaxed) { + return false; + } + if self.get_entity().age.load(Ordering::Relaxed) < 0 { + self.set_unhappy(); + return true; + } + + let trade_params = { + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if offers.is_empty() { + let data = self + .villager_data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + (data.profession_enum() != VillagerProfession::None + && data.profession_enum() != VillagerProfession::Nitwit) + .then(|| (data.profession_enum(), data.level.0)) + } else { + None } + }; + if let Some((prof, level)) = trade_params { + self.generate_trades(prof, level); + } - let trade_params = { - let offers = self.offers.lock().await; - if offers.is_empty() { - let data = self - .villager_data - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - (data.profession_enum() != VillagerProfession::None - && data.profession_enum() != VillagerProfession::Nitwit) - .then(|| (data.profession_enum(), data.level.0)) - } else { - None - } - }; - if let Some((prof, level)) = trade_params { - self.generate_trades(prof, level).await; - } + let has_offers = !self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty(); + if !has_offers { + self.set_unhappy(); + return true; + } - let has_offers = !self.offers.lock().await.is_empty(); - if !has_offers { - self.set_unhappy(); - return true; - } + player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::TalkedToVillager as i32, + 1, + ); - player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::TalkedToVillager as i32, - 1, - ); + if let Some(villager) = self + .self_weak + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + .and_then(Weak::upgrade) + { + let player = player.clone(); + tokio::spawn(async move { + villager.open_trading_screen(&player).await; + }); + } - self.open_trading_screen(player).await; - - true - }) + true } } diff --git a/crates/pumpkin/src/entity/passive/wandering_trader.rs b/crates/pumpkin/src/entity/passive/wandering_trader.rs index f591c1a44..639f96a69 100644 --- a/crates/pumpkin/src/entity/passive/wandering_trader.rs +++ b/crates/pumpkin/src/entity/passive/wandering_trader.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use std::sync::{Arc, Weak}; -use tokio::sync::Mutex; use uuid::Uuid; use pumpkin_data::data_component_impl::EquipmentSlot; @@ -46,7 +45,7 @@ use crate::entity::ai::pathfinder::NavigatorGoal; use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::mob::{Mob, MobEntity, NIGHT_END, NIGHT_START}; use crate::entity::player::Player; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, NbtFuture}; +use crate::entity::{Entity, EntityBase}; use crate::world::World; const DEFAULT_DESPAWN_DELAY: i32 = 0; @@ -112,7 +111,7 @@ pub struct WanderingTraderEntity { pub mob_entity: MobEntity, pub despawn_delay: AtomicI32, pub wander_target: std::sync::Mutex>, - pub offers: Mutex>, + pub offers: std::sync::Mutex>, pub merchant_inventory: Arc, pub trading_player: std::sync::Mutex>, pub is_trading: AtomicBool, @@ -131,7 +130,7 @@ impl WanderingTraderEntity { mob_entity, despawn_delay: AtomicI32::new(DEFAULT_DESPAWN_DELAY), wander_target: std::sync::Mutex::new(None), - offers: Mutex::new(Vec::new()), + offers: std::sync::Mutex::new(Vec::new()), merchant_inventory: Arc::new(SimpleInventory::new(3)), trading_player: std::sync::Mutex::new(None), is_trading: AtomicBool::new(false), @@ -286,8 +285,11 @@ impl WanderingTraderEntity { .unwrap_or_else(std::sync::PoisonError::into_inner) = target; } - pub async fn generate_trades(&self) { - let mut offers = self.offers.lock().await; + pub fn generate_trades(&self) { + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); offers.clear(); let mut rng = rand::rng(); add_offers_from_trade_set(&mut offers, TRADES_WANDERING_TRADER_BUYING, 2, &mut rng); @@ -297,7 +299,11 @@ impl WanderingTraderEntity { pub async fn open_trading_screen(&self, player: &Arc) { if let Some(sync_id) = player.open_handled_screen(self, None).await { - let offers = self.offers.lock().await.clone(); + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); self.send_trade_offers(player, sync_id, offers).await; } } @@ -454,7 +460,10 @@ impl WanderingTraderEntity { async fn complete_trade(&self, offer_index: usize, world: &Arc, player_uuid: Uuid) { let (reward_exp, reward_amount) = { - let mut offers = self.offers.lock().await; + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let Some(offer) = offers.get_mut(offer_index) else { return; }; @@ -495,7 +504,11 @@ impl ScreenHandlerFactory for WanderingTraderEntity { let player_uuid = server_player.map_or_else(uuid::Uuid::nil, |p| p.get_entity().entity_uuid); - let offers = self.offers.lock().await.clone(); + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let world = self.get_entity().world.load().clone(); let mut handler = MerchantScreenHandler::new( @@ -607,172 +620,184 @@ impl Mob for WanderingTraderEntity { Some(self) } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("DespawnDelay", self.despawn_delay.load(Ordering::Relaxed)); - let wander_target = *self + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("DespawnDelay", self.despawn_delay.load(Ordering::Relaxed)); + let wander_target = *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(target) = wander_target { + nbt.put( + "wander_target", + pumpkin_nbt::tag::NbtTag::IntArray(vec![target.0.x, target.0.y, target.0.z]), + ); + } + + let offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !offers.is_empty() { + let mut recipes = Vec::with_capacity(offers.len()); + for offer in offers.iter() { + let mut recipe = NbtCompound::new(); + let mut buy = NbtCompound::new(); + offer.base_cost_a.0.write_item_stack(&mut buy); + recipe.put_compound("buy", buy); + + if let Some(cost_b) = &offer.cost_b { + let mut buy_b = NbtCompound::new(); + cost_b.0.write_item_stack(&mut buy_b); + recipe.put_compound("buyB", buy_b); + } + + let mut sell_item = NbtCompound::new(); + offer.output.0.write_item_stack(&mut sell_item); + recipe.put_compound("sell", sell_item); + + recipe.put_int("uses", offer.uses); + recipe.put_int("maxUses", offer.max_uses); + recipe.put_bool("rewardExp", offer.reward_exp); + recipe.put_int("xp", offer.xp); + recipe.put_float("priceMultiplier", offer.price_multiplier); + recipe.put_int("specialPrice", offer.special_price); + recipe.put_int("demand", offer.demand); + + recipes.push(pumpkin_nbt::tag::NbtTag::Compound(recipe)); + } + let mut offers_compound = NbtCompound::new(); + offers_compound.put("Recipes", pumpkin_nbt::tag::NbtTag::List(recipes)); + nbt.put_compound("Offers", offers_compound); + } + } + + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(delay) = nbt.get_int("DespawnDelay") { + self.despawn_delay.store(delay, Ordering::Relaxed); + } + if let Some(target_arr) = nbt.get_int_array("wander_target") + && target_arr.len() >= 3 + { + *self .wander_target .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + Some(BlockPos::new(target_arr[0], target_arr[1], target_arr[2])); + } else if let (Some(x), Some(y), Some(z)) = ( + nbt.get_int("wander_target_x"), + nbt.get_int("wander_target_y"), + nbt.get_int("wander_target_z"), + ) { + *self + .wander_target + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(BlockPos::new(x, y, z)); + } + + if let Some(offers_compound) = nbt.get_compound("Offers") + && let Some(recipes) = offers_compound.get_list("Recipes") + { + let mut offers = self + .offers + .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(target) = wander_target { - nbt.put( - "wander_target", - pumpkin_nbt::tag::NbtTag::IntArray(vec![target.0.x, target.0.y, target.0.z]), - ); - } + offers.clear(); + for tag in recipes { + if let Some(recipe) = tag.extract_compound() { + let buy = recipe + .get_compound("buy") + .and_then(ItemStack::read_item_stack); + let buy_b = recipe + .get_compound("buyB") + .and_then(ItemStack::read_item_stack); + let sell_item = recipe + .get_compound("sell") + .and_then(ItemStack::read_item_stack); - let offers = self.offers.lock().await; - if !offers.is_empty() { - let mut recipes = Vec::with_capacity(offers.len()); - for offer in offers.iter() { - let mut recipe = NbtCompound::new(); - let mut buy = NbtCompound::new(); - offer.base_cost_a.0.write_item_stack(&mut buy); - recipe.put_compound("buy", buy); + if let (Some(buy), Some(sell_item)) = (buy, sell_item) + && !buy.is_empty() + && !sell_item.is_empty() + && buy_b.as_ref().is_none_or(|stack| !stack.is_empty()) + { + let uses = recipe.get_int("uses").unwrap_or(0); + let max_uses = recipe.get_int("maxUses").unwrap_or(12); + let reward_exp = recipe.get_bool("rewardExp").unwrap_or(true); + let xp = recipe.get_int("xp").unwrap_or(2); + let price_multiplier = recipe.get_float("priceMultiplier").unwrap_or(0.05); + let special_price = recipe.get_int("specialPrice").unwrap_or(0); + let demand = recipe.get_int("demand").unwrap_or(0); - if let Some(cost_b) = &offer.cost_b { - let mut buy_b = NbtCompound::new(); - cost_b.0.write_item_stack(&mut buy_b); - recipe.put_compound("buyB", buy_b); - } - - let mut sell_item = NbtCompound::new(); - offer.output.0.write_item_stack(&mut sell_item); - recipe.put_compound("sell", sell_item); - - recipe.put_int("uses", offer.uses); - recipe.put_int("maxUses", offer.max_uses); - recipe.put_bool("rewardExp", offer.reward_exp); - recipe.put_int("xp", offer.xp); - recipe.put_float("priceMultiplier", offer.price_multiplier); - recipe.put_int("specialPrice", offer.special_price); - recipe.put_int("demand", offer.demand); - - recipes.push(pumpkin_nbt::tag::NbtTag::Compound(recipe)); - } - let mut offers_compound = NbtCompound::new(); - offers_compound.put("Recipes", pumpkin_nbt::tag::NbtTag::List(recipes)); - nbt.put_compound("Offers", offers_compound); - } - }) - } - - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(delay) = nbt.get_int("DespawnDelay") { - self.despawn_delay.store(delay, Ordering::Relaxed); - } - if let Some(target_arr) = nbt.get_int_array("wander_target") - && target_arr.len() >= 3 - { - *self - .wander_target - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - Some(BlockPos::new(target_arr[0], target_arr[1], target_arr[2])); - } else if let (Some(x), Some(y), Some(z)) = ( - nbt.get_int("wander_target_x"), - nbt.get_int("wander_target_y"), - nbt.get_int("wander_target_z"), - ) { - *self - .wander_target - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = - Some(BlockPos::new(x, y, z)); - } - - if let Some(offers_compound) = nbt.get_compound("Offers") - && let Some(recipes) = offers_compound.get_list("Recipes") - { - let mut offers = self.offers.lock().await; - offers.clear(); - for tag in recipes { - if let Some(recipe) = tag.extract_compound() { - let buy = recipe - .get_compound("buy") - .and_then(ItemStack::read_item_stack); - let buy_b = recipe - .get_compound("buyB") - .and_then(ItemStack::read_item_stack); - let sell_item = recipe - .get_compound("sell") - .and_then(ItemStack::read_item_stack); - - if let (Some(buy), Some(sell_item)) = (buy, sell_item) - && !buy.is_empty() - && !sell_item.is_empty() - && buy_b.as_ref().is_none_or(|stack| !stack.is_empty()) - { - let uses = recipe.get_int("uses").unwrap_or(0); - let max_uses = recipe.get_int("maxUses").unwrap_or(12); - let reward_exp = recipe.get_bool("rewardExp").unwrap_or(true); - let xp = recipe.get_int("xp").unwrap_or(2); - let price_multiplier = - recipe.get_float("priceMultiplier").unwrap_or(0.05); - let special_price = recipe.get_int("specialPrice").unwrap_or(0); - let demand = recipe.get_int("demand").unwrap_or(0); - - offers.push(pumpkin_protocol::java::client::play::MerchantOffer { - base_cost_a: buy.into(), - output: sell_item.into(), - cost_b: buy_b.map(Into::into), - reward_exp, - uses, - max_uses, - xp, - special_price, - price_multiplier, - demand, - }); - } + offers.push(pumpkin_protocol::java::client::play::MerchantOffer { + base_cost_a: buy.into(), + output: sell_item.into(), + cost_b: buy_b.map(Into::into), + reward_exp, + uses, + max_uses, + xp, + special_price, + price_multiplier, + demand, + }); } } } + } - let current_age = self.get_age(); - if current_age < 0 { - self.set_age(0); - } - }) + let current_age = self.get_age(); + if current_age < 0 { + self.set_age(0); + } } - fn mob_interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - let player = player.clone(); - Box::pin(async move { - if item_stack.item == &Item::VILLAGER_SPAWN_EGG - || self.mob_entity.living_entity.health.load() <= 0.0 - || self.is_trading.load(Ordering::Relaxed) - || self.is_baby() - { - return false; - } + fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + if item_stack.item == &Item::VILLAGER_SPAWN_EGG + || self.mob_entity.living_entity.health.load() <= 0.0 + || self.is_trading.load(Ordering::Relaxed) + || self.is_baby() + { + return false; + } - player.increment_stat( - StatisticCategory::Custom, - CustomStatistic::TalkedToVillager as i32, - 1, - ); + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::TalkedToVillager as i32, + 1, + ); - let mut offers = self.offers.lock().await; + let has_offers = { + let mut offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if offers.is_empty() { drop(offers); - self.generate_trades().await; - offers = self.offers.lock().await; + self.generate_trades(); + offers = self + .offers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); } + !offers.is_empty() + }; - if offers.is_empty() { - return true; - } - drop(offers); + if !has_offers { + return true; + } - self.open_trading_screen(&player).await; - true - }) + if let Some(trader) = self + .self_weak + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + .and_then(Weak::upgrade) + { + let player = player.clone(); + tokio::spawn(async move { + trader.open_trading_screen(&player).await; + }); + } + true } fn mob_tick<'a>(&'a self, _caller: &'a Arc) { diff --git a/crates/pumpkin/src/entity/passive/wolf.rs b/crates/pumpkin/src/entity/passive/wolf.rs index 412543428..74b8f7332 100644 --- a/crates/pumpkin/src/entity/passive/wolf.rs +++ b/crates/pumpkin/src/entity/passive/wolf.rs @@ -12,7 +12,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, ageable::AgeableMob, ai::goal::{ active_target::ActiveTargetGoal, avoid_entity::AvoidEntityGoal, beg::BegGoal, @@ -223,52 +223,48 @@ impl Mob for WolfEntity { true } - fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let variant_str = match self.variant.load(Ordering::Relaxed) { - 0 => "minecraft:ashen", - 1 => "minecraft:black", - 2 => "minecraft:chestnut", - 4 => "minecraft:rusty", - 5 => "minecraft:snowy", - 6 => "minecraft:spotted", - 7 => "minecraft:striped", - 8 => "minecraft:woods", - _ => "minecraft:pale", - }; - nbt.put_string("variant", variant_str.to_string()); - nbt.put_byte( - "CollarColor", - self.collar_color.load(Ordering::Relaxed) as i8, - ); - }) + fn mob_write_nbt(&self, nbt: &mut NbtCompound) { + let variant_str = match self.variant.load(Ordering::Relaxed) { + 0 => "minecraft:ashen", + 1 => "minecraft:black", + 2 => "minecraft:chestnut", + 4 => "minecraft:rusty", + 5 => "minecraft:snowy", + 6 => "minecraft:spotted", + 7 => "minecraft:striped", + 8 => "minecraft:woods", + _ => "minecraft:pale", + }; + nbt.put_string("variant", variant_str.to_string()); + nbt.put_byte( + "CollarColor", + self.collar_color.load(Ordering::Relaxed) as i8, + ); } - fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - if let Some(variant_str) = nbt.get_string("variant") { - let variant = match variant_str - .strip_prefix("minecraft:") - .unwrap_or(variant_str) - { - "ashen" => 0, - "black" => 1, - "chestnut" => 2, - "rusty" => 4, - "snowy" => 5, - "spotted" => 6, - "striped" => 7, - "woods" => 8, - _ => 3, - }; - self.variant.store(variant, Ordering::Relaxed); - } - if let Some(collar) = nbt.get_byte("CollarColor") { - self.collar_color.store(collar as u8, Ordering::Relaxed); - } else if let Some(collar_int) = nbt.get_int("CollarColor") { - self.collar_color.store(collar_int as u8, Ordering::Relaxed); - } - }) + fn mob_read_nbt(&self, nbt: &NbtCompound) { + if let Some(variant_str) = nbt.get_string("variant") { + let variant = match variant_str + .strip_prefix("minecraft:") + .unwrap_or(variant_str) + { + "ashen" => 0, + "black" => 1, + "chestnut" => 2, + "rusty" => 4, + "snowy" => 5, + "spotted" => 6, + "striped" => 7, + "woods" => 8, + _ => 3, + }; + self.variant.store(variant, Ordering::Relaxed); + } + if let Some(collar) = nbt.get_byte("CollarColor") { + self.collar_color.store(collar as u8, Ordering::Relaxed); + } else if let Some(collar_int) = nbt.get_int("CollarColor") { + self.collar_color.store(collar_int as u8, Ordering::Relaxed); + } } fn get_mob_entity(&self) -> &MobEntity { diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index af2b79da7..bd765ed7e 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -272,7 +272,7 @@ use crate::command::context::command_source::CommandSource; use crate::command::node::dispatcher::CommandDispatcher; use crate::command::{CommandSender, client_suggestions}; use crate::data::SaveJSONConfiguration; -use crate::entity::{EntityBaseFuture, NbtFuture, TeleportFuture}; +use crate::entity::TeleportFuture; use crate::net::{ClientPlatform, GameProfile}; use crate::net::{DisconnectReason, PlayerConfig}; use crate::plugin::player::exp_change::PlayerExpChangeEvent; @@ -707,7 +707,7 @@ pub struct Player { /// The entity ID of the entity that the player is currently spectating/camera targeting. pub camera_target_id: AtomicCell>, /// The player's spawnpoint - pub respawn_point: Mutex>, + pub respawn_point: std::sync::Mutex>, /// The player's sleep status pub sleeping_since: AtomicCell>, /// Manages the player's breath level @@ -727,9 +727,9 @@ pub struct Player { /// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects. /// /// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes. - pub abilities: Mutex, + pub abilities: std::sync::Mutex, /// Player statistics - pub stats: Mutex, + pub stats: std::sync::Mutex, /// The current stage of block destruction of the block the player is breaking. pub current_block_destroy_stage: AtomicI32, /// The per-tick block destruction progress last sent to Bedrock clients. @@ -777,7 +777,7 @@ pub struct Player { pub experience_progress: AtomicCell, /// The player's total experience points. pub experience_points: AtomicI32, - pub item_cooldowns: Mutex>, + pub item_cooldowns: std::sync::Mutex>, pub experience_pick_up_delay: Mutex, pub chunk_manager: Mutex, pub has_played_before: AtomicBool, @@ -792,7 +792,7 @@ pub struct Player { pub screen_handler_sync_handler: Arc, pub tab_list_header: Mutex, pub tab_list_footer: Mutex, - pub display_name: Mutex>, + pub display_name: std::sync::Mutex>, pub tab_list_name: Mutex>, pub tab_list_order: AtomicI32, pub tab_list_latency: AtomicI32, @@ -1018,13 +1018,13 @@ impl Player { teleport_id_count: AtomicI32::new(0), mining: AtomicBool::new(false), mining_pos: Mutex::new(BlockPos::ZERO), - abilities: Mutex::new(abilities), - stats: Mutex::new(statistics::Statistics::default()), + abilities: std::sync::Mutex::new(abilities), + stats: std::sync::Mutex::new(statistics::Statistics::default()), gamemode: AtomicCell::new(gamemode), previous_gamemode: AtomicCell::new(None), camera_target_id: AtomicCell::new(None), // TODO: Send the CPlayerSpawnPosition packet when the client connects with proper values - respawn_point: Mutex::new(None), + respawn_point: std::sync::Mutex::new(None), sleeping_since: AtomicCell::new(None), // We want this to be an impossible watched section so that `chunker::update_position` // will mark chunks as watched for a new join rather than a respawn. @@ -1062,7 +1062,7 @@ impl Player { experience_level: AtomicI32::new(0), experience_progress: AtomicCell::new(0.0), experience_points: AtomicI32::new(0), - item_cooldowns: Mutex::new(HashMap::new()), + item_cooldowns: std::sync::Mutex::new(HashMap::new()), // Default to sending 16 chunks per tick. chunk_manager: Mutex::new(ChunkManager::new( 16, @@ -1086,7 +1086,7 @@ impl Player { screen_handler_sync_handler: Arc::new(SyncHandler::new()), tab_list_header: Mutex::new(TextComponent::text("")), tab_list_footer: Mutex::new(TextComponent::text("")), - display_name: Mutex::new(None), + display_name: std::sync::Mutex::new(None), tab_list_name: Mutex::new(None), tab_list_order: AtomicI32::new(0), tab_list_latency: AtomicI32::new(0), @@ -1122,8 +1122,11 @@ impl Player { .await; } - pub async fn start_cooldown(&self, group: String, duration: i32) { - let mut cooldowns = self.item_cooldowns.lock().await; + pub fn start_cooldown(&self, group: String, duration: i32) { + let mut cooldowns = self + .item_cooldowns + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); cooldowns.insert( group.clone(), ItemCooldown { @@ -1131,12 +1134,14 @@ impl Player { duration, }, ); - self.send_client_packet(&CItemCooldown::new(group, VarInt(duration))) - .await; + self.try_send_client_packet(&CItemCooldown::new(group, VarInt(duration))); } - pub async fn get_cooldown(&self, group: &str) -> f32 { - let cooldowns = self.item_cooldowns.lock().await; + pub fn get_cooldown(&self, group: &str) -> f32 { + let cooldowns = self + .item_cooldowns + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(cooldown) = cooldowns.get(group) { let current_tick = self.tick_counter.load(Ordering::Relaxed); let elapsed = current_tick - cooldown.start_tick; @@ -1147,8 +1152,11 @@ impl Player { 0.0 } - pub async fn is_on_cooldown(&self, group: &str) -> bool { - let mut cooldowns = self.item_cooldowns.lock().await; + pub fn is_on_cooldown(&self, group: &str) -> bool { + let mut cooldowns = self + .item_cooldowns + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(cooldown) = cooldowns.get(group) { let current_tick = self.tick_counter.load(Ordering::Relaxed); if current_tick - cooldown.start_tick < cooldown.duration { @@ -1160,7 +1168,10 @@ impl Player { } pub async fn set_display_name(&self, display_name: Option) { - *self.display_name.lock().await = display_name.clone(); + *self + .display_name + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = display_name.clone(); // Update the tab list for everyone let world = self.world(); world.broadcast_packet_all(&CPlayerInfoUpdate::new( @@ -1277,19 +1288,24 @@ impl Player { self.on_handled_screen_closed().await; } - let vehicle = self.living_entity.entity.vehicle.lock().await.clone(); + let vehicle = self + .living_entity + .entity + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(vehicle) = vehicle { self.root_vehicle_uuid .store(Some(vehicle.get_entity().entity_uuid)); vehicle .get_entity() - .remove_passenger_on_disconnect(self.entity_id()) - .await; + .remove_passenger_on_disconnect(self.entity_id()); } self.stats .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .increment_custom(statistics::CustomStatistic::LeaveGame, 1); let world = self.world(); world.remove_player(self, true).await; @@ -1344,8 +1360,7 @@ impl Player { vehicle .get_entity() - .add_passenger(vehicle.clone(), self.clone()) - .await; + .add_passenger(vehicle.clone(), self.clone()); } #[expect(clippy::too_many_lines)] @@ -1591,8 +1606,7 @@ impl Player { // 2. Refactor compute cost as a closure: damage_held_item(self, |stack| -> i32 { ... }) // 3. In practice, single-player scenarios are safe (this is not multiplayer). Document // as a known limitation if refactoring is deemed too invasive. - self.damage_held_item(Self::combat_weapon_durability_cost(&item_stack)) - .await; + self.damage_held_item(Self::combat_weapon_durability_cost(&item_stack)); // Vanilla `Player#attack` ends the successful-hit branch with // `causeFoodExhaustion(0.1F)`. Only landed hits exhaust; the miss/no-damage @@ -1613,12 +1627,44 @@ impl Player { .map_or(0, |w| w.item_damage_per_attack as i32) } - pub async fn sync_hand_slot(&self, slot_index: usize, stack: ItemStack) { - self.enqueue_slot_set_packet(&CSetPlayerInventory::new( + pub fn try_send_slot_set_packet(&self, packet: &CSetPlayerInventory) { + match self.client.as_ref() { + ClientPlatform::Java(java) => { + if let Ok(data) = java.serialize_packet(packet) { + java.try_enqueue_packet(data); + } + } + ClientPlatform::Bedrock(bedrock) => { + use pumpkin_protocol::bedrock::{ + client::inventory_slot::CInventorySlot, + network_item::{ContainerName, FullContainerName, NetworkItemStackDescriptor}, + }; + use pumpkin_protocol::codec::var_uint::VarUInt; + + let item_stack = &*packet.item.0; + let item_desc = NetworkItemStackDescriptor::from(item_stack); + let bedrock_packet = CInventorySlot { + container_id: VarUInt(0), + slot: VarUInt(packet.slot.0 as u32), + full_container_name: Some(FullContainerName { + container_name: ContainerName::Inventory, + dynamic_id: None, + }), + storage_item: None, + item: item_desc, + }; + if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { + bedrock.try_enqueue_packet(data); + } + } + } + } + + pub fn sync_hand_slot(&self, slot_index: usize, stack: ItemStack) { + self.try_send_slot_set_packet(&CSetPlayerInventory::new( (slot_index as i32).into(), &ItemStackSerializer::from(stack.clone()), - )) - .await; + )); if slot_index == self.inventory.get_selected_slot() as usize { self.living_entity @@ -1631,7 +1677,7 @@ impl Player { /// Applies `amount` durability damage to the item in `slot`. /// Broadcasts an [`EntityStatus`] break event and syncs the slot if the item is destroyed. - pub async fn damage_item_in_slot(&self, slot: &EquipmentSlot, amount: i32) -> bool { + pub fn damage_item_in_slot(&self, slot: &EquipmentSlot, amount: i32) -> bool { if matches!( self.gamemode.load(), GameMode::Creative | GameMode::Spectator @@ -1652,12 +1698,13 @@ impl Player { EquipmentSlot::Body(_) | EquipmentSlot::Saddle(_) => return false, }; - let mut stack = self.inventory().get_stack(slot_index).await; + let mut stack = self.inventory.get_slot(slot_index); let result = stack.damage_item(amount); let updated = (result != pumpkin_data::item_stack::DamageResult::Untouched) .then_some((result, stack.clone())); if let Some((result, updated_stack)) = updated { + self.inventory.set_slot(slot_index, updated_stack.clone()); if let Some(server) = self.world().server.upgrade() && let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) { @@ -1666,7 +1713,7 @@ impl Player { updated_stack.item.registry_key.to_string(), amount, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } // Send the break status before clearing the slot so the client can // use the item texture for break particles. @@ -1678,7 +1725,7 @@ impl Player { player_arc, updated_stack.item.registry_key.to_string(), ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } self.increment_stat( statistics::StatisticCategory::Broken, @@ -1692,11 +1739,10 @@ impl Player { ); } - self.enqueue_slot_set_packet(&CSetPlayerInventory::new( + self.try_send_slot_set_packet(&CSetPlayerInventory::new( (slot_index as i32).into(), &ItemStackSerializer::from(updated_stack.clone()), - )) - .await; + )); self.living_entity .send_equipment_changes(&[(slot.clone(), updated_stack)]); @@ -1708,12 +1754,11 @@ impl Player { } /// Convenience wrapper – damages the currently held (main-hand) item. - pub async fn damage_held_item(&self, amount: i32) -> bool { + pub fn damage_held_item(&self, amount: i32) -> bool { self.damage_item_in_slot(&EquipmentSlot::MAIN_HAND, amount) - .await } - pub async fn apply_tool_damage_for_block_break(&self, state: &BlockState) { + pub fn apply_tool_damage_for_block_break(&self, state: &BlockState) { if matches!( self.gamemode.load(), GameMode::Creative | GameMode::Spectator @@ -1732,7 +1777,7 @@ impl Player { .map_or(0, |tool| tool.damage_per_block as i32); if damage > 0 { - self.damage_held_item(damage).await; + self.damage_held_item(damage); } } @@ -1745,7 +1790,11 @@ impl Player { forced: bool, ) -> bool { if !forced - && let Some(respawn_point) = self.respawn_point.lock().await.as_ref() + && let Some(respawn_point) = self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() && dimension == respawn_point.dimension && block_pos == respawn_point.position { @@ -1775,7 +1824,10 @@ impl Player { ) .await; - *self.respawn_point.lock().await = Some(RespawnPoint { + *self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(RespawnPoint { dimension, position: block_pos, yaw, @@ -1803,8 +1855,13 @@ impl Player { type BedProperties = pumpkin_data::block_properties::WhiteBedLikeProperties; type AnchorProperties = pumpkin_data::block_properties::RespawnAnchorLikeProperties; - let respawn_guard = self.respawn_point.lock().await; - let respawn_point = respawn_guard.as_ref()?; + let respawn_point = { + let respawn_guard = self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + respawn_guard.clone()? + }; let world = if self.world().dimension == respawn_point.dimension { self.world() } else if let Some(server) = self.world().server.upgrade() { @@ -2532,7 +2589,7 @@ impl Player { .block_registry .broken(&world, block, &p, &pos, &server, state); } - p.apply_tool_damage_for_block_break(state).await; + p.apply_tool_damage_for_block_break(state); if can_harvest { p.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } @@ -2618,16 +2675,14 @@ impl Player { .swap(speed.to_bits(), Ordering::Relaxed); let speed_changed = old_speed != speed.to_bits(); if stage != self.current_block_destroy_stage.load(Ordering::Relaxed) || speed_changed { - world - .set_block_breaking( - &self.living_entity.entity, - location, - BlockBreakingProgress::Update { - stage, - speed: speed_changed.then_some(speed), - }, - ) - .await; + world.set_block_breaking( + &self.living_entity.entity, + location, + BlockBreakingProgress::Update { + stage, + speed: speed_changed.then_some(speed), + }, + ); self.current_block_destroy_stage .store(stage, Ordering::Relaxed); } @@ -2642,16 +2697,18 @@ impl Player { if was_mining || stage >= 0 { let pos = *self.mining_pos.lock().await; - self.world() - .set_block_breaking(&self.living_entity.entity, pos, BlockBreakingProgress::Stop) - .await; + self.world().set_block_breaking( + &self.living_entity.entity, + pos, + BlockBreakingProgress::Stop, + ); } } pub async fn jump(&self) { self.stats .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .increment_custom(statistics::CustomStatistic::Jump, 1); if self.living_entity.entity.is_sprinting() { self.add_exhaustion(0.2).await; @@ -2786,10 +2843,13 @@ impl Player { /// Updates the current abilities the player has. pub async fn send_abilities_update(&self) { + let abilities = *self + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); match self.client.as_ref() { ClientPlatform::Java(java) => { let mut b = 0; - let abilities = &self.abilities.lock().await; if abilities.invulnerable { b |= 1; @@ -2809,7 +2869,6 @@ impl Player { } } ClientPlatform::Bedrock(bedrock) => { - let abilities = self.abilities.lock().await; let is_op = self.permission_lvl.load() == PermissionLvl::Four; let is_spectator = self.gamemode.load() == GameMode::Spectator; @@ -2900,16 +2959,21 @@ impl Player { pub async fn send_stats(&self) { if let ClientPlatform::Java(java) = self.client.as_ref() { - let stats_guard = self.stats.lock().await; - let packet_stats: Vec = stats_guard - .stats - .iter() - .map(|((category, stat), value)| Statistic { - category_id: VarInt(*category), - statistic_id: VarInt(*stat), - value: VarInt(*value), - }) - .collect(); + let packet_stats: Vec = { + let stats_guard = self + .stats + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stats_guard + .stats + .iter() + .map(|((category, stat), value)| Statistic { + category_id: VarInt(*category), + statistic_id: VarInt(*stat), + value: VarInt(*value), + }) + .collect() + }; let packet = CAwardStats { stats: &packet_stats, @@ -2932,13 +2996,15 @@ impl Player { } } - pub async fn get_stat(&self, category: statistics::StatisticCategory, stat: i32) -> i32 { - self.stats.lock().await.get(category, stat) + pub fn get_stat(&self, category: statistics::StatisticCategory, stat: i32) -> i32 { + self.stats + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(category, stat) } - pub async fn get_custom_stat(&self, stat: statistics::CustomStatistic) -> i32 { + pub fn get_custom_stat(&self, stat: statistics::CustomStatistic) -> i32 { self.get_stat(statistics::StatisticCategory::Custom, stat as i32) - .await } pub fn set_custom_stat(&self, stat: statistics::CustomStatistic, value: i32) { @@ -2949,10 +3015,13 @@ impl Player { self.increment_stat(statistics::StatisticCategory::Custom, stat as i32, amount); } - pub async fn get_movement_statistic(&self) -> statistics::CustomStatistic { + pub fn get_movement_statistic(&self) -> statistics::CustomStatistic { let entity = self.get_entity(); if entity.has_vehicle() { - let vehicle = entity.vehicle.lock().await; + let vehicle = entity + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(vehicle) = vehicle.as_ref() { let entity_type = vehicle.get_entity().entity_type; if entity_type == &EntityType::OAK_BOAT @@ -3308,12 +3377,12 @@ impl Player { } // --- Experience & Leveling API --- - pub async fn add_experience(self: &Arc, points: i32) { - self.add_experience_points(points).await; + pub fn add_experience(self: &Arc, points: i32) { + self.add_experience_points(points); } - pub async fn add_levels(&self, levels: i32) { - self.add_experience_levels(levels).await; + pub fn add_levels(&self, levels: i32) { + self.add_experience_levels(levels); } pub fn get_experience_level(&self) -> i32 { @@ -3328,24 +3397,27 @@ impl Player { self.experience_points.load(Ordering::Relaxed) } - pub async fn set_experience_progress(&self, progress: f32) { + pub fn set_experience_progress(&self, progress: f32) { let level = self.get_experience_level(); let max_points = experience::points_in_level(level); let points = (progress.clamp(0.0, 1.0) * max_points as f32) as i32; - self.set_experience(level, progress, points).await; + self.set_experience(level, progress, points); } - pub async fn set_total_experience(&self, points: i32) { - self.set_experience_points(points).await; + pub fn set_total_experience(&self, points: i32) { + self.set_experience_points(points); } // --- Item Cooldown System --- - pub async fn set_item_cooldown(&self, item_id: &str, ticks: i32) { - self.start_cooldown(item_id.to_string(), ticks).await; + pub fn set_item_cooldown(&self, item_id: &str, ticks: i32) { + self.start_cooldown(item_id.to_string(), ticks); } - pub async fn get_item_cooldown(&self, item_id: &str) -> Option { - let cooldowns = self.item_cooldowns.lock().await; + pub fn get_item_cooldown(&self, item_id: &str) -> Option { + let cooldowns = self + .item_cooldowns + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(cooldown) = cooldowns.get(item_id) { let current_tick = self.tick_counter.load(Ordering::Relaxed); let elapsed = current_tick - cooldown.start_tick; @@ -3356,8 +3428,8 @@ impl Player { None } - pub async fn has_item_cooldown(&self, item_id: &str) -> bool { - self.is_on_cooldown(item_id).await + pub fn has_item_cooldown(&self, item_id: &str) -> bool { + self.is_on_cooldown(item_id) } // --- Tab List & Display Names --- @@ -3716,7 +3788,12 @@ impl Player { } pub async fn add_exhaustion(&self, exhaustion: f32) { - if self.abilities.lock().await.invulnerable { + if self + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .invulnerable + { return; } let mut exhaustion_event = @@ -3760,7 +3837,11 @@ impl Player { source: Option<&dyn crate::entity::EntityBase>, cause: Option<&dyn crate::entity::EntityBase>, ) -> bool { - if self.abilities.try_lock().is_ok_and(|a| a.invulnerable) + if self + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .invulnerable && damage_type != pumpkin_data::damage::DamageType::GENERIC_KILL && damage_type != pumpkin_data::damage::DamageType::OUT_OF_WORLD { @@ -3958,7 +4039,10 @@ impl Player { } pub async fn set_allow_flight(&self, allow: bool) { - self.abilities.lock().await.allow_flying = allow; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .allow_flying = allow; self.send_abilities_update().await; } @@ -3966,22 +4050,34 @@ impl Player { if flying { self.living_entity.fall_distance.store(0.0); } - self.abilities.lock().await.flying = flying; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .flying = flying; self.send_abilities_update().await; } pub async fn set_fly_speed(&self, speed: f32) { - self.abilities.lock().await.fly_speed = speed; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .fly_speed = speed; self.send_abilities_update().await; } pub async fn set_walk_speed(&self, speed: f32) { - self.abilities.lock().await.walk_speed = speed; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .walk_speed = speed; self.send_abilities_update().await; } pub async fn set_invulnerable(&self, invulnerable: bool) { - self.abilities.lock().await.invulnerable = invulnerable; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .invulnerable = invulnerable; self.send_abilities_update().await; } @@ -4219,7 +4315,7 @@ impl Player { // self.previous_gamemode.store(self.previous_gamemode.load()); { // Use another scope so that we instantly unlock `abilities`. - let mut abilities = self.abilities.lock().await; + let mut abilities = self.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner); abilities.set_for_gamemode(gamemode); }; self.send_abilities_update().await; @@ -4603,7 +4699,7 @@ impl Player { } /// Sets the player's experience level and notifies the client. - pub async fn set_experience(&self, level: i32, progress: f32, points: i32) { + pub fn set_experience(&self, level: i32, progress: f32, points: i32) { let old_level = self.experience_level.load(Ordering::Relaxed); if old_level != level && let Some(server) = self.world().server.upgrade() @@ -4614,7 +4710,7 @@ impl Player { old_level, level, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } // TODO: These should be atomic together, not isolated; make a struct containing these. can cause ABA issues self.experience_level.store(level, Ordering::Relaxed); @@ -4624,17 +4720,16 @@ impl Player { self.tick_experience(); if self.has_client_loaded() { - self.send_client_packet(&CSetExperience::new( + self.try_send_client_packet(&CSetExperience::new( progress.clamp(0.0, 1.0), level.into(), points.into(), - )) - .await; + )); } } /// Sets the player's experience level directly. - pub async fn set_experience_level(&self, new_level: i32, keep_progress: bool) { + pub fn set_experience_level(&self, new_level: i32, keep_progress: bool) { let progress = self.experience_progress.load(); let mut points = self.experience_points.load(Ordering::Relaxed); @@ -4651,7 +4746,7 @@ impl Player { points = (points as f32 * scale) as i32; } - self.set_experience(new_level, progress, points).await; + self.set_experience(new_level, progress, points); } pub fn add_effect(&self, effect: Effect) { @@ -4781,14 +4876,14 @@ impl Player { } /// Add experience levels to the player. - pub async fn add_experience_levels(&self, added_levels: i32) { + pub fn add_experience_levels(&self, added_levels: i32) { let current_level = self.experience_level.load(Ordering::Relaxed); let new_level = current_level + added_levels; - self.set_experience_level(new_level, true).await; + self.set_experience_level(new_level, true); } /// Set the player's experience points directly. Returns `true` if successful. - pub async fn set_experience_points(&self, new_points: i32) -> bool { + pub fn set_experience_points(&self, new_points: i32) -> bool { let current_points = self.experience_points.load(Ordering::Relaxed); if new_points == current_points { @@ -4803,17 +4898,16 @@ impl Player { } let progress = new_points as f32 / max_points as f32; - self.set_experience(current_level, progress, new_points) - .await; + self.set_experience(current_level, progress, new_points); true } /// Add experience points to the player. - pub async fn add_experience_points(self: &Arc, mut added_points: i32) { + pub fn add_experience_points(self: &Arc, mut added_points: i32) { let server = self.world().server.upgrade(); if let Some(server) = server { let mut event = PlayerExpChangeEvent::new(self.clone(), added_points); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); added_points = event.amount; } @@ -4827,10 +4921,10 @@ impl Player { let (new_level, new_points) = experience::total_to_level_and_points(safe_new_total); let progress = experience::progress_in_level(new_points, new_level); - self.set_experience(new_level, progress, new_points).await; + self.set_experience(new_level, progress, new_points); } - pub async fn apply_mending_from_xp(&self, mut xp: i32) -> i32 { + pub fn apply_mending_from_xp(&self, mut xp: i32) -> i32 { if xp <= 0 { return xp; } @@ -4849,7 +4943,7 @@ impl Player { } for (slot_index, equipment_slot) in slot_pairs { - let stack = self.inventory().get_stack(slot_index).await; + let stack = self.inventory.get_slot(slot_index); if stack.get_enchantment_level(&Enchantment::MENDING) > 0 && stack.get_damage() > 0 { candidates.push((slot_index, equipment_slot, stack)); } @@ -4868,18 +4962,15 @@ impl Player { } let updated_stack = stack.clone(); - self.inventory() - .set_stack(slot_index, updated_stack.clone()) - .await; + self.inventory.set_slot(slot_index, updated_stack.clone()); let xp_used = (repaired + 1) / 2; xp = xp.saturating_sub(xp_used); - self.enqueue_slot_set_packet(&CSetPlayerInventory::new( + self.try_send_slot_set_packet(&CSetPlayerInventory::new( (slot_index as i32).into(), &ItemStackSerializer::from(updated_stack.clone()), - )) - .await; + )); self.living_entity .send_equipment_changes(&[(equipment_slot, updated_stack)]); @@ -5585,12 +5676,12 @@ impl Player { } /// Find arrow in inventory (main hand, offhand, or inventory slots) - pub async fn find_arrow(&self) -> Option { + pub fn find_arrow(&self) -> Option { use pumpkin_data::item::Item; - let inventory = self.inventory(); + let inventory = &self.inventory; // Check offhand first - let stack = inventory.get_stack(PlayerInventory::OFF_HAND_SLOT).await; + let stack = inventory.get_slot(PlayerInventory::OFF_HAND_SLOT); if matches!( stack.item.id, id if id == Item::ARROW.id @@ -5603,7 +5694,7 @@ impl Player { // Check hotbar and main inventory for slot in 0..PlayerInventory::MAIN_SIZE { - let stack = inventory.get_stack(slot).await; + let stack = inventory.get_slot(slot); if matches!( stack.item.id, id if id == Item::ARROW.id @@ -5619,22 +5710,22 @@ impl Player { } /// Consume one arrow from the specified slot - pub async fn consume_arrow(&self, slot: usize) -> bool { + pub fn consume_arrow(&self, slot: usize) -> bool { let gamemode = self.gamemode.load(); if gamemode == GameMode::Creative { return true; // Don't consume in creative } - let inventory = self.inventory(); - let mut stack = inventory.get_stack(slot).await; + let inventory = &self.inventory; + let mut stack = inventory.get_slot(slot); match stack.item_count { 2.. => { stack.item_count -= 1; - inventory.set_stack(slot, stack).await; + inventory.set_slot(slot, stack); true } 1 => { - inventory.set_stack(slot, ItemStack::EMPTY.clone()).await; + inventory.set_slot(slot, ItemStack::EMPTY.clone()); true } _ => false, @@ -5786,145 +5877,18 @@ impl PartialEq for Player { } impl NBTStorage for PlayerInventory { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - // Save the selected slot (hotbar) - nbt.put_int("SelectedItemSlot", i32::from(self.get_selected_slot())); + fn write_nbt(&self, nbt: &mut NbtCompound) { + // Save the selected slot (hotbar) + nbt.put_int("SelectedItemSlot", i32::from(self.get_selected_slot())); - // Create inventory list with the correct capacity (inventory size) - let mut items: Vec = Vec::with_capacity(41); - { - let main_inv = self - .main_inventory - .read() - .unwrap_or_else(std::sync::PoisonError::into_inner); - for (i, stack) in main_inv.iter().enumerate() { - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - item_compound.put_byte("Slot", i as i8); - stack.write_item_stack(&mut item_compound); - items.push(NbtTag::Compound(item_compound)); - } - } - } - - let mut equipment_compound = NbtCompound::new(); - { - let equipment_guard = self - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - for (slot, stack) in &equipment_guard.equipment { - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - stack.write_item_stack(&mut item_compound); - let vanilla_slot = match slot { - EquipmentSlot::Feet(_) => { - equipment_compound.put_compound("feet", item_compound.clone()); - Some(100i8) - } - EquipmentSlot::Legs(_) => { - equipment_compound.put_compound("legs", item_compound.clone()); - Some(101i8) - } - EquipmentSlot::Chest(_) => { - equipment_compound.put_compound("chest", item_compound.clone()); - Some(102i8) - } - EquipmentSlot::Head(_) => { - equipment_compound.put_compound("head", item_compound.clone()); - Some(103i8) - } - EquipmentSlot::OffHand(_) => { - equipment_compound.put_compound("offhand", item_compound.clone()); - Some(-106i8) - } - _ => None, - }; - if let Some(slot_byte) = vanilla_slot { - let mut inv_item_compound = NbtCompound::new(); - inv_item_compound.put_byte("Slot", slot_byte); - stack.write_item_stack(&mut inv_item_compound); - items.push(NbtTag::Compound(inv_item_compound)); - } - } - } - } - nbt.put_compound("equipment", equipment_compound); - nbt.put("Inventory", NbtTag::List(items)); - }) - } - - fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - // Read selected hotbar slot - self.set_selected_slot(nbt.get_int("SelectedItemSlot").unwrap_or(0) as u8); - // Process inventory list - if let Some(inventory_list) = nbt.get_list("Inventory") { - for tag in inventory_list { - if let Some(item_compound) = tag.extract_compound() - && let Some(slot_byte) = item_compound.get_byte("Slot") - { - let slot = match slot_byte { - 100 => 36, // feet - 101 => 37, // legs - 102 => 38, // chest - 103 => 39, // head - -106 => 40, // offhand - s if (0..=40).contains(&s) => s as usize, - _ => continue, - }; - if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { - self.set_stack(slot, item_stack).await; - } - } - } - } - - if let Some(equipment) = nbt.get_compound("equipment") { - if let Some(offhand) = equipment.get_compound("offhand") - && let Some(item_stack) = ItemStack::read_item_stack(offhand) - { - self.set_stack(40, item_stack).await; - } - - if let Some(head) = equipment.get_compound("head") - && let Some(item_stack) = ItemStack::read_item_stack(head) - { - self.set_stack(39, item_stack).await; - } - - if let Some(chest) = equipment.get_compound("chest") - && let Some(item_stack) = ItemStack::read_item_stack(chest) - { - self.set_stack(38, item_stack).await; - } - - if let Some(legs) = equipment.get_compound("legs") - && let Some(item_stack) = ItemStack::read_item_stack(legs) - { - self.set_stack(37, item_stack).await; - } - - if let Some(feet) = equipment.get_compound("feet") - && let Some(item_stack) = ItemStack::read_item_stack(feet) - { - self.set_stack(36, item_stack).await; - } - } - }) - } -} - -impl NBTStorageInit for PlayerInventory {} - -impl NBTStorage for EnderChestInventory { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - // Create item list with the correct capacity (inventory size) - let mut items: Vec = Vec::with_capacity(Self::INVENTORY_SIZE); - let ec_items = self.items.read().await; - for (i, stack) in ec_items.iter().enumerate() { + // Create inventory list with the correct capacity (inventory size) + let mut items: Vec = Vec::with_capacity(41); + { + let main_inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (i, stack) in main_inv.iter().enumerate() { if !stack.is_empty() { let mut item_compound = NbtCompound::new(); item_compound.put_byte("Slot", i as i8); @@ -5932,28 +5896,169 @@ impl NBTStorage for EnderChestInventory { items.push(NbtTag::Compound(item_compound)); } } + } - nbt.put("EnderItems", NbtTag::List(items)); - }) - } - - fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - // Process item list - if let Some(item_list) = nbt.get_list("EnderItems") { - for tag in item_list { - if let Some(item_compound) = tag.extract_compound() - && let Some(slot_byte) = item_compound.get_byte("Slot") - && (0..Self::INVENTORY_SIZE as i8).contains(&slot_byte) - { - let slot = slot_byte as usize; - if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { - self.set_stack(slot, item_stack).await; + let mut equipment_compound = NbtCompound::new(); + { + let equipment_guard = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (slot, stack) in &equipment_guard.equipment { + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + stack.write_item_stack(&mut item_compound); + let vanilla_slot = match slot { + EquipmentSlot::Feet(_) => { + equipment_compound.put_compound("feet", item_compound.clone()); + Some(100i8) } + EquipmentSlot::Legs(_) => { + equipment_compound.put_compound("legs", item_compound.clone()); + Some(101i8) + } + EquipmentSlot::Chest(_) => { + equipment_compound.put_compound("chest", item_compound.clone()); + Some(102i8) + } + EquipmentSlot::Head(_) => { + equipment_compound.put_compound("head", item_compound.clone()); + Some(103i8) + } + EquipmentSlot::OffHand(_) => { + equipment_compound.put_compound("offhand", item_compound.clone()); + Some(-106i8) + } + _ => None, + }; + if let Some(slot_byte) = vanilla_slot { + let mut inv_item_compound = NbtCompound::new(); + inv_item_compound.put_byte("Slot", slot_byte); + stack.write_item_stack(&mut inv_item_compound); + items.push(NbtTag::Compound(inv_item_compound)); } } } - }) + } + nbt.put_compound("equipment", equipment_compound); + nbt.put("Inventory", NbtTag::List(items)); + } + + fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + // Read selected hotbar slot + self.set_selected_slot(nbt.get_int("SelectedItemSlot").unwrap_or(0) as u8); + // Process inventory list + let set_stack_sync = |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) = self.equipment_slots.get(&slot) { + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(slot, stack); + } + }; + + if let Some(inventory_list) = nbt.get_list("Inventory") { + for tag in inventory_list { + if let Some(item_compound) = tag.extract_compound() + && let Some(slot_byte) = item_compound.get_byte("Slot") + { + let slot = match slot_byte { + 100 => 36, // feet + 101 => 37, // legs + 102 => 38, // chest + 103 => 39, // head + -106 => 40, // offhand + s if (0..=40).contains(&s) => s as usize, + _ => continue, + }; + if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { + set_stack_sync(slot, item_stack); + } + } + } + } + + if let Some(equipment) = nbt.get_compound("equipment") { + if let Some(offhand) = equipment.get_compound("offhand") + && let Some(item_stack) = ItemStack::read_item_stack(offhand) + { + set_stack_sync(40, item_stack); + } + + if let Some(head) = equipment.get_compound("head") + && let Some(item_stack) = ItemStack::read_item_stack(head) + { + set_stack_sync(39, item_stack); + } + + if let Some(chest) = equipment.get_compound("chest") + && let Some(item_stack) = ItemStack::read_item_stack(chest) + { + set_stack_sync(38, item_stack); + } + + if let Some(legs) = equipment.get_compound("legs") + && let Some(item_stack) = ItemStack::read_item_stack(legs) + { + set_stack_sync(37, item_stack); + } + + if let Some(feet) = equipment.get_compound("feet") + && let Some(item_stack) = ItemStack::read_item_stack(feet) + { + set_stack_sync(36, item_stack); + } + } + } +} + +impl NBTStorageInit for PlayerInventory {} + +impl NBTStorage for EnderChestInventory { + fn write_nbt(&self, nbt: &mut NbtCompound) { + // Create item list with the correct capacity (inventory size) + let mut items: Vec = Vec::with_capacity(Self::INVENTORY_SIZE); + let ec_items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for (i, stack) in ec_items.iter().enumerate() { + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + item_compound.put_byte("Slot", i as i8); + stack.write_item_stack(&mut item_compound); + items.push(NbtTag::Compound(item_compound)); + } + } + + nbt.put("EnderItems", NbtTag::List(items)); + } + + fn read_nbt_non_mut(&self, nbt: &NbtCompound) { + // Process item list + if let Some(item_list) = nbt.get_list("EnderItems") { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for tag in item_list { + if let Some(item_compound) = tag.extract_compound() + && let Some(slot_byte) = item_compound.get_byte("Slot") + && (0..Self::INVENTORY_SIZE as i8).contains(&slot_byte) + { + let slot = slot_byte as usize; + if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { + items[slot] = item_stack; + } + } + } + } } } @@ -6061,281 +6166,301 @@ impl EntityBase for Player { TextComponent::text(self.gameprofile.name.clone()) } - fn get_display_name(&self) -> EntityBaseFuture<'_, TextComponent> { - Box::pin(async move { - if let Some(display_name) = self.display_name.lock().await.as_ref() { - return display_name.clone(); - } - let name = self.get_name(); - let name_clone = name.clone(); - let mut name = name.click_event(ClickEvent::SuggestCommand { - command: format!("/tell {} ", self.gameprofile.name.clone()).into(), - }); - name = name.hover_event(HoverEvent::show_entity( - self.living_entity.entity.entity_uuid.to_string(), - self.living_entity.entity.entity_type.resource_name.into(), - Some(name_clone), - )); - name.insertion(self.gameprofile.name.clone()) - }) + fn get_display_name(&self) -> TextComponent { + if let Some(display_name) = self + .display_name + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + return display_name.clone(); + } + let name = self.get_name(); + let name_clone = name.clone(); + let mut name = name.click_event(ClickEvent::SuggestCommand { + command: format!("/tell {} ", self.gameprofile.name.clone()).into(), + }); + name = name.hover_event(HoverEvent::show_entity( + self.living_entity.entity.entity_uuid.to_string(), + self.living_entity.entity.entity_type.resource_name.into(), + Some(name_clone), + )); + name.insertion(self.gameprofile.name.clone()) } fn cast_any(&self) -> &dyn std::any::Any { self } - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("DataVersion", DATA_VERSION); - self.inventory.write_nbt(nbt).await; - self.ender_chest_inventory.write_nbt(nbt).await; + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_int("DataVersion", DATA_VERSION); + self.inventory.write_nbt(nbt); + self.ender_chest_inventory.write_nbt(nbt); - self.abilities.lock().await.write_nbt(nbt).await; + self.abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .write_nbt(nbt); - let total_exp = - experience::points_to_level(self.experience_level.load(Ordering::Relaxed)) - + self.experience_points.load(Ordering::Relaxed); - nbt.put_float("XpP", self.experience_progress.load()); - nbt.put_int("XpLevel", self.experience_level.load(Ordering::Relaxed)); - nbt.put_int("XpTotal", total_exp); - nbt.put_int("XpSeed", self.enchantment_seed.load(Ordering::Relaxed)); - nbt.put_int("Score", self.score.load(Ordering::Relaxed)); - nbt.put_short("SleepTimer", self.sleeping_since.load().unwrap_or(0) as i16); + let total_exp = experience::points_to_level(self.experience_level.load(Ordering::Relaxed)) + + self.experience_points.load(Ordering::Relaxed); + nbt.put_float("XpP", self.experience_progress.load()); + nbt.put_int("XpLevel", self.experience_level.load(Ordering::Relaxed)); + nbt.put_int("XpTotal", total_exp); + nbt.put_int("XpSeed", self.enchantment_seed.load(Ordering::Relaxed)); + nbt.put_int("Score", self.score.load(Ordering::Relaxed)); + nbt.put_short("SleepTimer", self.sleeping_since.load().unwrap_or(0) as i16); - nbt.put_int("playerGameType", self.gamemode.load() as i32); - if let Some(previous_gamemode) = self.previous_gamemode.load() { - nbt.put_int("previousPlayerGameType", previous_gamemode as i32); - } + nbt.put_int("playerGameType", self.gamemode.load() as i32); + if let Some(previous_gamemode) = self.previous_gamemode.load() { + nbt.put_int("previousPlayerGameType", previous_gamemode as i32); + } - nbt.put_bool("seenCredits", self.seen_credits.load(Ordering::Relaxed)); - nbt.put_bool( - "spawn_extra_particles_on_fall", - self.spawn_extra_particles_on_fall.load(Ordering::Relaxed), - ); - nbt.put_bool( - "HasPlayedBefore", - self.has_played_before.load(Ordering::Relaxed), - ); + nbt.put_bool("seenCredits", self.seen_credits.load(Ordering::Relaxed)); + nbt.put_bool( + "spawn_extra_particles_on_fall", + self.spawn_extra_particles_on_fall.load(Ordering::Relaxed), + ); + nbt.put_bool( + "HasPlayedBefore", + self.has_played_before.load(Ordering::Relaxed), + ); - // Store food level, saturation, exhaustion, and tick timer - self.hunger_manager.write_nbt(nbt).await; + // Store food level, saturation, exhaustion, and tick timer + self.hunger_manager.write_nbt(nbt); - let air_supply = self - .breath_manager - .air_supply + let air_supply = self + .breath_manager + .air_supply + .load(Ordering::Relaxed) + .clamp(0, super::breath::MAX_AIR); + nbt.put_short("Air", air_supply as i16); + nbt.put_int("AirSupply", air_supply); + nbt.put_int( + "DrowningTick", + self.breath_manager + .drowning_tick .load(Ordering::Relaxed) - .clamp(0, super::breath::MAX_AIR); - nbt.put_short("Air", air_supply as i16); - nbt.put_int("AirSupply", air_supply); - nbt.put_int( - "DrowningTick", - self.breath_manager - .drowning_tick - .load(Ordering::Relaxed) - .clamp(0, super::breath::DROWNING_INTERVAL - 1), - ); + .clamp(0, super::breath::DROWNING_INTERVAL - 1), + ); + nbt.put_string( + "Dimension", + self.world().dimension.minecraft_name.to_string(), + ); + + if let Some(respawn) = self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + nbt.put_int("SpawnX", respawn.position.0.x); + nbt.put_int("SpawnY", respawn.position.0.y); + nbt.put_int("SpawnZ", respawn.position.0.z); nbt.put_string( - "Dimension", - self.world().dimension.minecraft_name.to_string(), + "SpawnDimension", + respawn.dimension.minecraft_name.to_owned(), ); + nbt.put_bool("SpawnForced", respawn.force); - if let Some(respawn) = self.respawn_point.lock().await.as_ref() { - nbt.put_int("SpawnX", respawn.position.0.x); - nbt.put_int("SpawnY", respawn.position.0.y); - nbt.put_int("SpawnZ", respawn.position.0.z); - nbt.put_string( - "SpawnDimension", - respawn.dimension.minecraft_name.to_owned(), - ); - nbt.put_bool("SpawnForced", respawn.force); + let mut respawn_compound = NbtCompound::new(); + respawn_compound.put_string("dimension", respawn.dimension.minecraft_name.to_string()); + respawn_compound.put( + "pos", + NbtTag::IntArray(vec![ + respawn.position.0.x, + respawn.position.0.y, + respawn.position.0.z, + ]), + ); + respawn_compound.put_float("angle", respawn.yaw); + respawn_compound.put_bool("forced", respawn.force); + nbt.put_compound("respawn", respawn_compound); + } - let mut respawn_compound = NbtCompound::new(); - respawn_compound - .put_string("dimension", respawn.dimension.minecraft_name.to_string()); - respawn_compound.put( - "pos", - NbtTag::IntArray(vec![ - respawn.position.0.x, - respawn.position.0.y, - respawn.position.0.z, - ]), - ); - respawn_compound.put_float("angle", respawn.yaw); - respawn_compound.put_bool("forced", respawn.force); - nbt.put_compound("respawn", respawn_compound); - } - - let vehicle_uuid = self - .living_entity - .entity - .vehicle - .lock() - .await - .as_ref() - .map(|vehicle| vehicle.get_entity().entity_uuid) - .or_else(|| self.root_vehicle_uuid.load()); - if let Some(vehicle_uuid) = vehicle_uuid { - write_root_vehicle(nbt, vehicle_uuid); - } - self.stats.lock().await.write_nbt(nbt); - }) + let vehicle_uuid = self + .living_entity + .entity + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + .map(|vehicle| vehicle.get_entity().entity_uuid) + .or_else(|| self.root_vehicle_uuid.load()); + if let Some(vehicle_uuid) = vehicle_uuid { + write_root_vehicle(nbt, vehicle_uuid); + } + self.stats + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .write_nbt(nbt); } #[expect(clippy::too_many_lines)] - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.inventory.read_nbt_non_mut(nbt).await; - self.ender_chest_inventory.read_nbt_non_mut(nbt).await; + fn read_custom_nbt(&self, nbt: &NbtCompound) { + self.inventory.read_nbt_non_mut(nbt); + self.ender_chest_inventory.read_nbt_non_mut(nbt); - let xp_p = nbt.get_float("XpP").unwrap_or(0.0); - let xp_level = nbt.get_int("XpLevel"); - let total_exp = nbt.get_int("XpTotal").unwrap_or(0); + let xp_p = nbt.get_float("XpP").unwrap_or(0.0); + let xp_level = nbt.get_int("XpLevel"); + let total_exp = nbt.get_int("XpTotal").unwrap_or(0); - if let Some(level) = xp_level { - self.experience_level.store(level, Ordering::Relaxed); - self.experience_progress.store(xp_p); - let points = (xp_p * experience::points_in_level(level) as f32).round() as i32; - self.experience_points.store(points, Ordering::Relaxed); - } else { - let (level, points) = experience::total_to_level_and_points(total_exp); - let progress = experience::progress_in_level(level, points); - self.experience_level.store(level, Ordering::Relaxed); - self.experience_progress.store(progress); - self.experience_points.store(points, Ordering::Relaxed); + if let Some(level) = xp_level { + self.experience_level.store(level, Ordering::Relaxed); + self.experience_progress.store(xp_p); + let points = (xp_p * experience::points_in_level(level) as f32).round() as i32; + self.experience_points.store(points, Ordering::Relaxed); + } else { + let (level, points) = experience::total_to_level_and_points(total_exp); + let progress = experience::progress_in_level(level, points); + self.experience_level.store(level, Ordering::Relaxed); + self.experience_progress.store(progress); + self.experience_points.store(points, Ordering::Relaxed); + } + + self.enchantment_seed.store( + nbt.get_int("XpSeed").unwrap_or_else(rand::random), + Ordering::Relaxed, + ); + + self.score + .store(nbt.get_int("Score").unwrap_or(0), Ordering::Relaxed); + if let Some(sleep_timer) = nbt.get_short("SleepTimer") + && sleep_timer > 0 + { + self.sleeping_since.store(Some(sleep_timer as u8)); + } + + self.seen_credits.store( + nbt.get_bool("seenCredits").unwrap_or(false), + Ordering::Relaxed, + ); + self.spawn_extra_particles_on_fall.store( + nbt.get_bool("spawn_extra_particles_on_fall") + .unwrap_or(false), + Ordering::Relaxed, + ); + + let gamemode = nbt + .get_int("playerGameType") + .or_else(|| nbt.get_byte("playerGameType").map(i32::from)) + .and_then(|val| GameMode::try_from(val).ok()) + .unwrap_or_else(|| self.gamemode.load()); + + self.gamemode.store(gamemode); + + self.previous_gamemode.store( + nbt.get_int("previousPlayerGameType") + .or_else(|| nbt.get_byte("previousPlayerGameType").map(i32::from)) + .and_then(|val| GameMode::try_from(val).ok()), + ); + + { + let mut abilities = self + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + abilities.set_for_gamemode(gamemode); + abilities.read_nbt(nbt); + if gamemode == GameMode::Creative { + abilities.allow_flying = true; + abilities.creative = true; + abilities.invulnerable = true; + } else if gamemode == GameMode::Spectator { + abilities.allow_flying = true; + abilities.creative = false; + abilities.invulnerable = true; } + } - self.enchantment_seed.store( - nbt.get_int("XpSeed").unwrap_or_else(rand::random), - Ordering::Relaxed, - ); - - self.score - .store(nbt.get_int("Score").unwrap_or(0), Ordering::Relaxed); - if let Some(sleep_timer) = nbt.get_short("SleepTimer") - && sleep_timer > 0 - { - self.sleeping_since.store(Some(sleep_timer as u8)); - } - - self.seen_credits.store( - nbt.get_bool("seenCredits").unwrap_or(false), - Ordering::Relaxed, - ); - self.spawn_extra_particles_on_fall.store( - nbt.get_bool("spawn_extra_particles_on_fall") - .unwrap_or(false), - Ordering::Relaxed, - ); - - let gamemode = nbt - .get_int("playerGameType") - .or_else(|| nbt.get_byte("playerGameType").map(i32::from)) - .and_then(|val| GameMode::try_from(val).ok()) - .unwrap_or_else(|| self.gamemode.load()); - - self.gamemode.store(gamemode); - - self.previous_gamemode.store( - nbt.get_int("previousPlayerGameType") - .or_else(|| nbt.get_byte("previousPlayerGameType").map(i32::from)) - .and_then(|val| GameMode::try_from(val).ok()), - ); - - { - let mut abilities = self.abilities.lock().await; - abilities.set_for_gamemode(gamemode); - abilities.read_nbt(nbt); - if gamemode == GameMode::Creative { - abilities.allow_flying = true; - abilities.creative = true; - abilities.invulnerable = true; - } else if gamemode == GameMode::Spectator { - abilities.allow_flying = true; - abilities.creative = false; - abilities.invulnerable = true; - } - } - - self.living_entity.entity.invulnerable.store( - matches!(gamemode, GameMode::Creative | GameMode::Spectator), - Ordering::Relaxed, - ); + self.living_entity.entity.invulnerable.store( + matches!(gamemode, GameMode::Creative | GameMode::Spectator), + Ordering::Relaxed, + ); + self.living_entity + .entity + .no_physics + .store(gamemode == GameMode::Spectator, Ordering::Relaxed); + if gamemode == GameMode::Spectator { self.living_entity .entity - .no_physics - .store(gamemode == GameMode::Spectator, Ordering::Relaxed); - if gamemode == GameMode::Spectator { - self.living_entity - .entity - .on_ground - .store(false, Ordering::Relaxed); - } + .on_ground + .store(false, Ordering::Relaxed); + } - self.has_played_before.store( - nbt.get_bool("HasPlayedBefore").unwrap_or(false), + self.has_played_before.store( + nbt.get_bool("HasPlayedBefore").unwrap_or(false), + Ordering::Relaxed, + ); + + self.hunger_manager.read_nbt_non_mut(nbt); + + if let Some(air) = nbt + .get_short("Air") + .map(i32::from) + .or_else(|| nbt.get_int("AirSupply")) + { + self.breath_manager + .air_supply + .store(air.clamp(0, super::breath::MAX_AIR), Ordering::Relaxed); + } + if let Some(tick) = nbt.get_int("DrowningTick") { + self.breath_manager.drowning_tick.store( + tick.clamp(0, super::breath::DROWNING_INTERVAL - 1), Ordering::Relaxed, ); + } - self.hunger_manager.read_nbt_non_mut(nbt).await; - - if let Some(air) = nbt - .get_short("Air") - .map(i32::from) - .or_else(|| nbt.get_int("AirSupply")) + // Load any saved spawnpoint data (both vanilla "respawn" compound and legacy SpawnX/SpawnY/SpawnZ) + if let Some(respawn_compound) = nbt.get_compound("respawn") { + let dim = respawn_compound + .get_string("dimension") + .and_then(|s| Dimension::from_name(s).cloned()) + .unwrap_or_else(|| self.world().dimension.clone()); + let pos = if let Some(pos_array) = respawn_compound.get_int_array("pos") + && pos_array.len() >= 3 { - self.breath_manager - .air_supply - .store(air.clamp(0, super::breath::MAX_AIR), Ordering::Relaxed); - } - if let Some(tick) = nbt.get_int("DrowningTick") { - self.breath_manager.drowning_tick.store( - tick.clamp(0, super::breath::DROWNING_INTERVAL - 1), - Ordering::Relaxed, - ); - } - - // Load any saved spawnpoint data (both vanilla "respawn" compound and legacy SpawnX/SpawnY/SpawnZ) - if let Some(respawn_compound) = nbt.get_compound("respawn") { - let dim = respawn_compound - .get_string("dimension") - .and_then(|s| Dimension::from_name(s).cloned()) - .unwrap_or_else(|| self.world().dimension.clone()); - let pos = if let Some(pos_array) = respawn_compound.get_int_array("pos") - && pos_array.len() >= 3 - { - BlockPos(Vector3::new(pos_array[0], pos_array[1], pos_array[2])) - } else { - BlockPos(Vector3::new(0, 0, 0)) - }; - let yaw = respawn_compound.get_float("angle").unwrap_or(0.0); - let force = respawn_compound.get_bool("forced").unwrap_or(false); - *self.respawn_point.lock().await = Some(RespawnPoint { - dimension: dim, - position: pos, - yaw, - force, - }); - } else if let (Some(x), Some(y), Some(z)) = ( - nbt.get_int("SpawnX"), - nbt.get_int("SpawnY"), - nbt.get_int("SpawnZ"), - ) { - let dim = nbt - .get_string("SpawnDimension") - .and_then(|s| Dimension::from_name(s).cloned()) - .unwrap_or_else(|| self.world().dimension.clone()); - let force = nbt.get_bool("SpawnForced").unwrap_or(false); - *self.respawn_point.lock().await = Some(RespawnPoint { - dimension: dim, - position: BlockPos(Vector3::new(x, y, z)), - yaw: 0.0, - force, - }); - } - self.root_vehicle_uuid.store(read_root_vehicle(nbt)); - self.stats.lock().await.read_nbt(nbt); - }) + BlockPos(Vector3::new(pos_array[0], pos_array[1], pos_array[2])) + } else { + BlockPos(Vector3::new(0, 0, 0)) + }; + let yaw = respawn_compound.get_float("angle").unwrap_or(0.0); + let force = respawn_compound.get_bool("forced").unwrap_or(false); + *self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(RespawnPoint { + dimension: dim, + position: pos, + yaw, + force, + }); + } else if let (Some(x), Some(y), Some(z)) = ( + nbt.get_int("SpawnX"), + nbt.get_int("SpawnY"), + nbt.get_int("SpawnZ"), + ) { + let dim = nbt + .get_string("SpawnDimension") + .and_then(|s| Dimension::from_name(s).cloned()) + .unwrap_or_else(|| self.world().dimension.clone()); + let force = nbt.get_bool("SpawnForced").unwrap_or(false); + *self + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(RespawnPoint { + dimension: dim, + position: BlockPos(Vector3::new(x, y, z)), + yaw: 0.0, + force, + }); + } + self.root_vehicle_uuid.store(read_root_vehicle(nbt)); + self.stats + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .read_nbt(nbt); } fn get_experience_reward(&self, _killer: Option<&dyn EntityBase>) -> u32 { @@ -6359,6 +6484,7 @@ pub enum TitleMode { /// Represents a player's abilities and special powers. /// /// This struct contains information about the player's current abilities, such as flight, invulnerability, and creative mode. +#[derive(Clone, Copy, Debug)] pub struct Abilities { /// Indicates whether the player is invulnerable to damage. pub invulnerable: bool, @@ -6377,24 +6503,20 @@ pub struct Abilities { } impl NBTStorage for Abilities { - fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async { - let mut component = NbtCompound::new(); - component.put_bool("invulnerable", self.invulnerable); - component.put_bool("flying", self.flying); - component.put_bool("mayfly", self.allow_flying); - component.put_bool("instabuild", self.creative); - component.put_bool("mayBuild", self.allow_modify_world); - component.put_float("flySpeed", self.fly_speed); - component.put_float("walkSpeed", self.walk_speed); - nbt.put_compound("abilities", component); - }) + fn write_nbt(&self, nbt: &mut NbtCompound) { + let mut component = NbtCompound::new(); + component.put_bool("invulnerable", self.invulnerable); + component.put_bool("flying", self.flying); + component.put_bool("mayfly", self.allow_flying); + component.put_bool("instabuild", self.creative); + component.put_bool("mayBuild", self.allow_modify_world); + component.put_float("flySpeed", self.fly_speed); + component.put_float("walkSpeed", self.walk_speed); + nbt.put_compound("abilities", component); } - fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - self.read_nbt(nbt); - }) + fn read_nbt(&mut self, nbt: &mut NbtCompound) { + Abilities::read_nbt(self, nbt); } } @@ -6765,7 +6887,7 @@ impl InventoryPlayer for Player { fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()> { Box::pin(async move { - self.add_experience_levels(levels).await; + self.add_experience_levels(levels); }) } @@ -7097,7 +7219,7 @@ impl InventoryPlayer for Player { debug!("Player: adding {amount} experience points"); let player = self.world().get_player_by_uuid(self.gameprofile.id); if let Some(player) = player { - player.add_experience_points(amount).await; + player.add_experience_points(amount); } } }) diff --git a/crates/pumpkin/src/entity/player/advancement.rs b/crates/pumpkin/src/entity/player/advancement.rs index 087247c73..c01d380ce 100644 --- a/crates/pumpkin/src/entity/player/advancement.rs +++ b/crates/pumpkin/src/entity/player/advancement.rs @@ -410,12 +410,7 @@ impl PlayerAdvancement { /// Grants the rewards (like experience) associated with completing an advancement. pub fn grant_reward(player: Arc, reward: &'static AdvancementReward) { - tokio::spawn(async move { - tokio::join!( - player.add_experience_points(reward.experience), - // more reward later - ); - }); + player.add_experience_points(reward.experience); } /// award a criterion of an advancement to the player, updating its status to complete and granting rewards if applicable. @@ -454,7 +449,7 @@ impl PlayerAdvancement { .show_advancement_messages { tokio::spawn(async move { - let player_name = player.get_display_name().await; + let player_name = player.get_display_name(); let je_component = TextComponent::translate( display.frame_type.get_translation(), [player_name.clone(), advancement.name()], diff --git a/crates/pumpkin/src/entity/projectile/arrow.rs b/crates/pumpkin/src/entity/projectile/arrow.rs index 21fcc5052..59c5d9abc 100644 --- a/crates/pumpkin/src/entity/projectile/arrow.rs +++ b/crates/pumpkin/src/entity/projectile/arrow.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering}; use crate::entity::projectile::ProjectileHit; use crate::{ - entity::{Entity, EntityBase, NbtFuture, living::LivingEntity, player::Player}, + entity::{Entity, EntityBase, living::LivingEntity, player::Player}, server::Server, }; use pumpkin_data::damage::DamageType; @@ -269,31 +269,21 @@ impl ArrowEntity { } impl EntityBase for ArrowEntity { - fn write_custom_nbt<'a>( - &'a self, - nbt: &'a mut pumpkin_nbt::compound::NbtCompound, - ) -> NbtFuture<'a, ()> { - Box::pin(async move { - let item_stack = self - .item_stack - .read() - .unwrap_or_else(std::sync::PoisonError::into_inner); - Self::write_item_stack_nbt(&item_stack, nbt); - }) + fn write_custom_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { + let item_stack = self + .item_stack + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + Self::write_item_stack_nbt(&item_stack, nbt); } - fn read_custom_nbt<'a>( - &'a self, - nbt: &'a pumpkin_nbt::compound::NbtCompound, - ) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(item_stack) = Self::read_item_stack_nbt(nbt) { - *self - .item_stack - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner) = item_stack; - } - }) + fn read_custom_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) { + if let Some(item_stack) = Self::read_item_stack_nbt(nbt) { + *self + .item_stack + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) = item_stack; + } } #[allow(clippy::too_many_lines)] fn tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { diff --git a/crates/pumpkin/src/entity/projectile/evoker_fangs.rs b/crates/pumpkin/src/entity/projectile/evoker_fangs.rs index dbb78661c..6f86f0a6b 100644 --- a/crates/pumpkin/src/entity/projectile/evoker_fangs.rs +++ b/crates/pumpkin/src/entity/projectile/evoker_fangs.rs @@ -5,7 +5,7 @@ use pumpkin_data::damage::DamageType; use pumpkin_data::sound::Sound; use crate::{ - entity::{Entity, EntityBase, NbtFuture}, + entity::{Entity, EntityBase}, server::Server, }; @@ -32,24 +32,14 @@ impl EvokerFangsEntity { } impl EntityBase for EvokerFangsEntity { - fn write_custom_nbt<'a>( - &'a self, - nbt: &'a mut pumpkin_nbt::compound::NbtCompound, - ) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_int("Warmup", self.warmup_ticks.load(Ordering::Relaxed) as i32); - }) + fn write_custom_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { + nbt.put_int("Warmup", self.warmup_ticks.load(Ordering::Relaxed) as i32); } - fn read_custom_nbt<'a>( - &'a self, - nbt: &'a pumpkin_nbt::compound::NbtCompound, - ) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(warmup) = nbt.get_int("Warmup") { - self.warmup_ticks.store(warmup as u32, Ordering::Relaxed); - } - }) + fn read_custom_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) { + if let Some(warmup) = nbt.get_int("Warmup") { + self.warmup_ticks.store(warmup as u32, Ordering::Relaxed); + } } fn tick<'a>(&'a self, _caller: &'a Arc, _server: &'a Server) { diff --git a/crates/pumpkin/src/entity/projectile/fireball.rs b/crates/pumpkin/src/entity/projectile/fireball.rs index 7cf78694a..315639bd9 100644 --- a/crates/pumpkin/src/entity/projectile/fireball.rs +++ b/crates/pumpkin/src/entity/projectile/fireball.rs @@ -11,7 +11,7 @@ use pumpkin_util::math::vector3::Vector3; use crate::{ entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, projectile::{ProjectileHit, ThrownItemEntity}, projectile_deflection::ProjectileDeflectionType, }, @@ -173,25 +173,21 @@ impl FireballEntity { } impl EntityBase for FireballEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_double("acceleration_power", self.get_acceleration_power()); - nbt.put_float("ExplosionPower", self.get_explosion_power()); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_double("acceleration_power", self.get_acceleration_power()); + nbt.put_float("ExplosionPower", self.get_explosion_power()); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(accel) = nbt - .get_double("acceleration_power") - .or_else(|| nbt.get_double("power")) - { - self.set_acceleration_power(accel); - } - if let Some(exp) = nbt.get_float("ExplosionPower") { - self.set_explosion_power(exp); - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + if let Some(accel) = nbt + .get_double("acceleration_power") + .or_else(|| nbt.get_double("power")) + { + self.set_acceleration_power(accel); + } + if let Some(exp) = nbt.get_float("ExplosionPower") { + self.set_explosion_power(exp); + } } fn init_data_tracker(&self) { @@ -260,10 +256,6 @@ impl EntityBase for FireballEntity { let hit_pos = hit.hit_pos(); let power = self.get_explosion_power(); - tokio::spawn(async move { - world - .explode(hit_pos, power, crate::world::ExplosionInteraction::Mob) - .await; - }); + world.explode(hit_pos, power, crate::world::ExplosionInteraction::Mob); } } diff --git a/crates/pumpkin/src/entity/projectile/wind_charge.rs b/crates/pumpkin/src/entity/projectile/wind_charge.rs index e2dc020d0..d436119de 100644 --- a/crates/pumpkin/src/entity/projectile/wind_charge.rs +++ b/crates/pumpkin/src/entity/projectile/wind_charge.rs @@ -88,21 +88,17 @@ impl WindChargeEntity { } } - pub async fn create_explosion(&self, position: Vector3) { + pub fn create_explosion(&self, position: Vector3) { let calculator = match self.kind { WindChargeKind::Normal { .. } => WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone(), WindChargeKind::Breeze => BREEZE_WIND_CHARGE_EXPLOSION_DAMAGE_CALCULATOR.clone(), }; - self.get_entity() - .world - .load() - .explode_with_calculator( - position, - EXPLOSION_POWER, - crate::world::ExplosionInteraction::Trigger, - Some(calculator), - ) - .await; + self.get_entity().world.load().explode_with_calculator( + position, + EXPLOSION_POWER, + crate::world::ExplosionInteraction::Trigger, + Some(calculator), + ); } pub fn deflect( diff --git a/crates/pumpkin/src/entity/projectile/wither_skull.rs b/crates/pumpkin/src/entity/projectile/wither_skull.rs index 303a3a536..34aea68ba 100644 --- a/crates/pumpkin/src/entity/projectile/wither_skull.rs +++ b/crates/pumpkin/src/entity/projectile/wither_skull.rs @@ -8,7 +8,7 @@ use pumpkin_util::{Difficulty, math::vector3::Vector3}; use crate::{ entity::{ - Entity, EntityBase, NbtFuture, + Entity, EntityBase, projectile::{ProjectileHit, ThrownItemEntity}, }, server::Server, @@ -81,18 +81,14 @@ impl WitherSkullEntity { } impl EntityBase for WitherSkullEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - nbt.put_bool("dangerous", self.is_dangerous()); - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + nbt.put_bool("dangerous", self.is_dangerous()); } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - if let Some(dangerous) = nbt.get_bool("dangerous") { - self.dangerous.store(dangerous, Ordering::Relaxed); - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + if let Some(dangerous) = nbt.get_bool("dangerous") { + self.dangerous.store(dangerous, Ordering::Relaxed); + } } fn init_data_tracker(&self) { @@ -157,8 +153,6 @@ impl EntityBase for WitherSkullEntity { } let hit_pos = hit.hit_pos(); - tokio::spawn(async move { - world.explode(hit_pos, 1.0, ExplosionInteraction::Mob).await; - }); + world.explode(hit_pos, 1.0, ExplosionInteraction::Mob); } } diff --git a/crates/pumpkin/src/entity/tnt.rs b/crates/pumpkin/src/entity/tnt.rs index 274d87d33..282c0a937 100644 --- a/crates/pumpkin/src/entity/tnt.rs +++ b/crates/pumpkin/src/entity/tnt.rs @@ -65,13 +65,9 @@ impl EntityBase for TNTEntity { let world = self.entity.world.load_full(); let pos = self.entity.pos.load(); let power = self.power; - tokio::spawn(async move { - if world.level_info.load().game_rules.tnt_explodes { - world - .explode(pos, power, crate::world::ExplosionInteraction::Tnt) - .await; - } - }); + if world.level_info.load().game_rules.tnt_explodes { + world.explode(pos, power, crate::world::ExplosionInteraction::Tnt); + } } else { // Safe decrement self.fuse.store(fuse - 1, Relaxed); diff --git a/crates/pumpkin/src/entity/vehicle/boat.rs b/crates/pumpkin/src/entity/vehicle/boat.rs index 20d07bc4c..faae09c2d 100644 --- a/crates/pumpkin/src/entity/vehicle/boat.rs +++ b/crates/pumpkin/src/entity/vehicle/boat.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use crossbeam::atomic::AtomicCell; use crate::entity::player::Player; -use crate::entity::{Entity, EntityBase, EntityBaseFuture, living::LivingEntity}; +use crate::entity::{Entity, EntityBase, living::LivingEntity}; use crate::server::Server; use pumpkin_data::damage::DamageType; @@ -95,44 +95,45 @@ impl EntityBase for BoatEntity { self.vehicle.damage_with_context(amount, source) } - fn interact<'a>( - &'a self, - player: &'a Arc, - _item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - if player.get_entity().is_sneaking() { - return false; - } + fn interact(&self, player: &Arc, _item_stack: &mut ItemStack) -> bool { + if player.get_entity().is_sneaking() { + return false; + } - if self.ticks_underwater.load() >= 60.0 { - return false; - } + if self.ticks_underwater.load() >= 60.0 { + return false; + } - if self.vehicle.entity.passengers.lock().await.len() >= 2 { - return false; - } + if self + .vehicle + .entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .len() + >= 2 + { + return false; + } - if player.get_entity().has_vehicle() { - return false; - } + if player.get_entity().has_vehicle() { + return false; + } - let world = self.vehicle.entity.world.load(); - let Some(vehicle) = world.get_entity_by_id(self.vehicle.entity.entity_id) else { - return false; - }; + let world = self.vehicle.entity.world.load(); + let Some(vehicle) = world.get_entity_by_id(self.vehicle.entity.entity_id) else { + return false; + }; - let Some(passenger) = world.get_player_by_id(player.entity_id()) else { - return false; - }; + let Some(passenger) = world.get_player_by_id(player.entity_id()) else { + return false; + }; - self.vehicle - .entity - .add_passenger(vehicle, passenger as Arc) - .await; + self.vehicle + .entity + .add_passenger(vehicle, passenger as Arc); - true - }) + true } fn set_paddle_state(&self, left: bool, right: bool) { diff --git a/crates/pumpkin/src/entity/vehicle/minecart.rs b/crates/pumpkin/src/entity/vehicle/minecart.rs index 6ba70b426..24a996a46 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart.rs @@ -12,9 +12,7 @@ use pumpkin_protocol::java::server::play::SPlayerInput; use rand::RngExt; use crate::{ - entity::{ - Entity, EntityBase, EntityBaseFuture, NbtFuture, living::LivingEntity, player::Player, - }, + entity::{Entity, EntityBase, living::LivingEntity, player::Player}, server::Server, }; use pumpkin_data::Block; @@ -113,28 +111,24 @@ impl MinecartEntity { } impl EntityBase for MinecartEntity { - fn write_custom_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - match &self.kind { - MinecartKind::Chest(minecart) => minecart.write_nbt(nbt).await, - MinecartKind::Furnace(minecart) => minecart.write_nbt(nbt), - MinecartKind::Hopper(minecart) => minecart.write_nbt(nbt).await, - MinecartKind::Tnt(minecart) => minecart.write_nbt(nbt), - MinecartKind::Rideable(_) | MinecartKind::Other => {} - } - }) + fn write_custom_nbt(&self, nbt: &mut NbtCompound) { + match &self.kind { + MinecartKind::Chest(minecart) => minecart.write_nbt(nbt), + MinecartKind::Furnace(minecart) => minecart.write_nbt(nbt), + MinecartKind::Hopper(minecart) => minecart.write_nbt(nbt), + MinecartKind::Tnt(minecart) => minecart.write_nbt(nbt), + MinecartKind::Rideable(_) | MinecartKind::Other => {} + } } - fn read_custom_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { - Box::pin(async move { - match &self.kind { - MinecartKind::Chest(minecart) => minecart.read_nbt(nbt).await, - MinecartKind::Furnace(minecart) => minecart.read_nbt(nbt), - MinecartKind::Hopper(minecart) => minecart.read_nbt(nbt).await, - MinecartKind::Tnt(minecart) => minecart.read_nbt(nbt), - MinecartKind::Rideable(_) | MinecartKind::Other => {} - } - }) + fn read_custom_nbt(&self, nbt: &NbtCompound) { + match &self.kind { + MinecartKind::Chest(minecart) => minecart.read_nbt(nbt), + MinecartKind::Furnace(minecart) => minecart.read_nbt(nbt), + MinecartKind::Hopper(minecart) => minecart.read_nbt(nbt), + MinecartKind::Tnt(minecart) => minecart.read_nbt(nbt), + MinecartKind::Rideable(_) | MinecartKind::Other => {} + } } #[allow(clippy::too_many_lines)] @@ -509,144 +503,148 @@ impl EntityBase for MinecartEntity { } #[allow(clippy::too_many_lines)] - fn push<'a>(&'a self, entity: &'a Arc) -> EntityBaseFuture<'a, ()> { - Box::pin(async move { - let self_entity = self.get_entity(); - let other_entity = entity.get_entity(); + fn push(&self, entity: &Arc) { + let self_entity = self.get_entity(); + let other_entity = entity.get_entity(); - if self_entity.no_physics.load(Ordering::Relaxed) - || other_entity.no_physics.load(Ordering::Relaxed) + if self_entity.no_physics.load(Ordering::Relaxed) + || other_entity.no_physics.load(Ordering::Relaxed) + { + return; + } + + { + let passengers = self_entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if passengers + .iter() + .any(|p| p.get_entity().entity_id == other_entity.entity_id) { return; } - + } + { + let passengers = other_entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if passengers + .iter() + .any(|p| p.get_entity().entity_id == self_entity.entity_id) { - let passengers = self_entity.passengers.lock().await; - if passengers - .iter() - .any(|p| p.get_entity().entity_id == other_entity.entity_id) - { - return; - } + return; } - { - let passengers = other_entity.passengers.lock().await; - if passengers - .iter() - .any(|p| p.get_entity().entity_id == self_entity.entity_id) - { - return; - } + } + + let mut xa = other_entity.pos.load().x - self_entity.pos.load().x; + let mut za = other_entity.pos.load().z - self_entity.pos.load().z; + let mut dd = xa * xa + za * za; + if dd >= 1.0E-4 { + dd = dd.sqrt(); + xa /= dd; + za /= dd; + let mut pow = 1.0 / dd; + if pow > 1.0 { + pow = 1.0; } + xa *= pow; + za *= pow; + xa *= 0.1; + za *= 0.1; + xa *= 0.5; + za *= 0.5; - let mut xa = other_entity.pos.load().x - self_entity.pos.load().x; - let mut za = other_entity.pos.load().z - self_entity.pos.load().z; - let mut dd = xa * xa + za * za; - if dd >= 1.0E-4 { - dd = dd.sqrt(); - xa /= dd; - za /= dd; - let mut pow = 1.0 / dd; - if pow > 1.0 { - pow = 1.0; - } - xa *= pow; - za *= pow; - xa *= 0.1; - za *= 0.1; - xa *= 0.5; - za *= 0.5; + let is_other_minecart = other_entity.entity_type.id == EntityType::MINECART.id + || other_entity.entity_type.id == EntityType::CHEST_MINECART.id + || other_entity.entity_type.id == EntityType::COMMAND_BLOCK_MINECART.id + || other_entity.entity_type.id == EntityType::FURNACE_MINECART.id + || other_entity.entity_type.id == EntityType::HOPPER_MINECART.id + || other_entity.entity_type.id == EntityType::SPAWNER_MINECART.id + || other_entity.entity_type.id == EntityType::TNT_MINECART.id; - let is_other_minecart = other_entity.entity_type.id == EntityType::MINECART.id - || other_entity.entity_type.id == EntityType::CHEST_MINECART.id - || other_entity.entity_type.id == EntityType::COMMAND_BLOCK_MINECART.id - || other_entity.entity_type.id == EntityType::FURNACE_MINECART.id - || other_entity.entity_type.id == EntityType::HOPPER_MINECART.id - || other_entity.entity_type.id == EntityType::SPAWNER_MINECART.id - || other_entity.entity_type.id == EntityType::TNT_MINECART.id; + if is_other_minecart { + let xo = self_entity.velocity.load().x; + let zo = self_entity.velocity.load().z; - if is_other_minecart { - let xo = self_entity.velocity.load().x; - let zo = self_entity.velocity.load().z; + let dir = Vector3::new(xo, 0.0, zo).normalize(); + let facing = Vector3::new( + f64::from(self_entity.yaw.load().to_radians().cos()), + 0.0, + f64::from(self_entity.yaw.load().to_radians().sin()), + ) + .normalize(); - let dir = Vector3::new(xo, 0.0, zo).normalize(); - let facing = Vector3::new( - f64::from(self_entity.yaw.load().to_radians().cos()), - 0.0, - f64::from(self_entity.yaw.load().to_radians().sin()), - ) - .normalize(); + let dot = dir.dot(&facing).abs(); + if dot >= 0.8 { + let vel = self_entity.velocity.load(); + let ovel = other_entity.velocity.load(); - let dot = dir.dot(&facing).abs(); - if dot >= 0.8 { - let vel = self_entity.velocity.load(); - let ovel = other_entity.velocity.load(); + let is_self_furnace = + self_entity.entity_type.id == EntityType::FURNACE_MINECART.id; + let is_other_furnace = + other_entity.entity_type.id == EntityType::FURNACE_MINECART.id; - let is_self_furnace = - self_entity.entity_type.id == EntityType::FURNACE_MINECART.id; - let is_other_furnace = - other_entity.entity_type.id == EntityType::FURNACE_MINECART.id; - - if is_other_furnace && !is_self_furnace { - self_entity.velocity.store(vel.multiply(0.2, 1.0, 0.2)); - let mut new_self_vel = self_entity.velocity.load(); - new_self_vel.x += ovel.x - xa; - new_self_vel.z += ovel.z - za; - self_entity.velocity.store(new_self_vel); - self_entity.send_velocity(); - - other_entity.velocity.store(ovel.multiply(0.95, 1.0, 0.95)); - other_entity.send_velocity(); - } else if !is_other_furnace && is_self_furnace { - other_entity.velocity.store(ovel.multiply(0.2, 1.0, 0.2)); - let mut new_other_vel = other_entity.velocity.load(); - new_other_vel.x += vel.x + xa; - new_other_vel.z += vel.z + za; - other_entity.velocity.store(new_other_vel); - other_entity.send_velocity(); - - self_entity.velocity.store(vel.multiply(0.95, 1.0, 0.95)); - self_entity.send_velocity(); - } else { - #[allow(clippy::manual_midpoint)] - let xdd = (ovel.x + vel.x) / 2.0; - #[allow(clippy::manual_midpoint)] - let zdd = (ovel.z + vel.z) / 2.0; - - self_entity.velocity.store(vel.multiply(0.2, 1.0, 0.2)); - let mut new_self_vel = self_entity.velocity.load(); - new_self_vel.x += xdd - xa; - new_self_vel.z += zdd - za; - self_entity.velocity.store(new_self_vel); - self_entity.send_velocity(); - - other_entity.velocity.store(ovel.multiply(0.2, 1.0, 0.2)); - let mut new_other_vel = other_entity.velocity.load(); - new_other_vel.x += xdd + xa; - new_other_vel.z += zdd + za; - other_entity.velocity.store(new_other_vel); - other_entity.send_velocity(); - } - } - } else { - if !self_entity.has_passengers() && self.is_pushable() { - let mut vel = self_entity.velocity.load(); - vel.x -= xa; - vel.z -= za; - self_entity.velocity.store(vel); + if is_other_furnace && !is_self_furnace { + self_entity.velocity.store(vel.multiply(0.2, 1.0, 0.2)); + let mut new_self_vel = self_entity.velocity.load(); + new_self_vel.x += ovel.x - xa; + new_self_vel.z += ovel.z - za; + self_entity.velocity.store(new_self_vel); self_entity.send_velocity(); - } - if !other_entity.has_passengers() && entity.is_pushable() { - let mut vel = other_entity.velocity.load(); - vel.x += xa / 4.0; - vel.z += za / 4.0; - other_entity.velocity.store(vel); + other_entity.velocity.store(ovel.multiply(0.95, 1.0, 0.95)); + other_entity.send_velocity(); + } else if !is_other_furnace && is_self_furnace { + other_entity.velocity.store(ovel.multiply(0.2, 1.0, 0.2)); + let mut new_other_vel = other_entity.velocity.load(); + new_other_vel.x += vel.x + xa; + new_other_vel.z += vel.z + za; + other_entity.velocity.store(new_other_vel); + other_entity.send_velocity(); + + self_entity.velocity.store(vel.multiply(0.95, 1.0, 0.95)); + self_entity.send_velocity(); + } else { + #[allow(clippy::manual_midpoint)] + let xdd = (ovel.x + vel.x) / 2.0; + #[allow(clippy::manual_midpoint)] + let zdd = (ovel.z + vel.z) / 2.0; + + self_entity.velocity.store(vel.multiply(0.2, 1.0, 0.2)); + let mut new_self_vel = self_entity.velocity.load(); + new_self_vel.x += xdd - xa; + new_self_vel.z += zdd - za; + self_entity.velocity.store(new_self_vel); + self_entity.send_velocity(); + + other_entity.velocity.store(ovel.multiply(0.2, 1.0, 0.2)); + let mut new_other_vel = other_entity.velocity.load(); + new_other_vel.x += xdd + xa; + new_other_vel.z += zdd + za; + other_entity.velocity.store(new_other_vel); other_entity.send_velocity(); } } + } else { + if !self_entity.has_passengers() && self.is_pushable() { + let mut vel = self_entity.velocity.load(); + vel.x -= xa; + vel.z -= za; + self_entity.velocity.store(vel); + self_entity.send_velocity(); + } + + if !other_entity.has_passengers() && entity.is_pushable() { + let mut vel = other_entity.velocity.load(); + vel.x += xa / 4.0; + vel.z += za / 4.0; + other_entity.velocity.store(vel); + other_entity.send_velocity(); + } } - }) + } } fn is_collidable(&self, _entity: Option>) -> bool { @@ -749,28 +747,32 @@ impl EntityBase for MinecartEntity { damaged } - fn interact<'a>( - &'a self, - player: &'a Arc, - item_stack: &'a mut ItemStack, - ) -> EntityBaseFuture<'a, bool> { - Box::pin(async move { - match &self.kind { - MinecartKind::Chest(minecart) => { - minecart.interact(&self.vehicle.entity, player).await - } - MinecartKind::Furnace(minecart) => { - minecart.interact(&self.vehicle.entity, player, item_stack) - } - MinecartKind::Hopper(minecart) => { - minecart.interact(&self.vehicle.entity, player).await - } - MinecartKind::Rideable(minecart) => { - minecart.interact(&self.vehicle.entity, player).await - } - MinecartKind::Tnt(_) | MinecartKind::Other => false, + fn interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { + match &self.kind { + MinecartKind::Chest(minecart) => { + let minecart = minecart.clone(); + let custom_name = self.vehicle.entity.custom_name.load().as_ref().clone(); + let player = player.clone(); + tokio::spawn(async move { + minecart.interact(custom_name, &player).await; + }); + true } - }) + MinecartKind::Furnace(minecart) => { + minecart.interact(&self.vehicle.entity, player, item_stack) + } + MinecartKind::Hopper(minecart) => { + let minecart = minecart.clone(); + let custom_name = self.vehicle.entity.custom_name.load().as_ref().clone(); + let player = player.clone(); + tokio::spawn(async move { + minecart.interact(custom_name, &player).await; + }); + true + } + MinecartKind::Rideable(minecart) => minecart.interact(&self.vehicle.entity, player), + MinecartKind::Tnt(_) | MinecartKind::Other => false, + } } fn on_player_collision(&self, player: &Arc) { @@ -813,21 +815,16 @@ impl EntityBase for MinecartEntity { fn move_entity(&self, caller: &Arc, motion: Vector3) { let to_position = self.vehicle.entity.pos.load().add(&motion); self.vehicle.entity.move_entity(caller, motion); - let caller_clone = caller.clone(); let entity_id = self.vehicle.entity.entity_id; let world = self.vehicle.entity.world.load().clone(); - tokio::spawn(async move { - if let Some(dyn_self) = world.get_entity_by_id(entity_id) { - let should_continue = dyn_self.push_entities(&caller_clone).await; - if should_continue { - let current_pos = dyn_self.get_entity().pos.load(); - let back_motion = to_position.sub(¤t_pos); - dyn_self - .get_entity() - .move_entity(&caller_clone, back_motion); - } + if let Some(dyn_self) = world.get_entity_by_id(entity_id) { + let should_continue = dyn_self.push_entities(caller); + if should_continue { + let current_pos = dyn_self.get_entity().pos.load(); + let back_motion = to_position.sub(¤t_pos); + dyn_self.get_entity().move_entity(caller, back_motion); } - }); + } } fn cast_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/entity/vehicle/minecart/chest.rs b/crates/pumpkin/src/entity/vehicle/minecart/chest.rs index e7cacf9ab..25a7e041c 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/chest.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/chest.rs @@ -4,10 +4,11 @@ use pumpkin_data::translation; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::text::TextComponent; -use crate::entity::{Entity, player::Player}; +use crate::entity::player::Player; use super::container::{self, MinecartInventory}; +#[derive(Clone)] pub(super) struct ChestMinecart { inventory: Arc, } @@ -23,9 +24,13 @@ impl ChestMinecart { &self.inventory } - pub(super) async fn interact(&self, entity: &Entity, player: &Arc) -> bool { + pub(super) async fn interact( + &self, + custom_name: Option, + player: &Arc, + ) -> bool { container::open( - entity, + custom_name, player, &self.inventory, TextComponent::translate_cross( @@ -38,11 +43,11 @@ impl ChestMinecart { .await } - pub(super) async fn write_nbt(&self, nbt: &mut NbtCompound) { - self.inventory.write_nbt(nbt).await; + pub(super) fn write_nbt(&self, nbt: &mut NbtCompound) { + self.inventory.write_nbt(nbt); } - pub(super) async fn read_nbt(&self, nbt: &NbtCompound) { - self.inventory.read_nbt(nbt).await; + pub(super) fn read_nbt(&self, nbt: &NbtCompound) { + self.inventory.read_nbt(nbt); } } diff --git a/crates/pumpkin/src/entity/vehicle/minecart/container.rs b/crates/pumpkin/src/entity/vehicle/minecart/container.rs index 4662fc9c9..282546ac7 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/container.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/container.rs @@ -40,7 +40,7 @@ impl MinecartInventory { !self.drops_claimed.swap(true, Ordering::AcqRel) } - pub(super) async fn read_nbt(&self, nbt: &NbtCompound) { + pub(super) fn read_nbt(&self, nbt: &NbtCompound) { let loot_table = nbt.get_string("LootTable").map(|loot_table| { ( loot_table.to_owned(), @@ -48,24 +48,40 @@ impl MinecartInventory { ) }); let has_loot_table = loot_table.is_some(); - *self.loot_table.lock().await = loot_table; + if let Ok(mut guard) = self.loot_table.try_lock() { + *guard = loot_table; + } if !has_loot_table { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.read_data(nbt, &mut items); + if let Ok(mut items) = self.items.try_write() { + items.fill_with(|| ItemStack::EMPTY.clone()); + self.read_data(nbt, &mut items); + } } } - pub(super) async fn write_nbt(&self, nbt: &mut NbtCompound) { - let loot_table = self.loot_table.lock().await.clone(); + pub(super) fn write_nbt(&self, nbt: &mut NbtCompound) { + let loot_table = self + .loot_table + .try_lock() + .ok() + .and_then(|guard| guard.clone()); if let Some((loot_table, seed)) = loot_table { nbt.put_string("LootTable", loot_table); if seed != 0 { nbt.put_long("LootTableSeed", seed); } - } else { - self.write_inventory_nbt(nbt, true).await; + } else if let Ok(items) = self.items.try_read() { + let mut list: Vec = Vec::new(); + for (slot, stack) in items.iter().enumerate() { + if !stack.is_empty() { + let mut compound = NbtCompound::new(); + compound.put_byte("Slot", slot as i8); + stack.write_item_stack(&mut compound); + list.push(pumpkin_nbt::tag::NbtTag::Compound(compound)); + } + } + nbt.put("Items", pumpkin_nbt::tag::NbtTag::List(list)); } } @@ -174,7 +190,7 @@ impl ScreenHandlerFactory for MinecartScreenFactory { } pub(super) async fn open( - entity: &Entity, + custom_name: Option, player: &Arc, inventory: &Arc, title: TextComponent, @@ -191,7 +207,7 @@ pub(super) async fn open( .open_handled_screen( &MinecartScreenFactory { inventory: inventory.clone(), - title: entity.custom_name.load().as_ref().clone().unwrap_or(title), + title: custom_name.unwrap_or(title), hopper, }, None, @@ -261,10 +277,10 @@ mod tests { "minecraft:chests/abandoned_mineshaft".to_string(), ); source.put_long("LootTableSeed", 1234); - inventory.read_nbt(&source).await; + inventory.read_nbt(&source); let mut deferred = NbtCompound::new(); - inventory.write_nbt(&mut deferred).await; + inventory.write_nbt(&mut deferred); assert_eq!( deferred.get_string("LootTable"), Some("minecraft:chests/abandoned_mineshaft") @@ -276,7 +292,7 @@ mod tests { assert!(!inventory.is_empty().await); let mut unpacked = NbtCompound::new(); - inventory.write_nbt(&mut unpacked).await; + inventory.write_nbt(&mut unpacked); assert!(unpacked.get_string("LootTable").is_none()); assert!(unpacked.get_list("Items").is_some()); } @@ -289,10 +305,10 @@ mod tests { .await; let mut nbt = NbtCompound::new(); - inventory.write_nbt(&mut nbt).await; + inventory.write_nbt(&mut nbt); let restored = MinecartInventory::new(27); - restored.read_nbt(&nbt).await; + restored.read_nbt(&nbt); let stack = restored.get_stack(8).await; assert_eq!(stack.get_item().id, Item::POWERED_RAIL.id); assert_eq!(stack.item_count, 3); diff --git a/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs b/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs index ac8901068..0eb5af2cf 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs @@ -12,15 +12,16 @@ use pumpkin_util::text::TextComponent; use super::container::{self, MinecartInventory}; +#[derive(Clone)] pub(super) struct HopperMinecart { - enabled: AtomicBool, + enabled: Arc, inventory: Arc, } impl HopperMinecart { pub(super) fn new() -> Self { Self { - enabled: AtomicBool::new(true), + enabled: Arc::new(AtomicBool::new(true)), inventory: Arc::new(MinecartInventory::new(5)), } } @@ -119,9 +120,13 @@ impl HopperMinecart { false } - pub(super) async fn interact(&self, entity: &Entity, player: &Arc) -> bool { + pub(super) async fn interact( + &self, + custom_name: Option, + player: &Arc, + ) -> bool { container::open( - entity, + custom_name, player, &self.inventory, TextComponent::translate_cross( @@ -134,13 +139,13 @@ impl HopperMinecart { .await } - pub(super) async fn write_nbt(&self, nbt: &mut NbtCompound) { - self.inventory.write_nbt(nbt).await; + pub(super) fn write_nbt(&self, nbt: &mut NbtCompound) { + self.inventory.write_nbt(nbt); nbt.put_bool("Enabled", self.enabled.load(Ordering::Relaxed)); } - pub(super) async fn read_nbt(&self, nbt: &NbtCompound) { - self.inventory.read_nbt(nbt).await; + pub(super) fn read_nbt(&self, nbt: &NbtCompound) { + self.inventory.read_nbt(nbt); self.enabled .store(nbt.get_bool("Enabled").unwrap_or(true), Ordering::Relaxed); } diff --git a/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs b/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs index 222b0530e..7875f5c73 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/rideable.rs @@ -5,9 +5,13 @@ use crate::entity::{Entity, EntityBase, player::Player}; pub(super) struct RideableMinecart; impl RideableMinecart { - pub(super) async fn interact(&self, entity: &Entity, player: &Arc) -> bool { + pub(super) fn interact(&self, entity: &Entity, player: &Arc) -> bool { if player.get_entity().is_sneaking() - || !entity.passengers.lock().await.is_empty() + || !entity + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() || player.get_entity().has_vehicle() { return false; @@ -21,9 +25,7 @@ impl RideableMinecart { return false; }; - entity - .add_passenger(vehicle, passenger as Arc) - .await; + entity.add_passenger(vehicle, passenger as Arc); true } } diff --git a/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs b/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs index 014539522..7298ee803 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/tnt.rs @@ -95,16 +95,11 @@ impl TntMinecart { let pos = entity.pos.load(); let primed = self.fuse.load(Ordering::Relaxed) > -1; entity.remove(); - let world_clone = world.clone(); - tokio::spawn(async move { - if primed { - world_clone.explode_tnt_minecart(pos, power).await; - } else { - world_clone - .explode(pos, power, crate::world::ExplosionInteraction::Tnt) - .await; - } - }); + if primed { + world.explode_tnt_minecart(pos, power); + } else { + world.explode(pos, power, crate::world::ExplosionInteraction::Tnt); + } } pub(super) fn set_fuse(&self, fuse: i32) { diff --git a/crates/pumpkin/src/item/items/armor_stand.rs b/crates/pumpkin/src/item/items/armor_stand.rs index 54b8046a3..03b8bfac7 100644 --- a/crates/pumpkin/src/item/items/armor_stand.rs +++ b/crates/pumpkin/src/item/items/armor_stand.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -38,61 +37,58 @@ impl ItemMetadata for ArmorStandItem { } impl ItemBehaviour for ArmorStandItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - _block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let position = Self::calculate_placement_position(&location, face).to_f64(); + _block: &Block, + _server: &Server, + ) { + let world = player.world(); + let position = Self::calculate_placement_position(&location, face).to_f64(); - let bottom_center = Vector3::new(position.x, position.y, position.z); + let bottom_center = Vector3::new(position.x, position.y, position.z); - let armor_stand_dimensions = EntityType::ARMOR_STAND.dimension; - let width = f64::from(armor_stand_dimensions[0]); - let height = f64::from(armor_stand_dimensions[1]); + let armor_stand_dimensions = EntityType::ARMOR_STAND.dimension; + let width = f64::from(armor_stand_dimensions[0]); + let height = f64::from(armor_stand_dimensions[1]); - let bounding_box = BoundingBox::new( - Vector3::new( - bottom_center.x - width / 2.0, - bottom_center.y, - bottom_center.z - width / 2.0, - ), - Vector3::new( - bottom_center.x + width / 2.0, - bottom_center.y + height, - bottom_center.z + width / 2.0, - ), + let bounding_box = BoundingBox::new( + Vector3::new( + bottom_center.x - width / 2.0, + bottom_center.y, + bottom_center.z - width / 2.0, + ), + Vector3::new( + bottom_center.x + width / 2.0, + bottom_center.y + height, + bottom_center.z + width / 2.0, + ), + ); + + if world.is_space_empty(bounding_box) && world.get_entities_at_box(&bounding_box).is_empty() + { + let (player_yaw, _) = player.rotation(); + let rotation = ((wrap_degrees(player_yaw - 180.0) + 22.5) / 45.0).floor() * 45.0; + + let entity = Entity::new(world.clone(), position, &EntityType::ARMOR_STAND); + + entity.set_rotation(rotation, 0.0); + + world.play_sound( + Sound::EntityArmorStandPlace, + SoundCategory::Blocks, + &entity.pos.load(), ); - if world.is_space_empty(bounding_box) - && world.get_entities_at_box(&bounding_box).is_empty() - { - let (player_yaw, _) = player.rotation(); - let rotation = ((wrap_degrees(player_yaw - 180.0) + 22.5) / 45.0).floor() * 45.0; + let armor_stand = ArmorStandEntity::new(entity); - let entity = Entity::new(world.clone(), position, &EntityType::ARMOR_STAND); - - entity.set_rotation(rotation, 0.0); - - world.play_sound( - Sound::EntityArmorStandPlace, - SoundCategory::Blocks, - &entity.pos.load(), - ); - - let armor_stand = ArmorStandEntity::new(entity); - - world.spawn_entity(Arc::new(armor_stand)); - item.decrement_unless_creative(player.gamemode.load(), 1); - } - }) + world.spawn_entity(Arc::new(armor_stand)); + item.decrement_unless_creative(player.gamemode.load(), 1); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/axe.rs b/crates/pumpkin/src/item/items/axe.rs index eb3a45677..27086147b 100644 --- a/crates/pumpkin/src/item/items/axe.rs +++ b/crates/pumpkin/src/item/items/axe.rs @@ -1,5 +1,3 @@ -use std::pin::Pin; - use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; @@ -25,89 +23,85 @@ impl ItemMetadata for AxeItem { } impl ItemBehaviour for AxeItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - // I tried to follow mojang order of doing things. - let world = player.world(); - let replacement_block = try_use_axe(block.id); - // First we try to strip the block. by getting his equivalent and applying it the axis. + block: &Block, + _server: &Server, + ) { + // I tried to follow mojang order of doing things. + let world = player.world(); + let replacement_block = try_use_axe(block.id); + // First we try to strip the block. by getting his equivalent and applying it the axis. - // If there is a strip equivalent. - let changed = replacement_block.is_some_and(|replacement| { - let new_block = replacement.to_block(); - // Bamboo blocks are pillars too, but they are not part of the logs tag. - let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_LOGS) - || block == &Block::BAMBOO_BLOCK - { - let log_information = world.get_block_state_id(&location); - let log_props = - PaleOakWoodLikeProperties::from_state_id(log_information, block); - // create new properties for the new log. - let mut new_log_properties = PaleOakWoodLikeProperties::default(new_block); - new_log_properties.axis = log_props.axis; + // If there is a strip equivalent. + let changed = replacement_block.is_some_and(|replacement| { + let new_block = replacement.to_block(); + // Bamboo blocks are pillars too, but they are not part of the logs tag. + let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_LOGS) + || block == &Block::BAMBOO_BLOCK + { + let log_information = world.get_block_state_id(&location); + let log_props = PaleOakWoodLikeProperties::from_state_id(log_information, block); + // create new properties for the new log. + let mut new_log_properties = PaleOakWoodLikeProperties::default(new_block); + new_log_properties.axis = log_props.axis; - // create new properties for the new log. + // create new properties for the new log. - // Set old axis to the new log. - new_log_properties.axis = log_props.axis; - new_log_properties.to_state_id(new_block) - } - // Let's check if It's a door - else if block.has_tag(&tag::Block::MINECRAFT_DOORS) { - // get block state of the old log. - // get block state of the old log. - let door_information = world.get_block_state_id(&location); - // get the door properties - let door_props = OakDoorLikeProperties::from_state_id(door_information, block); - let other_half_pos = match door_props.half { - DoubleBlockHalf::Lower => location.up(), - DoubleBlockHalf::Upper => location.down(), - }; - let (other_block, other_state_id) = - world.get_block_and_state_id(&other_half_pos); - if other_block == block { - let other_new_state_id = - crate::block::blocks::weathering_copper::with_properties_of( - other_block, - other_state_id, - new_block, - ); - world.set_block_state( - &other_half_pos, - other_new_state_id, - BlockFlags::NOTIFY_ALL, - ); - } - crate::block::blocks::weathering_copper::with_properties_of( - block, - door_information, - new_block, - ) - } else { - crate::block::blocks::weathering_copper::with_properties_of( - block, - world.get_block_state_id(&location), - new_block, - ) - }; - world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL); - true - }); - - if changed && player.gamemode.load() != GameMode::Creative { - // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. - let _ = item.damage_item(1); + // Set old axis to the new log. + new_log_properties.axis = log_props.axis; + new_log_properties.to_state_id(new_block) } - }) + // Let's check if It's a door + else if block.has_tag(&tag::Block::MINECRAFT_DOORS) { + // get block state of the old log. + // get block state of the old log. + let door_information = world.get_block_state_id(&location); + // get the door properties + let door_props = OakDoorLikeProperties::from_state_id(door_information, block); + let other_half_pos = match door_props.half { + DoubleBlockHalf::Lower => location.up(), + DoubleBlockHalf::Upper => location.down(), + }; + let (other_block, other_state_id) = world.get_block_and_state_id(&other_half_pos); + if other_block == block { + let other_new_state_id = + crate::block::blocks::weathering_copper::with_properties_of( + other_block, + other_state_id, + new_block, + ); + world.set_block_state( + &other_half_pos, + other_new_state_id, + BlockFlags::NOTIFY_ALL, + ); + } + crate::block::blocks::weathering_copper::with_properties_of( + block, + door_information, + new_block, + ) + } else { + crate::block::blocks::weathering_copper::with_properties_of( + block, + world.get_block_state_id(&location), + new_block, + ) + }; + world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL); + true + }); + + if changed && player.gamemode.load() != GameMode::Creative { + // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. + let _ = item.damage_item(1); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/boat.rs b/crates/pumpkin/src/item/items/boat.rs index 19e0437d8..a5446a252 100644 --- a/crates/pumpkin/src/item/items/boat.rs +++ b/crates/pumpkin/src/item/items/boat.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -89,105 +88,99 @@ impl ItemMetadata for BoatItem { impl ItemBehaviour for BoatItem { /// Vanilla: `BoatItem.use()` - raycasts to find placement position - fn normal_use<'a>( - &'a self, - item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let (start_pos, end_pos) = self.get_start_and_end_pos(player); + fn normal_use(&self, item: &Item, player: &Player) { + let world = player.world(); + let (start_pos, end_pos) = self.get_start_and_end_pos(player); - // Vanilla: raycast with FluidHandling.ANY - stops on water/lava surface or solid blocks - let checker = |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos); + // Vanilla: raycast with FluidHandling.ANY - stops on water/lava surface or solid blocks + let checker = |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos); - // Air doesn't stop the raycast - if state_id == Block::AIR.default_state.id { - return false; - } - - // Check if it's a fluid - stop on any fluid (FluidHandling.ANY in vanilla) - if Fluid::from_state_id(state_id).is_some() { - return true; - } - - // Stop on solid blocks - true - }; - - let Some((hit_pos, _direction)) = world.raycast(start_pos, end_pos, checker) else { - return; - }; - - // Calculate hit position - center of the block top or water surface - // TODO: Vanilla uses exact raycast intersection point (hitResult.getPos()), - // Pumpkin's raycast only returns block positions. - let hit_vec = Vector3::new( - f64::from(hit_pos.0.x) + 0.5, - f64::from(hit_pos.0.y) + 1.0, - f64::from(hit_pos.0.z) + 0.5, - ); - - // Vanilla: Check for entities in the path that would block placement - // Get player's rotation vector stretched by 5.0 and expanded by 1.0 - let (yaw, pitch) = player.rotation(); - let rotation_vec = - Vector3::rotation_vector(f64::from(pitch), f64::from(yaw)).multiply(5.0, 5.0, 5.0); - let player_entity = player.get_entity().bounding_box.load(); - let search_box = player_entity.stretch(rotation_vec).expand_all(1.0); - - let entities = world.get_entities_at_box(&search_box); - let player_eye_pos = player.eye_position(); - - for entity in &entities { - // Vanilla: EntityPredicates.CAN_HIT = !isSpectator() && canHit() - if entity.is_spectator() || !entity.can_hit() { - continue; - } - - // Vanilla: expand by getTargetingMargin() (0.0 for most entities) - let entity_box = entity.get_entity().bounding_box.load(); - // Check if entity's bounding box contains the player's eye position - if entity_box.intersects(&BoundingBox::new(player_eye_pos, player_eye_pos)) { - // Entity is blocking the line of sight at eye position - return; - } + // Air doesn't stop the raycast + if state_id == Block::AIR.default_state.id { + return false; } - // Create the boat entity - let entity_type = Self::item_to_entity(item); - let dimensions = Self::get_entity_dimensions(entity_type); - let boat_box = BoundingBox::new_from_pos(hit_vec.x, hit_vec.y, hit_vec.z, &dimensions); - - // Vanilla: if (!world.isSpaceEmpty(lv7, lv7.getBoundingBox())) return FAIL - if !world.is_space_empty(boat_box) { - return; + // Check if it's a fluid - stop on any fluid (FluidHandling.ANY in vanilla) + if Fluid::from_state_id(state_id).is_some() { + return true; } - // Check no entities occupy the space - if !world.get_entities_at_box(&boat_box).is_empty() { - return; + // Stop on solid blocks + true + }; + + let Some((hit_pos, _direction)) = world.raycast(start_pos, end_pos, checker) else { + return; + }; + + // Calculate hit position - center of the block top or water surface + // TODO: Vanilla uses exact raycast intersection point (hitResult.getPos()), + // Pumpkin's raycast only returns block positions. + let hit_vec = Vector3::new( + f64::from(hit_pos.0.x) + 0.5, + f64::from(hit_pos.0.y) + 1.0, + f64::from(hit_pos.0.z) + 0.5, + ); + + // Vanilla: Check for entities in the path that would block placement + // Get player's rotation vector stretched by 5.0 and expanded by 1.0 + let (yaw, pitch) = player.rotation(); + let rotation_vec = + Vector3::rotation_vector(f64::from(pitch), f64::from(yaw)).multiply(5.0, 5.0, 5.0); + let player_entity = player.get_entity().bounding_box.load(); + let search_box = player_entity.stretch(rotation_vec).expand_all(1.0); + + let entities = world.get_entities_at_box(&search_box); + let player_eye_pos = player.eye_position(); + + for entity in &entities { + // Vanilla: EntityPredicates.CAN_HIT = !isSpectator() && canHit() + if entity.is_spectator() || !entity.can_hit() { + continue; } - // Create and spawn the boat - let entity = Entity::new(world.clone(), hit_vec, entity_type); + // Vanilla: expand by getTargetingMargin() (0.0 for most entities) + let entity_box = entity.get_entity().bounding_box.load(); + // Check if entity's bounding box contains the player's eye position + if entity_box.intersects(&BoundingBox::new(player_eye_pos, player_eye_pos)) { + // Entity is blocking the line of sight at eye position + return; + } + } - // Set yaw to player's yaw - let (player_yaw, _) = player.rotation(); - entity.set_rotation(player_yaw, 0.0); + // Create the boat entity + let entity_type = Self::item_to_entity(item); + let dimensions = Self::get_entity_dimensions(entity_type); + let boat_box = BoundingBox::new_from_pos(hit_vec.x, hit_vec.y, hit_vec.z, &dimensions); - let boat_entity = Arc::new(BoatEntity::new(entity)); - world.spawn_entity(boat_entity); + // Vanilla: if (!world.isSpaceEmpty(lv7, lv7.getBoundingBox())) return FAIL + if !world.is_space_empty(boat_box) { + return; + } - // Decrement item unless in creative mode - let mut stack = player.inventory.held_item(); - stack.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(stack); + // Check no entities occupy the space + if !world.get_entities_at_box(&boat_box).is_empty() { + return; + } - // TODO: world.emitGameEvent(user, GameEvent.ENTITY_PLACE, hitResult.getPos()) - // TODO: user.incrementStat(Stats.USED.getOrCreateStat(this)) - }) + // Create and spawn the boat + let entity = Entity::new(world.clone(), hit_vec, entity_type); + + // Set yaw to player's yaw + let (player_yaw, _) = player.rotation(); + entity.set_rotation(player_yaw, 0.0); + + let boat_entity = Arc::new(BoatEntity::new(entity)); + world.spawn_entity(boat_entity); + + // Decrement item unless in creative mode + let mut stack = player.inventory.held_item(); + stack.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(stack); + + // TODO: world.emitGameEvent(user, GameEvent.ENTITY_PLACE, hitResult.getPos()) + // TODO: user.incrementStat(Stats.USED.getOrCreateStat(this)) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/bone_meal.rs b/crates/pumpkin/src/item/items/bone_meal.rs index 83acafb36..420a2bc5a 100644 --- a/crates/pumpkin/src/item/items/bone_meal.rs +++ b/crates/pumpkin/src/item/items/bone_meal.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -22,27 +20,25 @@ impl ItemMetadata for BoneMealItem { impl ItemBehaviour for BoneMealItem { #[allow(clippy::too_many_lines)] - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let state_id = world.get_block_state_id(&location); - if server - .block_registry - .bone_meal(block, &world, &location, state_id) - { - world.sync_world_event(WorldEvent::ParticlesAndSoundPlantGrowth, location, 15); - item.decrement_unless_creative(player.gamemode.load(), 1); - } - }) + block: &Block, + server: &Server, + ) { + let world = player.world(); + let state_id = world.get_block_state_id(&location); + if server + .block_registry + .bone_meal(block, &world, &location, state_id) + { + world.sync_world_event(WorldEvent::ParticlesAndSoundPlantGrowth, location, 15); + item.decrement_unless_creative(player.gamemode.load(), 1); + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/bow.rs b/crates/pumpkin/src/item/items/bow.rs index 784b7d1d3..0299b4074 100644 --- a/crates/pumpkin/src/item/items/bow.rs +++ b/crates/pumpkin/src/item/items/bow.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -14,7 +12,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_protocol::IdOr; use pumpkin_protocol::java::client::play::CSoundEffect; use pumpkin_util::GameMode; -use pumpkin_world::inventory::Inventory; pub struct BowItem; @@ -25,41 +22,27 @@ impl ItemMetadata for BowItem { } impl ItemBehaviour for BowItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - // Check if player has arrows (or is in creative mode) - let has_arrows = self.has_arrows(player).await; - let gamemode = player.gamemode.load(); + fn normal_use(&self, _item: &Item, player: &Player) { + // Check if player has arrows (or is in creative mode) + let has_arrows = self.has_arrows(player); + let gamemode = player.gamemode.load(); - if !has_arrows && gamemode != GameMode::Creative { - return; - } + if !has_arrows && gamemode != GameMode::Creative { + return; + } - // Get the held item stack - let inventory = player.inventory(); - let stack = inventory.held_item(); + // Get the held item stack + let inventory = player.inventory(); + let stack = inventory.held_item(); - // Start the bow drawing animation - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, - ); - }) + // Start the bow drawing animation + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a ItemStack, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - Self::release_bow(player).await; - }) + fn on_stopped_using(&self, _stack: &ItemStack, player: &Player) { + Self::release_bow(player); } fn get_use_duration(&self) -> i32 { @@ -78,7 +61,7 @@ impl BowItem { const ARROW_SPEED_MULTIPLIER: f32 = 3.0; /// Called when the player releases the bow - pub async fn release_bow(player: &Player) { + pub fn release_bow(player: &Player) { // Get the used ticks let use_ticks = player.living_entity.item_use_time.load(Ordering::Relaxed); let use_ticks = Self::USE_DURATION - use_ticks; @@ -89,7 +72,7 @@ impl BowItem { } // Check arrows again - let arrow_slot = player.find_arrow().await; + let arrow_slot = player.find_arrow(); let gamemode = player.gamemode.load(); if arrow_slot.is_none() && gamemode != GameMode::Creative { @@ -97,7 +80,7 @@ impl BowItem { } let projectile = if let Some(slot) = arrow_slot { - let stack = player.inventory().get_stack(slot).await; + let stack = player.inventory.get_slot(slot); stack.copy_with_count(1) } else { ItemStack::new(1, &Item::ARROW) @@ -126,16 +109,16 @@ impl BowItem { && gamemode != GameMode::Creative && !(has_infinity && infinite_projectile) { - player.consume_arrow(slot).await; + player.consume_arrow(slot); } // Damage bow - player.damage_held_item(1).await; + player.damage_held_item(1); } /// Check if player has arrows in their inventory - async fn has_arrows(&self, player: &Player) -> bool { - player.find_arrow().await.is_some() + fn has_arrows(&self, player: &Player) -> bool { + player.find_arrow().is_some() } /// Calculate the power/charge of the bow based on time held diff --git a/crates/pumpkin/src/item/items/brush.rs b/crates/pumpkin/src/item/items/brush.rs index e8d6d913d..d387314aa 100644 --- a/crates/pumpkin/src/item/items/brush.rs +++ b/crates/pumpkin/src/item/items/brush.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use crate::entity::item::ItemEntity; @@ -26,15 +24,14 @@ impl ItemMetadata for BrushItem { } fn get_dusted_stage(block: &Block, state_id: BlockStateId) -> u8 { - block - .properties(state_id) - .and_then(|props| { - props - .to_props() - .into_iter() - .find(|(key, _)| *key == "dusted") - .and_then(|(_, val)| val.parse::().ok()) - }) + let Some(props) = block.properties(state_id) else { + return 0; + }; + props + .to_props() + .iter() + .find(|(k, _)| *k == "dusted") + .and_then(|(_, v)| v.parse::().ok()) .unwrap_or(0) } @@ -59,170 +56,161 @@ fn set_dusted_stage(block: &Block, state_id: BlockStateId, stage: u8) -> BlockSt fn get_random_archaeology_loot() -> &'static Item { let loot_table = [ - &Item::ARCHER_POTTERY_SHERD, - &Item::PRIZE_POTTERY_SHERD, - &Item::SKULL_POTTERY_SHERD, - &Item::ARMS_UP_POTTERY_SHERD, + &Item::SNORT_POTTERY_SHERD, + &Item::FLOW_POTTERY_SHERD, + &Item::GUSTER_POTTERY_SHERD, + &Item::SCRAPE_POTTERY_SHERD, &Item::ANGLER_POTTERY_SHERD, - &Item::EMERALD, - &Item::DIAMOND, - &Item::SNIFFER_EGG, - &Item::BONE, - &Item::GOLD_NUGGET, - &Item::COAL, + &Item::ARCHER_POTTERY_SHERD, + &Item::ARMS_UP_POTTERY_SHERD, + &Item::BLADE_POTTERY_SHERD, + &Item::BREWER_POTTERY_SHERD, + &Item::BURN_POTTERY_SHERD, + &Item::DANGER_POTTERY_SHERD, + &Item::EXPLORER_POTTERY_SHERD, + &Item::FRIEND_POTTERY_SHERD, + &Item::HEART_POTTERY_SHERD, + &Item::HEARTBREAK_POTTERY_SHERD, + &Item::HOWL_POTTERY_SHERD, + &Item::MINER_POTTERY_SHERD, + &Item::MOURNER_POTTERY_SHERD, + &Item::PLENTY_POTTERY_SHERD, + &Item::PRIZE_POTTERY_SHERD, + &Item::SHEAF_POTTERY_SHERD, + &Item::SHELTER_POTTERY_SHERD, + &Item::SKULL_POTTERY_SHERD, ]; + let idx = (rand::random::() as usize) % loot_table.len(); loot_table[idx] } impl ItemBehaviour for BrushItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - player.world().play_sound( - Sound::ItemBrushBrushingGeneric, - SoundCategory::Players, - &player.position(), - ); - let stack = player.inventory().held_item(); - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, - ); - }) + fn normal_use(&self, _item: &Item, player: &Player) { + player.world().play_sound( + Sound::ItemBrushBrushingGeneric, + SoundCategory::Players, + &player.position(), + ); + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); } - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let is_sand = block == &Block::SUSPICIOUS_SAND; - let is_gravel = block == &Block::SUSPICIOUS_GRAVEL; - let block_center = Vector3::new( - f64::from(location.0.x) + 0.5, - f64::from(location.0.y) + 0.5, - f64::from(location.0.z) + 0.5, - ); + block: &Block, + _server: &Server, + ) { + let world = player.world(); + let is_sand = block == &Block::SUSPICIOUS_SAND; + let is_gravel = block == &Block::SUSPICIOUS_GRAVEL; + let block_center = Vector3::new( + f64::from(location.0.x) + 0.5, + f64::from(location.0.y) + 0.5, + f64::from(location.0.z) + 0.5, + ); - if is_sand || is_gravel { - let current_state_id = world.get_block_state_id(&location); - let current_stage = get_dusted_stage(block, current_state_id); + if is_sand || is_gravel { + let current_state_id = world.get_block_state_id(&location); + let current_stage = get_dusted_stage(block, current_state_id); - if current_stage < 3 { - let next_stage_id = - set_dusted_stage(block, current_state_id, current_stage + 1); - world.set_block_state(&location, next_stage_id, BlockFlags::NOTIFY_ALL); + if current_stage < 3 { + let next_stage_id = set_dusted_stage(block, current_state_id, current_stage + 1); + world.set_block_state(&location, next_stage_id, BlockFlags::NOTIFY_ALL); - world.play_sound( - if is_sand { - Sound::ItemBrushBrushingSand - } else { - Sound::ItemBrushBrushingGravel - }, - SoundCategory::Blocks, - &block_center, - ); - } else { - let replacement_state_id = if is_sand { - Block::SAND.default_state.id - } else { - Block::GRAVEL.default_state.id - }; - - world.set_block_state(&location, replacement_state_id, BlockFlags::NOTIFY_ALL); - - world.play_sound( - if is_sand { - Sound::ItemBrushBrushingSandComplete - } else { - Sound::ItemBrushBrushingGravelComplete - }, - SoundCategory::Blocks, - &block_center, - ); - - let loot_item = get_random_archaeology_loot(); - let spawn_pos = Vector3::new( - f64::from(location.0.x) + 0.5, - f64::from(location.0.y) + 1.0, - f64::from(location.0.z) + 0.5, - ); - let item_entity = Arc::new(ItemEntity::new( - Entity::new(world.clone(), spawn_pos, &EntityType::ITEM), - ItemStack::new(1, loot_item), - )); - world.spawn_entity(item_entity); - } - - player.damage_held_item(1).await; - } else { world.play_sound( - Sound::ItemBrushBrushingGeneric, + if is_sand { + Sound::ItemBrushBrushingSand + } else { + Sound::ItemBrushBrushingGravel + }, SoundCategory::Blocks, &block_center, ); - } + } else { + let replacement_state_id = if is_sand { + Block::SAND.default_state.id + } else { + Block::GRAVEL.default_state.id + }; - let stack = player.inventory().held_item(); - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, - ); - }) - } + world.set_block_state(&location, replacement_state_id, BlockFlags::NOTIFY_ALL); - fn use_on_entity<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, - entity: Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let ent = entity.get_entity(); - if ent.entity_type == &EntityType::ARMADILLO { - let world = player.world(); world.play_sound( - Sound::EntityArmadilloBrush, - SoundCategory::Neutral, - &ent.pos.load(), + if is_sand { + Sound::ItemBrushBrushingSandComplete + } else { + Sound::ItemBrushBrushingGravelComplete + }, + SoundCategory::Blocks, + &block_center, ); + let loot_item = get_random_archaeology_loot(); + let spawn_pos = Vector3::new( + f64::from(location.0.x) + 0.5, + f64::from(location.0.y) + 1.0, + f64::from(location.0.z) + 0.5, + ); let item_entity = Arc::new(ItemEntity::new( - Entity::new(world.clone(), ent.pos.load(), &EntityType::ITEM), - ItemStack::new(1, &Item::ARMADILLO_SCUTE), + Entity::new(world.clone(), spawn_pos, &EntityType::ITEM), + ItemStack::new(1, loot_item), )); world.spawn_entity(item_entity); - - player.damage_held_item(16).await; - } else { - let world = player.world(); - world.play_sound( - Sound::ItemBrushBrushingGeneric, - SoundCategory::Neutral, - &ent.pos.load(), - ); } - let stack = player.inventory().held_item(); - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, + player.damage_held_item(1); + } else { + world.play_sound( + Sound::ItemBrushBrushingGeneric, + SoundCategory::Blocks, + &block_center, ); - }) + } + + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); + } + + fn use_on_entity(&self, _item: &mut ItemStack, player: &Player, entity: Arc) { + let ent = entity.get_entity(); + if ent.entity_type == &EntityType::ARMADILLO { + let world = player.world(); + world.play_sound( + Sound::EntityArmadilloBrush, + SoundCategory::Neutral, + &ent.pos.load(), + ); + + let item_entity = Arc::new(ItemEntity::new( + Entity::new(world.clone(), ent.pos.load(), &EntityType::ITEM), + ItemStack::new(1, &Item::ARMADILLO_SCUTE), + )); + world.spawn_entity(item_entity); + + player.damage_held_item(16); + } else { + let world = player.world(); + world.play_sound( + Sound::ItemBrushBrushingGeneric, + SoundCategory::Neutral, + &ent.pos.load(), + ); + } + + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); } fn get_use_duration(&self) -> i32 { diff --git a/crates/pumpkin/src/item/items/bucket.rs b/crates/pumpkin/src/item/items/bucket.rs index 053b12ffc..46c6f7d2e 100644 --- a/crates/pumpkin/src/item/items/bucket.rs +++ b/crates/pumpkin/src/item/items/bucket.rs @@ -1,4 +1,4 @@ -use std::{pin::Pin, sync::Arc}; +use std::sync::Arc; use crate::{ entity::player::Player, @@ -16,7 +16,7 @@ use pumpkin_util::{ GameMode, math::{position::BlockPos, vector3::Vector3}, }; -use pumpkin_world::{inventory::Inventory, tick::TickPriority, world::BlockFlags}; +use pumpkin_world::{tick::TickPriority, world::BlockFlags}; use crate::world::World; @@ -102,7 +102,7 @@ fn set_waterlogged(block: &Block, state: BlockStateId, waterlogged: bool) -> Blo block.from_properties(&props).to_state_id(block) } -async fn give_player_bucket_item(player: &Player, item: &'static Item) { +fn give_player_bucket_item(player: &Player, item: &'static Item) { if player.gamemode.load() == GameMode::Creative { let has_item = { let inv = player @@ -126,17 +126,20 @@ async fn give_player_bucket_item(player: &Player, item: &'static Item) { } else { held_stack.decrement(1); player.inventory.set_held_item(held_stack); - player - .inventory - .offer_or_drop_stack(item_stack, player) - .await; + let mut stack_to_give = item_stack; + let was_added = player.inventory.insert_stack_anywhere(&mut stack_to_give); + if !was_added && !stack_to_give.is_empty() { + player + .world() + .drop_stack(&player.position().to_block_pos(), stack_to_give); + } } } } /// Tries to pick up powder snow, a waterlogged block, or a fluid source block at `block_pos`, /// returning the matching filled bucket item on success. -pub(crate) async fn try_pickup_fluid_at( +pub(crate) fn try_pickup_fluid_at( world: &Arc, block_pos: BlockPos, ) -> Option<&'static Item> { @@ -175,12 +178,12 @@ pub(crate) async fn try_pickup_fluid_at( None } -async fn try_pickup_bucket_item( +fn try_pickup_bucket_item( world: &Arc, block_pos: BlockPos, direction: BlockDirection, ) -> Option<&'static Item> { - if let Some(item) = try_pickup_fluid_at(world, block_pos).await { + if let Some(item) = try_pickup_fluid_at(world, block_pos) { return Some(item); } @@ -231,7 +234,7 @@ fn try_place_powder_snow(world: &Arc, pos: BlockPos, direction: BlockDire true } -pub(crate) async fn try_place_filled_bucket( +pub(crate) fn try_place_filled_bucket( world: &Arc, item: &Item, pos: BlockPos, @@ -279,54 +282,48 @@ pub(crate) async fn try_place_filled_bucket( } impl ItemBehaviour for EmptyBucketItem { - fn normal_use<'a>( - &'a self, - _block: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let (start_pos, end_pos) = get_start_and_end_pos(player); + fn normal_use(&self, _block: &Item, player: &Player) { + let world = player.world(); + let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos); + let checker = |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos); - let block = Block::from_state_id(state_id); + let block = Block::from_state_id(state_id); - if state_id == Block::AIR.default_state.id { - return false; - } - - (block.id != Block::WATER.id && block.id != Block::LAVA.id) - || ((block.id == Block::WATER.id && state_id == Block::WATER.default_state.id) - || (block.id == Block::LAVA.id && state_id == Block::LAVA.default_state.id)) - }; - - let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker) else { - return; - }; - - let Some(item) = try_pickup_bucket_item(&world, block_pos, direction).await else { - return; - }; - - if let Some(server) = world.server.upgrade() - && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) - { - let mut event = - crate::plugin::api::events::player::player_bucket::PlayerBucketFillEvent::new( - player_arc, - block_pos, - item.registry_key.to_string(), - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - return; - } + if state_id == Block::AIR.default_state.id { + return false; } - give_player_bucket_item(player, item).await; - }) + (block.id != Block::WATER.id && block.id != Block::LAVA.id) + || ((block.id == Block::WATER.id && state_id == Block::WATER.default_state.id) + || (block.id == Block::LAVA.id && state_id == Block::LAVA.default_state.id)) + }; + + let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker) else { + return; + }; + + let Some(item) = try_pickup_bucket_item(&world, block_pos, direction) else { + return; + }; + + if let Some(server) = world.server.upgrade() + && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) + { + let mut event = + crate::plugin::api::events::player::player_bucket::PlayerBucketFillEvent::new( + player_arc, + block_pos, + item.registry_key.to_string(), + ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + return; + } + } + + give_player_bucket_item(player, item); } fn as_any(&self) -> &dyn std::any::Any { @@ -335,55 +332,48 @@ impl ItemBehaviour for EmptyBucketItem { } impl ItemBehaviour for FilledBucketItem { - fn normal_use<'a>( - &'a self, - item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos); - if Fluid::from_state_id(state_id).is_some() { - return false; - } - state_id != Block::AIR.default_state.id - }; - - let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker) else { - return; - }; - - if should_evaporate_in_nether(item, &world) { - play_bucket_evaporation(&world, &player.position()); - return; - } - if !try_place_filled_bucket(&world, item, pos, direction).await { - return; + fn normal_use(&self, item: &Item, player: &Player) { + let world = player.world(); + let (start_pos, end_pos) = get_start_and_end_pos(player); + let checker = |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos); + if Fluid::from_state_id(state_id).is_some() { + return false; } + state_id != Block::AIR.default_state.id + }; - if let Some(server) = world.server.upgrade() - && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) - { - let mut event = - crate::plugin::api::events::player::player_bucket::PlayerBucketEmptyEvent::new( - player_arc, - pos, - item.registry_key.to_string(), - ); - server.plugin_manager.fire(&server, &mut event).await; - } + let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker) else { + return; + }; - //TODO: Spawn entity if applicable - if player.gamemode.load() != GameMode::Creative { - let item_stack = ItemStack::new(1, &Item::BUCKET); - player - .inventory - .set_stack(player.inventory.get_selected_slot().into(), item_stack) - .await; - } - }) + if should_evaporate_in_nether(item, &world) { + play_bucket_evaporation(&world, &player.position()); + return; + } + if !try_place_filled_bucket(&world, item, pos, direction) { + return; + } + + if let Some(server) = world.server.upgrade() + && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) + { + let mut event = + crate::plugin::api::events::player::player_bucket::PlayerBucketEmptyEvent::new( + player_arc, + pos, + item.registry_key.to_string(), + ); + server.plugin_manager.fire_blocking(&server, &mut event); + } + + //TODO: Spawn entity if applicable + if player.gamemode.load() != GameMode::Creative { + let item_stack = ItemStack::new(1, &Item::BUCKET); + player + .inventory + .set_slot(player.inventory.get_selected_slot() as usize, item_stack); + } } fn as_any(&self) -> &dyn std::any::Any { @@ -392,28 +382,16 @@ impl ItemBehaviour for FilledBucketItem { } impl ItemBehaviour for MilkBucketItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let stack = player.inventory().held_item(); - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 32); - }) + fn normal_use(&self, _item: &Item, player: &Player) { + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, 32); } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a ItemStack, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - player.living_entity.reset_effects_and_attributes(); - give_player_bucket_item(player, &Item::BUCKET).await; - }) + fn on_stopped_using(&self, _stack: &ItemStack, player: &Player) { + player.living_entity.reset_effects_and_attributes(); + give_player_bucket_item(player, &Item::BUCKET); } fn get_use_duration(&self) -> i32 { diff --git a/crates/pumpkin/src/item/items/bundle.rs b/crates/pumpkin/src/item/items/bundle.rs index 7c68c84bf..6db2aa1ff 100644 --- a/crates/pumpkin/src/item/items/bundle.rs +++ b/crates/pumpkin/src/item/items/bundle.rs @@ -1,5 +1,3 @@ -use std::pin::Pin; - use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use pumpkin_data::data_component_impl::BundleContentsImpl; @@ -16,20 +14,35 @@ impl ItemMetadata for BundleItem { } impl ItemBehaviour for BundleItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let mut held_item = player.inventory.held_item(); - let mut matched = false; - let mut used_slot_index = player.inventory.get_selected_slot() as usize; + fn normal_use(&self, _item: &Item, player: &Player) { + let mut held_item = player.inventory.held_item(); + let mut matched = false; + let mut used_slot_index = player.inventory.get_selected_slot() as usize; - if !held_item.is_empty() && Self::ids().contains(&held_item.item.id) { - matched = true; + if !held_item.is_empty() && Self::ids().contains(&held_item.item.id) { + matched = true; + if let Some(bundle_contents) = held_item.get_data_component_mut::() + && let Some(extracted_stack) = bundle_contents.try_extract() + { + let position = player.position(); + player.world().play_sound( + Sound::ItemBundleRemoveOne, + pumpkin_data::sound::SoundCategory::Players, + &position, + ); + let updated_bundle = held_item.clone(); + + player.drop_item(extracted_stack); + player.sync_hand_slot(used_slot_index, updated_bundle); + } + } + + if !matched { + let mut off_hand_item = player.inventory.off_hand_item(); + if !off_hand_item.is_empty() && Self::ids().contains(&off_hand_item.item.id) { + used_slot_index = 40; // OFF_HAND_SLOT if let Some(bundle_contents) = - held_item.get_data_component_mut::() + off_hand_item.get_data_component_mut::() && let Some(extracted_stack) = bundle_contents.try_extract() { let position = player.position(); @@ -38,35 +51,13 @@ impl ItemBehaviour for BundleItem { pumpkin_data::sound::SoundCategory::Players, &position, ); - let updated_bundle = held_item.clone(); + let updated_bundle = off_hand_item.clone(); player.drop_item(extracted_stack); - player.sync_hand_slot(used_slot_index, updated_bundle).await; + player.sync_hand_slot(used_slot_index, updated_bundle); } } - - if !matched { - let mut off_hand_item = player.inventory.off_hand_item(); - if !off_hand_item.is_empty() && Self::ids().contains(&off_hand_item.item.id) { - used_slot_index = 40; // OFF_HAND_SLOT - if let Some(bundle_contents) = - off_hand_item.get_data_component_mut::() - && let Some(extracted_stack) = bundle_contents.try_extract() - { - let position = player.position(); - player.world().play_sound( - Sound::ItemBundleRemoveOne, - pumpkin_data::sound::SoundCategory::Players, - &position, - ); - let updated_bundle = off_hand_item.clone(); - - player.drop_item(extracted_stack); - player.sync_hand_slot(used_slot_index, updated_bundle).await; - } - } - } - }) + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/clock.rs b/crates/pumpkin/src/item/items/clock.rs index f77ba0f7c..baee2a31e 100644 --- a/crates/pumpkin/src/item/items/clock.rs +++ b/crates/pumpkin/src/item/items/clock.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -16,19 +14,13 @@ impl ItemMetadata for ClockItem { } impl ItemBehaviour for ClockItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - world.play_sound( - Sound::UiButtonClick, - SoundCategory::Players, - &player.position(), - ); - }) + fn normal_use(&self, _item: &Item, player: &Player) { + let world = player.world(); + world.play_sound( + Sound::UiButtonClick, + SoundCategory::Players, + &player.position(), + ); } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/compass.rs b/crates/pumpkin/src/item/items/compass.rs index 0ead5b5d1..632f6c56e 100644 --- a/crates/pumpkin/src/item/items/compass.rs +++ b/crates/pumpkin/src/item/items/compass.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -23,46 +21,46 @@ impl ItemMetadata for CompassItem { } impl ItemBehaviour for CompassItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if block.id == Block::LODESTONE.id && item.item.id == Item::COMPASS.id { - let world = player.world(); - world.play_sound( - Sound::ItemLodestoneCompassLock, - SoundCategory::Players, - &location.to_f64(), - ); + block: &Block, + _server: &Server, + ) { + if block.id == Block::LODESTONE.id && item.item.id == Item::COMPASS.id { + let world = player.world(); + world.play_sound( + Sound::ItemLodestoneCompassLock, + SoundCategory::Players, + &location.to_f64(), + ); - let mut lodestone_compass = ItemStack::new(1, &Item::COMPASS); - let tracker = LodestoneTrackerImpl { - target: Some(LodestoneTarget { - dimension: world.dimension.minecraft_name.to_string(), - x: location.0.x, - y: location.0.y, - z: location.0.z, - }), - tracked: true, - }; - lodestone_compass - .patch - .push((DataComponent::LodestoneTracker, Some(tracker.to_dyn()))); + let mut lodestone_compass = ItemStack::new(1, &Item::COMPASS); + let tracker = LodestoneTrackerImpl { + target: Some(LodestoneTarget { + dimension: world.dimension.minecraft_name.to_string(), + x: location.0.x, + y: location.0.y, + z: location.0.z, + }), + tracked: true, + }; + lodestone_compass + .patch + .push((DataComponent::LodestoneTracker, Some(tracker.to_dyn()))); - item.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .offer_or_drop_stack(lodestone_compass, player) - .await; + item.decrement_unless_creative(player.gamemode.load(), 1); + let was_added = player + .inventory + .insert_stack_anywhere(&mut lodestone_compass); + if !was_added && !lodestone_compass.is_empty() { + world.drop_stack(&player.position().to_block_pos(), lodestone_compass); } - }) + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/crossbow.rs b/crates/pumpkin/src/item/items/crossbow.rs index 68d3e44af..9c3b47cb7 100644 --- a/crates/pumpkin/src/item/items/crossbow.rs +++ b/crates/pumpkin/src/item/items/crossbow.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -14,7 +12,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::GameMode; -use pumpkin_world::inventory::Inventory; pub struct CrossbowItem; @@ -25,102 +22,90 @@ impl ItemMetadata for CrossbowItem { } impl ItemBehaviour for CrossbowItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let inventory = player.inventory(); - let stack = inventory.held_item(); + fn normal_use(&self, _item: &Item, player: &Player) { + let inventory = player.inventory(); + let stack = inventory.held_item(); - // Every crossbow carries a ChargedProjectiles component by default, so its mere - // presence does not mean the crossbow is loaded. Vanilla checks the list is also - // non-empty (CrossbowItem.java:68). - if stack - .get_data_component::() - .is_some_and(|charged| !charged.projectiles.is_empty()) - { - Self::fire_projectiles(player).await; - return; - } + // Every crossbow carries a ChargedProjectiles component by default, so its mere + // presence does not mean the crossbow is loaded. Vanilla checks the list is also + // non-empty (CrossbowItem.java:68). + if stack + .get_data_component::() + .is_some_and(|charged| !charged.projectiles.is_empty()) + { + Self::fire_projectiles(player); + return; + } - let has_arrows = player.find_arrow().await.is_some(); - if !has_arrows && player.gamemode.load() != GameMode::Creative { - return; - } + let has_arrows = player.find_arrow().is_some(); + if !has_arrows && player.gamemode.load() != GameMode::Creative { + return; + } - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); - }) + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a ItemStack, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let use_ticks = player.living_entity.item_use_time.load(Ordering::Relaxed); - let use_ticks = 72000 - use_ticks; + fn on_stopped_using(&self, _stack: &ItemStack, player: &Player) { + let use_ticks = player.living_entity.item_use_time.load(Ordering::Relaxed); + let use_ticks = 72000 - use_ticks; - let mut charge_time = 25; - let mut stack = player.inventory().held_item(); + let mut charge_time = 25; + let mut stack = player.inventory().held_item(); - if let Some(enchantments) = stack.get_data_component::() { - for (enchantment, level) in enchantments.enchantment.iter() { - if **enchantment == pumpkin_data::Enchantment::QUICK_CHARGE { - charge_time -= 5 * level; - } + if let Some(enchantments) = stack.get_data_component::() { + for (enchantment, level) in enchantments.enchantment.iter() { + if **enchantment == pumpkin_data::Enchantment::QUICK_CHARGE { + charge_time -= 5 * level; } } - charge_time = charge_time.max(0); + } + charge_time = charge_time.max(0); - if use_ticks >= charge_time { - let arrow_slot = player.find_arrow().await; - let (arrow_nbt_wrapper, slot) = { - if let Some(slot) = arrow_slot { - let inventory = player.inventory(); + if use_ticks >= charge_time { + let arrow_slot = player.find_arrow(); + let (arrow_nbt_wrapper, slot) = { + if let Some(slot) = arrow_slot { + let inventory = player.inventory(); - let arrow_stack = inventory.get_stack(slot).await; - let mut arrow_nbt = pumpkin_nbt::compound::NbtCompound::new(); - arrow_stack - .copy_with_count(1) - .write_item_stack(&mut arrow_nbt); - (Some(arrow_nbt), slot) - } else if player.gamemode.load() == GameMode::Creative { - let mut arrow_nbt = pumpkin_nbt::compound::NbtCompound::new(); - let arrow_stack = ItemStack::new(1, &Item::ARROW); - arrow_stack.write_item_stack(&mut arrow_nbt); + let arrow_stack = inventory.get_slot(slot); + let mut arrow_nbt = pumpkin_nbt::compound::NbtCompound::new(); + arrow_stack + .copy_with_count(1) + .write_item_stack(&mut arrow_nbt); + (Some(arrow_nbt), slot) + } else if player.gamemode.load() == GameMode::Creative { + let mut arrow_nbt = pumpkin_nbt::compound::NbtCompound::new(); + let arrow_stack = ItemStack::new(1, &Item::ARROW); + arrow_stack.write_item_stack(&mut arrow_nbt); - (Some(arrow_nbt), 0) - } else { - (None, 0) - } - }; - if let Some(arrow_nbt) = arrow_nbt_wrapper { - stack.patch.push(( - DataComponent::ChargedProjectiles, - Some(Box::new(ChargedProjectilesImpl { - projectiles: vec![arrow_nbt], - })), - )); - player.inventory().set_held_item(stack); - - if player.gamemode.load() != GameMode::Creative { - player.consume_arrow(slot).await; - } - - player.world().play_sound( - Sound::ItemCrossbowLoadingEnd, - SoundCategory::Players, - &player.position(), - ); + (Some(arrow_nbt), 0) + } else { + (None, 0) } + }; + if let Some(arrow_nbt) = arrow_nbt_wrapper { + stack.patch.push(( + DataComponent::ChargedProjectiles, + Some(Box::new(ChargedProjectilesImpl { + projectiles: vec![arrow_nbt], + })), + )); + player.inventory().set_held_item(stack); + + if player.gamemode.load() != GameMode::Creative { + player.consume_arrow(slot); + } + + player.world().play_sound( + Sound::ItemCrossbowLoadingEnd, + SoundCategory::Players, + &player.position(), + ); } - player.living_entity.clear_active_hand(); - }) + } + player.living_entity.clear_active_hand(); } fn get_use_duration(&self) -> i32 { @@ -133,7 +118,7 @@ impl ItemBehaviour for CrossbowItem { } impl CrossbowItem { - async fn fire_projectiles(player: &Player) { + fn fire_projectiles(player: &Player) { let mut held = player.inventory().held_item(); let projectiles = held.get_data_component::().cloned(); let has_multishot = @@ -192,7 +177,7 @@ impl CrossbowItem { held.patch .retain(|(id, _)| *id != DataComponent::ChargedProjectiles); player.inventory().set_held_item(held); - player.damage_held_item(1).await; + player.damage_held_item(1); } } } diff --git a/crates/pumpkin/src/item/items/egg.rs b/crates/pumpkin/src/item/items/egg.rs index cce289dcb..d3bb27ee1 100644 --- a/crates/pumpkin/src/item/items/egg.rs +++ b/crates/pumpkin/src/item/items/egg.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -22,54 +21,48 @@ impl ItemMetadata for EggItem { const POWER: f32 = 1.5; impl ItemBehaviour for EggItem { - fn normal_use<'a>( - &'a self, - _block: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let position = player.position(); - let world = player.world(); - world.play_sound( - Sound::EntityEggThrow, - pumpkin_data::sound::SoundCategory::Players, - &position, - ); + fn normal_use(&self, _block: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + world.play_sound( + Sound::EntityEggThrow, + pumpkin_data::sound::SoundCategory::Players, + &position, + ); - // Capture the held item stack and pass it to the thrown egg entity - let item_stack: ItemStack = player.inventory.held_item(); + // Capture the held item stack and pass it to the thrown egg entity + let item_stack: ItemStack = player.inventory.held_item(); - let entity = Entity::new(world.clone(), position, &EntityType::EGG); - let egg = EggEntity::new_shot(entity, player.get_entity()); + let entity = Entity::new(world.clone(), position, &EntityType::EGG); + let egg = EggEntity::new_shot(entity, player.get_entity()); - // Propagate the item stack so clients show correct variant - egg.set_item_stack(item_stack); + // Propagate the item stack so clients show correct variant + egg.set_item_stack(item_stack); - let (yaw, pitch) = player.rotation(); - egg.thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(egg)); + let (yaw, pitch) = player.rotation(); + egg.thrown + .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(egg)); - // Consume item - let mut main_hand = player.inventory.held_item(); - let consumed = if !main_hand.is_empty() && Self::ids().contains(&main_hand.item.id) { - main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand); - true - } else { - false - }; + // Consume item + let mut main_hand = player.inventory.held_item(); + let consumed = if !main_hand.is_empty() && Self::ids().contains(&main_hand.item.id) { + main_hand.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(main_hand); + true + } else { + false + }; - if !consumed { - let mut off_hand = player.inventory.off_hand_item(); - if !off_hand.is_empty() && Self::ids().contains(&off_hand.item.id) { - off_hand.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); - } + if !consumed { + let mut off_hand = player.inventory.off_hand_item(); + if !off_hand.is_empty() && Self::ids().contains(&off_hand.item.id) { + off_hand.decrement_unless_creative(player.gamemode.load(), 1); + player + .inventory + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } - }) + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/end_crystal.rs b/crates/pumpkin/src/item/items/end_crystal.rs index 8d93b27db..cc0bfe025 100644 --- a/crates/pumpkin/src/item/items/end_crystal.rs +++ b/crates/pumpkin/src/item/items/end_crystal.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -23,43 +22,41 @@ impl ItemMetadata for EndCrystalItem { } impl ItemBehaviour for EndCrystalItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - _block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let block = world.get_block(&location); - if block != &Block::OBSIDIAN && block != &Block::BEDROCK { - return; - } + _block: &Block, + _server: &Server, + ) { + let world = player.world(); + let block = world.get_block(&location); + if block != &Block::OBSIDIAN && block != &Block::BEDROCK { + return; + } - let location = location.up(); - let location_vec = location.0.to_f64(); + let location = location.up(); + let location_vec = location.0.to_f64(); - if !world.get_block_state(&location).is_air() - || !world - .get_entities_at_box(&BoundingBox::new( - Vector3::new(location_vec.x, location_vec.y, location_vec.z), - Vector3::new(location_vec.x + 1.0, location_vec.y + 2.0, location_vec.z), - )) - .is_empty() - { - return; - } + if !world.get_block_state(&location).is_air() + || !world + .get_entities_at_box(&BoundingBox::new( + Vector3::new(location_vec.x, location_vec.y, location_vec.z), + Vector3::new(location_vec.x + 1.0, location_vec.y + 2.0, location_vec.z), + )) + .is_empty() + { + return; + } - let entity = Entity::new(world.clone(), location.to_f64(), &EntityType::END_CRYSTAL); - let end_crystal = Arc::new(EndCrystalEntity::new(entity)); - world.spawn_entity(end_crystal.clone()); - end_crystal.set_show_bottom(false); - item.decrement_unless_creative(player.gamemode.load(), 1); - }) + let entity = Entity::new(world.clone(), location.to_f64(), &EntityType::END_CRYSTAL); + let end_crystal = Arc::new(EndCrystalEntity::new(entity)); + world.spawn_entity(end_crystal.clone()); + end_crystal.set_show_bottom(false); + item.decrement_unless_creative(player.gamemode.load(), 1); } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/ender_eye.rs b/crates/pumpkin/src/item/items/ender_eye.rs index 210dd624a..07314fa8b 100644 --- a/crates/pumpkin/src/item/items/ender_eye.rs +++ b/crates/pumpkin/src/item/items/ender_eye.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -31,113 +30,105 @@ impl ItemMetadata for EnderEyeItem { } impl ItemBehaviour for EnderEyeItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if block.id != Block::END_PORTAL_FRAME.id { + block: &Block, + _server: &Server, + ) { + if block.id != Block::END_PORTAL_FRAME.id { + return; + } + + let world = player.world(); + let state_id = world.get_block_state_id(&location); + + let new_state_id = { + // Skip if the frame already holds an eye. + let Some(props) = block.properties(state_id) else { + return; + }; + let props_raw = props.to_props(); + if props_raw.iter().any(|(k, v)| *k == "eye" && *v == "true") { return; } - let world = player.world(); - let state_id = world.get_block_state_id(&location); + // Build new state with eye=true. + let props: Vec<(&str, &str)> = props_raw + .iter() + .map(|(k, v)| if *k == "eye" { (*k, "true") } else { (*k, *v) }) + .collect(); - let new_state_id = { - // Skip if the frame already holds an eye. - let Some(props) = block.properties(state_id) else { - return; - }; - let props_raw = props.to_props(); - if props_raw.iter().any(|(k, v)| *k == "eye" && *v == "true") { - return; - } + block.from_properties(&props).to_state_id(block) + }; - // Build new state with eye=true. - let props: Vec<(&str, &str)> = props_raw - .iter() - .map(|(k, v)| if *k == "eye" { (*k, "true") } else { (*k, *v) }) - .collect(); + world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_LISTENERS); + // Consume one item. + item.decrement_unless_creative(player.gamemode.load(), 1); + world.sync_world_event(WorldEvent::EndPortalFrameFill, location, 0); - block.from_properties(&props).to_state_id(block) - }; - - world.set_block_state(&location, new_state_id, BlockFlags::NOTIFY_LISTENERS); - // Consume one item. - item.decrement_unless_creative(player.gamemode.load(), 1); - world.sync_world_event(WorldEvent::EndPortalFrameFill, location, 0); - - // Try to complete the portal. - EndPortal::get_new_portal(&world, location); - }) + // Try to complete the portal. + EndPortal::get_new_portal(&world, location); } - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); + fn normal_use(&self, _item: &Item, player: &Player) { + let world = player.world(); - let (start_pos, end_pos) = self.get_start_and_end_pos(player); - let checker = |pos: &BlockPos, w: &Arc| { - w.get_block_state_id(pos) != Block::AIR.default_state.id - }; - if let Some((hit_pos, _)) = world.raycast(start_pos, end_pos, checker) - && world.get_block(&hit_pos) == &Block::END_PORTAL_FRAME - { - return; - } + let (start_pos, end_pos) = self.get_start_and_end_pos(player); + let checker = |pos: &BlockPos, w: &Arc| { + w.get_block_state_id(pos) != Block::AIR.default_state.id + }; + if let Some((hit_pos, _)) = world.raycast(start_pos, end_pos, checker) + && world.get_block(&hit_pos) == &Block::END_PORTAL_FRAME + { + return; + } - let origin = player.get_entity().block_pos.load(); - let target_block_pos = find_stronghold(&world, origin); + let origin = player.get_entity().block_pos.load(); + let target_block_pos = find_stronghold(&world, origin); - let Some(target) = target_block_pos else { - return; - }; + let Some(target) = target_block_pos else { + return; + }; - let spawn_pos = Vector3::new( - player.get_entity().pos.load().x, - player.get_entity().pos.load().y - + f64::from(EntityType::EYE_OF_ENDER.dimension[1]) * 0.5, - player.get_entity().pos.load().z, - ); + let spawn_pos = Vector3::new( + player.get_entity().pos.load().x, + player.get_entity().pos.load().y + + f64::from(EntityType::EYE_OF_ENDER.dimension[1]) * 0.5, + player.get_entity().pos.load().z, + ); - let entity = Entity::new(world.clone(), spawn_pos, &EntityType::EYE_OF_ENDER); - let eye = Arc::new(EyeOfEnder::new(entity)); + let entity = Entity::new(world.clone(), spawn_pos, &EntityType::EYE_OF_ENDER); + let eye = Arc::new(EyeOfEnder::new(entity)); - let target_vec = Vector3::new( - f64::from(target.0.x), - f64::from(target.0.y), - f64::from(target.0.z), - ); - eye.signal_to(target_vec); + let target_vec = Vector3::new( + f64::from(target.0.x), + f64::from(target.0.y), + f64::from(target.0.z), + ); + eye.signal_to(target_vec); - world.spawn_entity(eye); + world.spawn_entity(eye); - let pitch = 0.33f32 + rand::random::() * (0.5 - 0.33); - world.play_sound_fine( - Sound::EntityEnderEyeLaunch, - SoundCategory::Neutral, - &spawn_pos, - 1.0, - pitch, - ); + let pitch = 0.33f32 + rand::random::() * (0.5 - 0.33); + world.play_sound_fine( + Sound::EntityEnderEyeLaunch, + SoundCategory::Neutral, + &spawn_pos, + 1.0, + pitch, + ); - player.trigger_advancement( - crate::entity::player::advancement::trigger::AdvancementTrigger::LaunchedEyeOfEnder, - ); - let mut stack = player.inventory.held_item(); - stack.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(stack); - }) + player.trigger_advancement( + crate::entity::player::advancement::trigger::AdvancementTrigger::LaunchedEyeOfEnder, + ); + let mut stack = player.inventory.held_item(); + stack.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(stack); } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/ender_pearl.rs b/crates/pumpkin/src/item/items/ender_pearl.rs index b9e51350c..130ae383a 100644 --- a/crates/pumpkin/src/item/items/ender_pearl.rs +++ b/crates/pumpkin/src/item/items/ender_pearl.rs @@ -1,6 +1,5 @@ use rand::{RngExt, rng}; -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -26,55 +25,44 @@ const DIVERGENCE: f32 = 1.0; const THROW_SOUND_VOLUME: f32 = 0.5; impl ItemBehaviour for EnderPearlItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let position = player.position(); - let world = player.world(); - world.play_sound_fine( - Sound::EntityEnderPearlThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - THROW_SOUND_VOLUME, - 0.4 / (rng().random::() * 0.4 + 0.8), - ); + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + world.play_sound_fine( + Sound::EntityEnderPearlThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + THROW_SOUND_VOLUME, + 0.4 / (rng().random::() * 0.4 + 0.8), + ); - let entity = Entity::new(world.clone(), position, &EntityType::ENDER_PEARL); - let pearl = EnderPearlEntity::new_shot(entity, player.get_entity()); - let (yaw, pitch) = player.rotation(); - pearl.thrown.set_velocity_from( - player.get_entity(), - pitch, - yaw, - ROLL, - POWER, - DIVERGENCE, - ); - world.spawn_entity(Arc::new(pearl)); + let entity = Entity::new(world.clone(), position, &EntityType::ENDER_PEARL); + let pearl = EnderPearlEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + pearl + .thrown + .set_velocity_from(player.get_entity(), pitch, yaw, ROLL, POWER, DIVERGENCE); + world.spawn_entity(Arc::new(pearl)); - // Consume item - let mut main_hand = player.inventory.held_item(); - let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::ENDER_PEARL.id { - main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand); - true - } else { - false - }; + // Consume item + let mut main_hand = player.inventory.held_item(); + let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::ENDER_PEARL.id { + main_hand.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(main_hand); + true + } else { + false + }; - if !consumed { - let mut off_hand = player.inventory.off_hand_item(); - if !off_hand.is_empty() && off_hand.item.id == Item::ENDER_PEARL.id { - off_hand.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); - } + if !consumed { + let mut off_hand = player.inventory.off_hand_item(); + if !off_hand.is_empty() && off_hand.item.id == Item::ENDER_PEARL.id { + off_hand.decrement_unless_creative(player.gamemode.load(), 1); + player + .inventory + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } - }) + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/experience_bottle.rs b/crates/pumpkin/src/item/items/experience_bottle.rs index a64ed8052..ee4f77e9c 100644 --- a/crates/pumpkin/src/item/items/experience_bottle.rs +++ b/crates/pumpkin/src/item/items/experience_bottle.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::player::Player; @@ -17,27 +15,21 @@ impl ItemMetadata for ExperienceBottleItem { } impl ItemBehaviour for ExperienceBottleItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let pos = player.eye_position(); - world.play_sound( - Sound::EntityExperienceBottleThrow, - SoundCategory::Players, - &pos, - ); + fn normal_use(&self, _item: &Item, player: &Player) { + let world = player.world(); + let pos = player.eye_position(); + world.play_sound( + Sound::EntityExperienceBottleThrow, + SoundCategory::Players, + &pos, + ); - let amount = (rand::random::() % 9 + 3) as u32; // 3..=11 exp - ExperienceOrbEntity::spawn(&world, pos, amount); + let amount = (rand::random::() % 9 + 3) as u32; // 3..=11 exp + ExperienceOrbEntity::spawn(&world, pos, amount); - let mut held = player.inventory().held_item(); - held.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory().set_held_item(held); - }) + let mut held = player.inventory().held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory().set_held_item(held); } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/firework_rocket.rs b/crates/pumpkin/src/item/items/firework_rocket.rs index a8802d4b5..65ce72e2c 100644 --- a/crates/pumpkin/src/item/items/firework_rocket.rs +++ b/crates/pumpkin/src/item/items/firework_rocket.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::player::Player; @@ -23,49 +22,41 @@ impl ItemMetadata for FireworkRocketItem { } impl ItemBehaviour for FireworkRocketItem { - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, cursor_pos: Vector3, - _block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { + _block: &Block, + _server: &Server, + ) { + let world = player.world(); + let entity = Entity::new( + world.clone(), + Vector3::new( + f64::from(location.0.x) + f64::from(cursor_pos.x), + f64::from(location.0.y) + f64::from(cursor_pos.y), + f64::from(location.0.z) + f64::from(cursor_pos.z), + ), + &EntityType::FIREWORK_ROCKET, + ); + let entity = FireworkRocketEntity::new(entity); + world.spawn_entity(Arc::new(entity)); + } + + fn normal_use(&self, _item: &Item, player: &Player) { + if player.get_entity().is_fall_flying() { let world = player.world(); let entity = Entity::new( world.clone(), - Vector3::new( - f64::from(location.0.x) + f64::from(cursor_pos.x), - f64::from(location.0.y) + f64::from(cursor_pos.y), - f64::from(location.0.z) + f64::from(cursor_pos.z), - ), + player.get_entity().pos.load(), &EntityType::FIREWORK_ROCKET, ); - let entity = FireworkRocketEntity::new(entity); + let entity = FireworkRocketEntity::new_shot(entity, player.get_entity()); world.spawn_entity(Arc::new(entity)); - }) - } - - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async { - if player.get_entity().is_fall_flying() { - let world = player.world(); - let entity = Entity::new( - world.clone(), - player.get_entity().pos.load(), - &EntityType::FIREWORK_ROCKET, - ); - let entity = FireworkRocketEntity::new_shot(entity, player.get_entity()); - world.spawn_entity(Arc::new(entity)); - } - }) + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/fishing_rod.rs b/crates/pumpkin/src/item/items/fishing_rod.rs index 4ff3bfab7..22c1f12b0 100644 --- a/crates/pumpkin/src/item/items/fishing_rod.rs +++ b/crates/pumpkin/src/item/items/fishing_rod.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -21,62 +19,54 @@ impl ItemMetadata for FishingRodItem { } impl ItemBehaviour for FishingRodItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let bobber_id = player.fishing_bobber.load(Ordering::Relaxed); + fn normal_use(&self, _item: &Item, player: &Player) { + let world = player.world(); + let bobber_id = player.fishing_bobber.load(Ordering::Relaxed); - if bobber_id == -1 { - // Cast - world.play_sound( - Sound::EntityFishingBobberThrow, - SoundCategory::Neutral, - &player.position(), - ); + if bobber_id == -1 { + // Cast + world.play_sound( + Sound::EntityFishingBobberThrow, + SoundCategory::Neutral, + &player.position(), + ); - let bobber_entity = Entity::new( - world.clone(), - player.position(), - &EntityType::FISHING_BOBBER, - ); - let bobber = FishingBobberEntity::new(bobber_entity, player); + let bobber_entity = Entity::new( + world.clone(), + player.position(), + &EntityType::FISHING_BOBBER, + ); + let bobber = FishingBobberEntity::new(bobber_entity, player); - let look_vec = player.living_entity.get_looking_vector(); - bobber - .entity - .velocity - .store(look_vec.multiply(1.5, 1.5, 1.5)); + let look_vec = player.living_entity.get_looking_vector(); + bobber + .entity + .velocity + .store(look_vec.multiply(1.5, 1.5, 1.5)); - player - .fishing_bobber - .store(bobber.entity.entity_id, Ordering::Relaxed); + player + .fishing_bobber + .store(bobber.entity.entity_id, Ordering::Relaxed); - let bobber_arc: Arc = Arc::new(bobber); - world.spawn_entity(bobber_arc); - } else { - // Reel in - if let Some(bobber_base) = world.get_entity_by_id(bobber_id) { - if let Some(bobber) = - bobber_base.cast_any().downcast_ref::() - { - let _result = bobber.reel_in(player); - // TODO: give items - } - bobber_base.get_entity().remove(); + let bobber_arc: Arc = Arc::new(bobber); + world.spawn_entity(bobber_arc); + } else { + // Reel in + if let Some(bobber_base) = world.get_entity_by_id(bobber_id) { + if let Some(bobber) = bobber_base.cast_any().downcast_ref::() { + let _result = bobber.reel_in(player); + // TODO: give items } - player.fishing_bobber.store(-1, Ordering::Relaxed); - - world.play_sound( - Sound::EntityFishingBobberRetrieve, - SoundCategory::Neutral, - &player.position(), - ); + bobber_base.get_entity().remove(); } - }) + player.fishing_bobber.store(-1, Ordering::Relaxed); + + world.play_sound( + Sound::EntityFishingBobberRetrieve, + SoundCategory::Neutral, + &player.position(), + ); + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/glass_bottle.rs b/crates/pumpkin/src/item/items/glass_bottle.rs index d19a9fd04..7fbe8453d 100644 --- a/crates/pumpkin/src/item/items/glass_bottle.rs +++ b/crates/pumpkin/src/item/items/glass_bottle.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -22,81 +20,78 @@ impl ItemMetadata for GlassBottleItem { } impl ItemBehaviour for GlassBottleItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); + block: &Block, + _server: &Server, + ) { + let world = player.world(); - let is_water_target = - block.id == Block::WATER.id || block.id == Block::WATER_CAULDRON.id; + let is_water_target = block.id == Block::WATER.id || block.id == Block::WATER_CAULDRON.id; - let check_pos = if is_water_target { - location - } else { - location.offset(face.to_offset()) - }; + let check_pos = if is_water_target { + location + } else { + location.offset(face.to_offset()) + }; - let (check_block, check_state_id) = world.get_block_and_state_id(&check_pos); + let (check_block, check_state_id) = world.get_block_and_state_id(&check_pos); - if check_block.id == Block::WATER.id || check_block.id == Block::WATER_CAULDRON.id { - let cauldron_action = (check_block.id == Block::WATER_CAULDRON.id) - .then(|| { - check_block.properties(check_state_id).and_then(|props| { - let prop_map = props.to_props(); - prop_map - .iter() - .find(|(k, _)| *k == "level") - .and_then(|(_, level_str)| level_str.parse::().ok()) - .map(|level| { - if level > 1 { - let new_level = (level - 1).to_string(); - let new_props: Vec<(&str, &str)> = prop_map - .iter() - .map(|(k, v)| { - if *k == "level" { - (*k, new_level.as_str()) - } else { - (*k, *v) - } - }) - .collect(); - check_block - .from_properties(&new_props) - .to_state_id(check_block) - } else { - Block::CAULDRON.default_state.id - } - }) - }) + if check_block.id == Block::WATER.id || check_block.id == Block::WATER_CAULDRON.id { + let cauldron_action = (check_block.id == Block::WATER_CAULDRON.id) + .then(|| { + check_block.properties(check_state_id).and_then(|props| { + let prop_map = props.to_props(); + prop_map + .iter() + .find(|(k, _)| *k == "level") + .and_then(|(_, level_str)| level_str.parse::().ok()) + .map(|level| { + if level > 1 { + let new_level = (level - 1).to_string(); + let new_props: Vec<(&str, &str)> = prop_map + .iter() + .map(|(k, v)| { + if *k == "level" { + (*k, new_level.as_str()) + } else { + (*k, *v) + } + }) + .collect(); + check_block + .from_properties(&new_props) + .to_state_id(check_block) + } else { + Block::CAULDRON.default_state.id + } + }) }) - .flatten(); + }) + .flatten(); - if let Some(new_state_id) = cauldron_action { - world.set_block_state(&check_pos, new_state_id, BlockFlags::NOTIFY_ALL); - } - - world.play_sound( - Sound::ItemBottleFill, - SoundCategory::Players, - &check_pos.to_f64(), - ); - - let water_bottle = ItemStack::new(1, &Item::POTION); - item.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .offer_or_drop_stack(water_bottle, player) - .await; + if let Some(new_state_id) = cauldron_action { + world.set_block_state(&check_pos, new_state_id, BlockFlags::NOTIFY_ALL); } - }) + + world.play_sound( + Sound::ItemBottleFill, + SoundCategory::Players, + &check_pos.to_f64(), + ); + + let mut water_bottle = ItemStack::new(1, &Item::POTION); + item.decrement_unless_creative(player.gamemode.load(), 1); + let was_added = player.inventory.insert_stack_anywhere(&mut water_bottle); + if !was_added && !water_bottle.is_empty() { + world.drop_stack(&player.position().to_block_pos(), water_bottle); + } + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/goat_horn.rs b/crates/pumpkin/src/item/items/goat_horn.rs index cd073a22d..a0ee65cb9 100644 --- a/crates/pumpkin/src/item/items/goat_horn.rs +++ b/crates/pumpkin/src/item/items/goat_horn.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -16,24 +14,16 @@ impl ItemMetadata for GoatHornItem { } impl ItemBehaviour for GoatHornItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - player.world().play_sound( - Sound::ItemGoatHornSound0, - SoundCategory::Players, - &player.position(), - ); - let stack = player.inventory().held_item(); - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, - ); - }) + fn normal_use(&self, _item: &Item, player: &Player) { + player.world().play_sound( + Sound::ItemGoatHornSound0, + SoundCategory::Players, + &player.position(), + ); + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); } fn get_use_duration(&self) -> i32 { diff --git a/crates/pumpkin/src/item/items/hoe.rs b/crates/pumpkin/src/item/items/hoe.rs index 164f2b5ba..85974209f 100644 --- a/crates/pumpkin/src/item/items/hoe.rs +++ b/crates/pumpkin/src/item/items/hoe.rs @@ -12,7 +12,6 @@ use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; -use std::pin::Pin; use std::sync::Arc; pub struct HoeItem; @@ -24,86 +23,84 @@ impl ItemMetadata for HoeItem { } impl ItemBehaviour for HoeItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - // Yes, Minecraft does hardcode these - if block == &Block::GRASS_BLOCK - || block == &Block::DIRT_PATH - || block == &Block::DIRT - || block == &Block::COARSE_DIRT - || block == &Block::ROOTED_DIRT - { - let mut future_block = block; - let world = player.world(); - let mut changed = false; + block: &Block, + _server: &Server, + ) { + // Yes, Minecraft does hardcode these + if block == &Block::GRASS_BLOCK + || block == &Block::DIRT_PATH + || block == &Block::DIRT + || block == &Block::COARSE_DIRT + || block == &Block::ROOTED_DIRT + { + let mut future_block = block; + let world = player.world(); + let mut changed = false; - //Only rooted can be right-clicked on the bottom of the block - if face == BlockDirection::Down { - if block == &Block::ROOTED_DIRT { - future_block = &Block::DIRT; - changed = true; - } - } else { - // grass, dirt && dirt path become farmland - if (block == &Block::GRASS_BLOCK - || block == &Block::DIRT_PATH - || block == &Block::DIRT) - && world.get_block_state(&location.up()).is_air() - { - future_block = &Block::FARMLAND; - changed = true; - } - //Coarse dirt and rooted dirt become dirt - else if block == &Block::COARSE_DIRT || block == &Block::ROOTED_DIRT { - future_block = &Block::DIRT; - changed = true; - } - } - - // Vanilla returns PASS without touching the block when nothing is tilled, - // otherwise the rewrite would reset properties such as `snowy` on grass blocks. - if changed { - world.set_block_state( - &location, - future_block.default_state.id, - BlockFlags::NOTIFY_ALL, - ); - } - - //Also rooted_dirt drop a hanging_root + //Only rooted can be right-clicked on the bottom of the block + if face == BlockDirection::Down { if block == &Block::ROOTED_DIRT { - let location = match face { - BlockDirection::Up => location.up().to_f64(), - BlockDirection::Down => location.down().to_f64(), - BlockDirection::North => location.up().to_f64().add_raw(0.0, -0.4, -1.0), - BlockDirection::South => location.up().to_f64().add_raw(0.0, -0.4, 1.0), - BlockDirection::West => location.up().to_f64().add_raw(-1.0, -0.4, 0.0), - BlockDirection::East => location.up().to_f64().add_raw(1.0, -0.4, 0.0), - }; - let entity = Entity::new(world.clone(), location, &EntityType::ITEM); - // TODO: Merge stacks together - let item_entity = Arc::new(ItemEntity::new( - entity, - ItemStack::new(1, &Item::HANGING_ROOTS), - )); - world.spawn_entity(item_entity); + future_block = &Block::DIRT; + changed = true; } - - if changed && player.gamemode.load() != GameMode::Creative { - // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. - let _ = item.damage_item(1); + } else { + // grass, dirt && dirt path become farmland + if (block == &Block::GRASS_BLOCK + || block == &Block::DIRT_PATH + || block == &Block::DIRT) + && world.get_block_state(&location.up()).is_air() + { + future_block = &Block::FARMLAND; + changed = true; + } + //Coarse dirt and rooted dirt become dirt + else if block == &Block::COARSE_DIRT || block == &Block::ROOTED_DIRT { + future_block = &Block::DIRT; + changed = true; } } - }) + + // Vanilla returns PASS without touching the block when nothing is tilled, + // otherwise the rewrite would reset properties such as `snowy` on grass blocks. + if changed { + world.set_block_state( + &location, + future_block.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + } + + //Also rooted_dirt drop a hanging_root + if block == &Block::ROOTED_DIRT { + let location = match face { + BlockDirection::Up => location.up().to_f64(), + BlockDirection::Down => location.down().to_f64(), + BlockDirection::North => location.up().to_f64().add_raw(0.0, -0.4, -1.0), + BlockDirection::South => location.up().to_f64().add_raw(0.0, -0.4, 1.0), + BlockDirection::West => location.up().to_f64().add_raw(-1.0, -0.4, 0.0), + BlockDirection::East => location.up().to_f64().add_raw(1.0, -0.4, 0.0), + }; + let entity = Entity::new(world.clone(), location, &EntityType::ITEM); + // TODO: Merge stacks together + let item_entity = Arc::new(ItemEntity::new( + entity, + ItemStack::new(1, &Item::HANGING_ROOTS), + )); + world.spawn_entity(item_entity); + } + + if changed && player.gamemode.load() != GameMode::Creative { + // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. + let _ = item.damage_item(1); + } + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/honeycomb.rs b/crates/pumpkin/src/item/items/honeycomb.rs index 4c35ca140..91464f293 100644 --- a/crates/pumpkin/src/item/items/honeycomb.rs +++ b/crates/pumpkin/src/item/items/honeycomb.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -31,20 +30,18 @@ impl ItemMetadata for HoneyCombItem { } impl ItemBehaviour for HoneyCombItem { - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - try_wax_block(&world, location, block).await; - }) + block: &Block, + _server: &Server, + ) { + let world = player.world(); + try_wax_block(&world, location, block); } fn as_any(&self) -> &dyn std::any::Any { @@ -54,7 +51,7 @@ impl ItemBehaviour for HoneyCombItem { /// Waxes the block at `location` if it has a waxed equivalent, emitting the wax /// particles and sound on success. -pub(crate) async fn try_wax_block(world: &Arc, location: BlockPos, block: &Block) -> bool { +pub(crate) fn try_wax_block(world: &Arc, location: BlockPos, block: &Block) -> bool { let Some(replacement) = get_waxed_equivalent(block.id) else { return false; }; diff --git a/crates/pumpkin/src/item/items/ignite/fire_charge.rs b/crates/pumpkin/src/item/items/ignite/fire_charge.rs index bdfadc41a..305d4a11b 100644 --- a/crates/pumpkin/src/item/items/ignite/fire_charge.rs +++ b/crates/pumpkin/src/item/items/ignite/fire_charge.rs @@ -1,6 +1,3 @@ -use std::pin::Pin; -use std::sync::Arc; - use pumpkin_data::BlockDirection; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; @@ -10,6 +7,7 @@ use pumpkin_data::{Block, BlockStateId}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; +use std::sync::Arc; use crate::entity::player::Player; use crate::item::items::ignite::ignition::Ignition; @@ -26,31 +24,28 @@ impl ItemMetadata for FireChargeItem { } impl ItemBehaviour for FireChargeItem { - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - Ignition::ignite_block( - |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { - world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); + block: &Block, + _server: &Server, + ) { + let world = player.world(); + Ignition::ignite_block( + |world: Arc, pos: BlockPos, new_state_id: BlockStateId| { + world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); - world.play_block_sound(Sound::ItemFirechargeUse, SoundCategory::Blocks, pos); - }, - &world, - location, - location.offset(face.to_offset()), - block, - ) - .await; - }) + world.play_block_sound(Sound::ItemFirechargeUse, SoundCategory::Blocks, pos); + }, + &world, + location, + location.offset(face.to_offset()), + block, + ); } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs index 48766c492..0a981dcec 100644 --- a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs +++ b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs @@ -10,7 +10,6 @@ use pumpkin_data::{Block, BlockStateId}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; -use std::pin::Pin; use std::sync::Arc; use crate::item::items::ignite::ignition::Ignition; @@ -26,57 +25,51 @@ impl ItemMetadata for FlintAndSteelItem { } impl ItemBehaviour for FlintAndSteelItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let server_ref = world.server.upgrade(); - if let Some(server_ref) = server_ref { - let player_arc = world.get_player_by_uuid(player.gameprofile.id); - let mut event = - crate::plugin::api::events::block::block_ignite::BlockIgniteEvent::new( - location, - &pumpkin_data::Block::FIRE, - player_arc, - ); - server_ref - .plugin_manager - .fire(&server_ref, &mut event) - .await; - let mut portal_event = PortalCreateEvent::new(location, PortalType::Nether); - server_ref - .plugin_manager - .fire(&server_ref, &mut portal_event) - .await; - if event.cancelled || portal_event.cancelled { - return; - } - } - - let ignited = Ignition::ignite_block( - |world: Arc, pos: BlockPos, new_state_id: BlockStateId| async move { - world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); - }, - &world, + block: &Block, + _server: &Server, + ) { + let world = player.world(); + let server_ref = world.server.upgrade(); + if let Some(server_ref) = server_ref { + let player_arc = world.get_player_by_uuid(player.gameprofile.id); + let mut event = crate::plugin::api::events::block::block_ignite::BlockIgniteEvent::new( location, - location.offset(face.to_offset()), - block, - ) - .await; - - if ignited && player.gamemode.load() != pumpkin_util::GameMode::Creative { - // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. - let _ = item.damage_item(1); + &pumpkin_data::Block::FIRE, + player_arc, + ); + server_ref + .plugin_manager + .fire_blocking(&server_ref, &mut event); + let mut portal_event = PortalCreateEvent::new(location, PortalType::Nether); + server_ref + .plugin_manager + .fire_blocking(&server_ref, &mut portal_event); + if event.cancelled || portal_event.cancelled { + return; } - }) + } + + let ignited = Ignition::ignite_block( + |world: Arc, pos: BlockPos, new_state_id: BlockStateId| { + world.set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL); + }, + &world, + location, + location.offset(face.to_offset()), + block, + ); + + if ignited && player.gamemode.load() != pumpkin_util::GameMode::Creative { + // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. + let _ = item.damage_item(1); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/ignite/ignition.rs b/crates/pumpkin/src/item/items/ignite/ignition.rs index 550fbee94..5ec16747c 100644 --- a/crates/pumpkin/src/item/items/ignite/ignition.rs +++ b/crates/pumpkin/src/item/items/ignite/ignition.rs @@ -12,7 +12,7 @@ pub struct Ignition; impl Ignition { /// Lights `block` at `location` itself if it can be lit (campfires, candles, candle /// cakes), otherwise places a fire block at `fire_pos`. - pub async fn ignite_block( + pub fn ignite_block( ignite_logic: F, world: &Arc, location: BlockPos, @@ -20,8 +20,7 @@ impl Ignition { block: &Block, ) -> bool where - F: FnOnce(Arc, BlockPos, BlockStateId) -> Fut, - Fut: Future, + F: FnOnce(Arc, BlockPos, BlockStateId), { if world.get_fluid(&location).name != Fluid::EMPTY.name { return false; @@ -31,13 +30,13 @@ impl Ignition { let state_id = world.get_block_state_id(&location); if let Some(new_state_id) = can_be_lit(block, state_id) { - ignite_logic(world.clone(), location, new_state_id).await; + ignite_logic(world.clone(), location, new_state_id); return true; } let state_id = FireBlock.get_state_for_position(world, &fire_block, &fire_pos); if FireBlockBase::can_place_at(world, &fire_pos) { - ignite_logic(world.clone(), fire_pos, state_id).await; + ignite_logic(world.clone(), fire_pos, state_id); return true; } diff --git a/crates/pumpkin/src/item/items/lead.rs b/crates/pumpkin/src/item/items/lead.rs index d0907ce36..ac35163f7 100644 --- a/crates/pumpkin/src/item/items/lead.rs +++ b/crates/pumpkin/src/item/items/lead.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; @@ -25,73 +24,71 @@ impl ItemMetadata for LeadItem { } impl ItemBehaviour for LeadItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_FENCES) { - return; - } + block: &Block, + _server: &Server, + ) { + if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_FENCES) { + return; + } - let world = player.world(); - let center = Vector3::new( - f64::from(location.0.x) + 0.5, - f64::from(location.0.y) + 0.5, - f64::from(location.0.z) + 0.5, - ); + let world = player.world(); + let center = Vector3::new( + f64::from(location.0.x) + 0.5, + f64::from(location.0.y) + 0.5, + f64::from(location.0.z) + 0.5, + ); - let search_dim = EntityDimensions { - width: 32.0, - height: 32.0, - eye_height: 16.0, - }; + let search_dim = EntityDimensions { + width: 32.0, + height: 32.0, + eye_height: 16.0, + }; - let search_box = BoundingBox::new_from_pos(center.x, center.y, center.z, &search_dim); + let search_box = BoundingBox::new_from_pos(center.x, center.y, center.z, &search_dim); - let player_id = player.entity_id(); - let entities = world.get_entities_at_box(&search_box); + let player_id = player.entity_id(); + let entities = world.get_entities_at_box(&search_box); - let mut any_leashed = false; - let mut knot: Option> = None; + let mut any_leashed = false; + let mut knot: Option> = None; - for entity_base in entities { - let ent = entity_base.get_entity(); - let is_leashed_to_player = ent - .leashed_to - .try_lock() - .ok() - .and_then(|guard| { - guard - .as_ref() - .map(|holder| holder.get_entity().entity_id == player_id) - }) - .unwrap_or(false); + for entity_base in entities { + let ent = entity_base.get_entity(); + let is_leashed_to_player = ent + .leashed_to + .try_lock() + .ok() + .and_then(|guard| { + guard + .as_ref() + .map(|holder| holder.get_entity().entity_id == player_id) + }) + .unwrap_or(false); - if is_leashed_to_player { - if knot.is_none() { - knot = Some(LeashKnotEntity::get_or_create(&world, location)); - } - if let Some(k) = &knot { - ent.leash_to(k.clone() as Arc); - any_leashed = true; - } + if is_leashed_to_player { + if knot.is_none() { + knot = Some(LeashKnotEntity::get_or_create(&world, location)); + } + if let Some(k) = &knot { + ent.leash_to(k.clone() as Arc); + any_leashed = true; } } + } - if any_leashed { - if player.gamemode.load() != pumpkin_util::GameMode::Creative { - item.decrement(1); - } - world.play_sound(Sound::ItemLeadTied, SoundCategory::Neutral, ¢er); + if any_leashed { + if player.gamemode.load() != pumpkin_util::GameMode::Creative { + item.decrement(1); } - }) + world.play_sound(Sound::ItemLeadTied, SoundCategory::Neutral, ¢er); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/map.rs b/crates/pumpkin/src/item/items/map.rs index c565db7bc..3e3cadd9e 100644 --- a/crates/pumpkin/src/item/items/map.rs +++ b/crates/pumpkin/src/item/items/map.rs @@ -8,8 +8,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_util::GameMode; use std::any::Any; -use std::future::Future; -use std::pin::Pin; pub struct MapItem; @@ -20,56 +18,56 @@ impl ItemMetadata for MapItem { } impl ItemBehaviour for MapItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let Some(server) = player.world().server.upgrade() else { - return; + fn normal_use(&self, _item: &Item, player: &Player) { + let Some(server) = player.world().server.upgrade() else { + return; + }; + + let inventory = player.inventory(); + let held_stack = inventory.held_item(); + let (found, mut hand_stack, hand) = + if !held_stack.is_empty() && held_stack.item.id == Item::MAP.id { + (true, held_stack, pumpkin_util::Hand::Right) + } else { + let off_hand = inventory.off_hand_item(); + if !off_hand.is_empty() && off_hand.item.id == Item::MAP.id { + (true, off_hand, pumpkin_util::Hand::Left) + } else { + (false, held_stack, pumpkin_util::Hand::Right) + } }; - let inventory = player.inventory(); - let held_stack = inventory.held_item(); - let (found, mut hand_stack, hand) = - if !held_stack.is_empty() && held_stack.item.id == Item::MAP.id { - (true, held_stack, pumpkin_util::Hand::Right) - } else { - let off_hand = inventory.off_hand_item(); - if !off_hand.is_empty() && off_hand.item.id == Item::MAP.id { - (true, off_hand, pumpkin_util::Hand::Left) - } else { - (false, held_stack, pumpkin_util::Hand::Right) - } - }; + if found { + let map_id = server.next_map_id(); + let _ = server.map_manager.create_map( + map_id, + player.world().dimension.clone(), + player.position().x as i32, + player.position().z as i32, + 0, // Default scale + ); - if found { - let map_id = server.next_map_id(); - let _ = server.map_manager.create_map( - map_id, - player.world().dimension.clone(), - player.position().x as i32, - player.position().z as i32, - 0, // Default scale - ); + let mut filled_map = ItemStack::new(1, &Item::FILLED_MAP); + filled_map.patch.push(( + DataComponent::MapId, + Some(MapIdImpl { id: map_id }.to_dyn()), + )); - let mut filled_map = ItemStack::new(1, &Item::FILLED_MAP); - filled_map.patch.push(( - DataComponent::MapId, - Some(MapIdImpl { id: map_id }.to_dyn()), - )); - - let gamemode = player.gamemode.load(); - if hand_stack.item_count == 1 && gamemode != GameMode::Creative { - inventory.set_stack_in_hand(hand, filled_map); - } else { - hand_stack.decrement_unless_creative(gamemode, 1); - inventory.set_stack_in_hand(hand, hand_stack); - inventory.offer_or_drop_stack(filled_map, player).await; + let gamemode = player.gamemode.load(); + if hand_stack.item_count == 1 && gamemode != GameMode::Creative { + inventory.set_stack_in_hand(hand, filled_map); + } else { + hand_stack.decrement_unless_creative(gamemode, 1); + inventory.set_stack_in_hand(hand, hand_stack); + let mut stack_to_give = filled_map; + let was_added = inventory.insert_stack_anywhere(&mut stack_to_give); + if !was_added && !stack_to_give.is_empty() { + player + .world() + .drop_stack(&player.position().to_block_pos(), stack_to_give); } } - }) + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/minecart.rs b/crates/pumpkin/src/item/items/minecart.rs index 678e99ae7..3123dfa74 100644 --- a/crates/pumpkin/src/item/items/minecart.rs +++ b/crates/pumpkin/src/item/items/minecart.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -52,44 +51,42 @@ impl ItemMetadata for MinecartItem { } impl ItemBehaviour for MinecartItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); + block: &Block, + _server: &Server, + ) { + let world = player.world(); - if !block.has_tag(&tag::Block::MINECRAFT_RAILS) { - return; - } - let state_id = world.get_block_state_id(&location); - let is_ascending = if PoweredRailLikeProperties::handles_block_id(block.id) { - PoweredRailLikeProperties::from_state_id(state_id, block) - .shape - .is_ascending() - } else { - RailLikeProperties::from_state_id(state_id, block) - .shape - .is_ascending() - }; - let height = if is_ascending { 0.5 } else { 0.0 }; - let entity_type = Self::item_to_entity(item.item); - let pos = location.to_f64(); - let entity = Entity::new( - world.clone(), - Vector3::new(pos.x, pos.y + 0.0625 + height, pos.z), - entity_type, - ); - let minecart_entity = Arc::new(MinecartEntity::new(entity)); - world.spawn_entity(minecart_entity); - item.decrement_unless_creative(player.gamemode.load(), 1); - }) + if !block.has_tag(&tag::Block::MINECRAFT_RAILS) { + return; + } + let state_id = world.get_block_state_id(&location); + let is_ascending = if PoweredRailLikeProperties::handles_block_id(block.id) { + PoweredRailLikeProperties::from_state_id(state_id, block) + .shape + .is_ascending() + } else { + RailLikeProperties::from_state_id(state_id, block) + .shape + .is_ascending() + }; + let height = if is_ascending { 0.5 } else { 0.0 }; + let entity_type = Self::item_to_entity(item.item); + let pos = location.to_f64(); + let entity = Entity::new( + world.clone(), + Vector3::new(pos.x, pos.y + 0.0625 + height, pos.z), + entity_type, + ); + let minecart_entity = Arc::new(MinecartEntity::new(entity)); + world.spawn_entity(minecart_entity); + item.decrement_unless_creative(player.gamemode.load(), 1); } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/name_tag.rs b/crates/pumpkin/src/item/items/name_tag.rs index 6610b1425..58a51e668 100644 --- a/crates/pumpkin/src/item/items/name_tag.rs +++ b/crates/pumpkin/src/item/items/name_tag.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; @@ -17,22 +16,15 @@ impl ItemMetadata for NameTagItem { } impl ItemBehaviour for NameTagItem { - fn use_on_entity<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, - entity: Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let entity = entity.get_entity(); - if entity.entity_type.saveable - && let Some(name) = item.get_data_component::() - { - // TODO - entity.set_custom_name(name.name.clone()); - item.decrement_unless_creative(player.gamemode.load(), 1); - } - }) + fn use_on_entity(&self, item: &mut ItemStack, player: &Player, entity: Arc) { + let entity = entity.get_entity(); + if entity.entity_type.saveable + && let Some(name) = item.get_data_component::() + { + // TODO + entity.set_custom_name(name.name.clone()); + item.decrement_unless_creative(player.gamemode.load(), 1); + } } fn as_any(&self) -> &dyn std::any::Any { self diff --git a/crates/pumpkin/src/item/items/on_a_stick.rs b/crates/pumpkin/src/item/items/on_a_stick.rs index c5842cde0..24eb7d121 100644 --- a/crates/pumpkin/src/item/items/on_a_stick.rs +++ b/crates/pumpkin/src/item/items/on_a_stick.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -17,20 +15,19 @@ impl ItemMetadata for CarrotOnAStickItem { } impl ItemBehaviour for CarrotOnAStickItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let vehicle_opt = player.get_entity().vehicle.lock().await.clone(); - if let Some(vehicle) = vehicle_opt - && let Some(steerable) = vehicle.get_item_steerable() - && steerable.boost() - { - player.damage_held_item(7).await; - } - }) + fn normal_use(&self, _item: &Item, player: &Player) { + let vehicle_opt = player + .get_entity() + .vehicle + .try_lock() + .ok() + .and_then(|guard| guard.clone()); + if let Some(vehicle) = vehicle_opt + && let Some(steerable) = vehicle.get_item_steerable() + && steerable.boost() + { + player.damage_held_item(7); + } } fn as_any(&self) -> &dyn Any { @@ -45,20 +42,19 @@ impl ItemMetadata for WarpedFungusOnAStickItem { } impl ItemBehaviour for WarpedFungusOnAStickItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let vehicle_opt = player.get_entity().vehicle.lock().await.clone(); - if let Some(vehicle) = vehicle_opt - && let Some(steerable) = vehicle.get_item_steerable() - && steerable.boost() - { - player.damage_held_item(7).await; - } - }) + fn normal_use(&self, _item: &Item, player: &Player) { + let vehicle_opt = player + .get_entity() + .vehicle + .try_lock() + .ok() + .and_then(|guard| guard.clone()); + if let Some(vehicle) = vehicle_opt + && let Some(steerable) = vehicle.get_item_steerable() + && steerable.boost() + { + player.damage_held_item(7); + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/potions.rs b/crates/pumpkin/src/item/items/potions.rs index 44105e1dc..e07a32484 100644 --- a/crates/pumpkin/src/item/items/potions.rs +++ b/crates/pumpkin/src/item/items/potions.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -38,13 +37,8 @@ impl ItemMetadata for LingeringPotionItem { const POWER: f32 = 0.5; impl ItemBehaviour for PotionItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - _player: &'a Player, - ) -> Pin + Send + 'a>> { + fn normal_use(&self, _item: &Item, _player: &Player) { // Drinking is handled by the consumable flow in the server (active hand + consumption tick). - Box::pin(async move {}) } fn as_any(&self) -> &dyn std::any::Any { @@ -53,59 +47,52 @@ impl ItemBehaviour for PotionItem { } impl ItemBehaviour for SplashPotionItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let position = player.position(); - let world = player.world(); - world.play_sound( - Sound::EntityWitchThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ); - let entity = Entity::new(world.clone(), position, &EntityType::SPLASH_POTION); - let splash = SplashPotionEntity::new_shot(entity, player.get_entity()); + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + world.play_sound( + Sound::EntityWitchThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ); + let entity = Entity::new(world.clone(), position, &EntityType::SPLASH_POTION); + let splash = SplashPotionEntity::new_shot(entity, player.get_entity()); - // Copy the held item stack data into the projectile - let main_s = player.inventory.held_item(); - let mut used_main = true; - let mut stack = (!main_s.is_empty() - && main_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id) - .then_some(main_s); - if stack.is_none() { - let off_s = player.inventory.off_hand_item(); - if !off_s.is_empty() && off_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id - { - stack = Some(off_s); - used_main = false; - } + // Copy the held item stack data into the projectile + let main_s = player.inventory.held_item(); + let mut used_main = true; + let mut stack = (!main_s.is_empty() + && main_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id) + .then_some(main_s); + if stack.is_none() { + let off_s = player.inventory.off_hand_item(); + if !off_s.is_empty() && off_s.item.id == pumpkin_data::item::Item::SPLASH_POTION.id { + stack = Some(off_s); + used_main = false; } - let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); - splash.set_item_stack(stack); + } + let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); + splash.set_item_stack(stack); - let (yaw, pitch) = player.rotation(); - splash - .thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + let (yaw, pitch) = player.rotation(); + splash + .thrown + .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(splash)); + world.spawn_entity(Arc::new(splash)); - // Decrement the used stack (clear) - if used_main { - let mut s = player.inventory.held_item(); - s.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(s); - } else { - let mut s = player.inventory.off_hand_item(); - s.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, s); - } - }) + // Decrement the used stack (clear) + if used_main { + let mut s = player.inventory.held_item(); + s.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(s); + } else { + let mut s = player.inventory.off_hand_item(); + s.decrement_unless_creative(player.gamemode.load(), 1); + player + .inventory + .set_stack_in_hand(pumpkin_util::Hand::Left, s); + } } fn as_any(&self) -> &dyn std::any::Any { @@ -114,59 +101,51 @@ impl ItemBehaviour for SplashPotionItem { } impl ItemBehaviour for LingeringPotionItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let position = player.position(); - let world = player.world(); - world.play_sound( - Sound::EntityWitchThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ); - let entity = Entity::new(world.clone(), position, &EntityType::LINGERING_POTION); - let ling = LingeringPotionEntity::new_shot(entity, player.get_entity()); + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + world.play_sound( + Sound::EntityWitchThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ); + let entity = Entity::new(world.clone(), position, &EntityType::LINGERING_POTION); + let ling = LingeringPotionEntity::new_shot(entity, player.get_entity()); - // Copy the held item stack data into the projectile - let main_s = player.inventory.held_item(); - let mut used_main = true; - let mut stack = (!main_s.is_empty() - && main_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id) - .then_some(main_s); - if stack.is_none() { - let off_s = player.inventory.off_hand_item(); - if !off_s.is_empty() - && off_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id - { - stack = Some(off_s); - used_main = false; - } + // Copy the held item stack data into the projectile + let main_s = player.inventory.held_item(); + let mut used_main = true; + let mut stack = (!main_s.is_empty() + && main_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id) + .then_some(main_s); + if stack.is_none() { + let off_s = player.inventory.off_hand_item(); + if !off_s.is_empty() && off_s.item.id == pumpkin_data::item::Item::LINGERING_POTION.id { + stack = Some(off_s); + used_main = false; } - let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); - ling.set_item_stack(stack); + } + let stack = stack.unwrap_or_else(|| ItemStack::EMPTY.clone()); + ling.set_item_stack(stack); - let (yaw, pitch) = player.rotation(); - ling.thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + let (yaw, pitch) = player.rotation(); + ling.thrown + .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(ling)); + world.spawn_entity(Arc::new(ling)); - // Decrement the used stack (clear) - if used_main { - let mut s = player.inventory.held_item(); - s.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(s); - } else { - let mut s = player.inventory.off_hand_item(); - s.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, s); - } - }) + // Decrement the used stack (clear) + if used_main { + let mut s = player.inventory.held_item(); + s.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(s); + } else { + let mut s = player.inventory.off_hand_item(); + s.decrement_unless_creative(player.gamemode.load(), 1); + player + .inventory + .set_stack_in_hand(pumpkin_util::Hand::Left, s); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/saddle.rs b/crates/pumpkin/src/item/items/saddle.rs index 063d0e7be..f5bcc4357 100644 --- a/crates/pumpkin/src/item/items/saddle.rs +++ b/crates/pumpkin/src/item/items/saddle.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; @@ -19,30 +17,23 @@ impl ItemMetadata for SaddleItem { } impl ItemBehaviour for SaddleItem { - fn use_on_entity<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, - entity: Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(mob) = entity.get_mob() - && mob.can_be_saddled() - && !mob.is_saddled() - { - mob.set_saddled(true); - let ent = entity.get_entity(); - let sound = if ent.entity_type == &pumpkin_data::entity::EntityType::STRIDER { - Sound::EntityStriderSaddle - } else { - Sound::EntityPigSaddle - }; - player - .world() - .play_sound(sound, SoundCategory::Neutral, &ent.pos.load()); - item.decrement_unless_creative(player.gamemode.load(), 1); - } - }) + fn use_on_entity(&self, item: &mut ItemStack, player: &Player, entity: Arc) { + if let Some(mob) = entity.get_mob() + && mob.can_be_saddled() + && !mob.is_saddled() + { + mob.set_saddled(true); + let ent = entity.get_entity(); + let sound = if ent.entity_type == &pumpkin_data::entity::EntityType::STRIDER { + Sound::EntityStriderSaddle + } else { + Sound::EntityPigSaddle + }; + player + .world() + .play_sound(sound, SoundCategory::Neutral, &ent.pos.load()); + item.decrement_unless_creative(player.gamemode.load(), 1); + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/items/shears.rs b/crates/pumpkin/src/item/items/shears.rs index a5773b153..3b0009638 100644 --- a/crates/pumpkin/src/item/items/shears.rs +++ b/crates/pumpkin/src/item/items/shears.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -53,57 +51,48 @@ impl ItemBehaviour for ShearsItem { player.gamemode.load() != GameMode::Creative } - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + player: &Player, location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let state_id = world.get_block_state_id(&location); + block: &Block, + _server: &Server, + ) { + let world = player.world(); + let state_id = world.get_block_state_id(&location); - if handle_growing_plant(player, &location, block, state_id).await { - return; - } + if handle_growing_plant(player, &location, block, state_id) { + return; + } - if handle_beehive(player, &location, block, state_id).await { - return; - } + if handle_beehive(player, &location, block, state_id) { + return; + } - handle_pumpkin(player, &location, block).await; - }) + handle_pumpkin(player, &location, block); } - fn use_on_entity<'a>( - &'a self, - _item: &'a mut ItemStack, - player: &'a Player, - entity: Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(sheep) = entity.get_mob().and_then(|m| m.get_sheep()) - && !sheep.is_sheared() - { - sheep.set_sheared(true); - let world = player.world(); - let pos = sheep.mob_entity.living_entity.entity.pos.load(); - world.play_sound(Sound::EntitySheepShear, SoundCategory::Players, &pos); + fn use_on_entity(&self, _item: &mut ItemStack, player: &Player, entity: Arc) { + if let Some(sheep) = entity.get_mob().and_then(|m| m.get_sheep()) + && !sheep.is_sheared() + { + sheep.set_sheared(true); + let world = player.world(); + let pos = sheep.mob_entity.living_entity.entity.pos.load(); + world.play_sound(Sound::EntitySheepShear, SoundCategory::Players, &pos); - let wool_count = (rand::random::() % 3 + 1) as u8; - let wool_item = get_wool_item_for_color(sheep.get_color()); - let item_entity = Arc::new(ItemEntity::new( - Entity::new(world.clone(), pos, &EntityType::ITEM), - ItemStack::new(wool_count, wool_item), - )); - world.spawn_entity(item_entity); - player.damage_held_item(1).await; - } - }) + let wool_count = (rand::random::() % 3 + 1) as u8; + let wool_item = get_wool_item_for_color(sheep.get_color()); + let item_entity = Arc::new(ItemEntity::new( + Entity::new(world.clone(), pos, &EntityType::ITEM), + ItemStack::new(wool_count, wool_item), + )); + world.spawn_entity(item_entity); + player.damage_held_item(1); + } } fn as_any(&self) -> &dyn Any { @@ -111,7 +100,7 @@ impl ItemBehaviour for ShearsItem { } } -async fn handle_growing_plant( +fn handle_growing_plant( player: &Player, location: &BlockPos, block: &Block, @@ -151,14 +140,14 @@ async fn handle_growing_plant( SoundCategory::Blocks, &location.to_f64(), ); - player.damage_held_item(1).await; + player.damage_held_item(1); return true; } false } -async fn handle_beehive( +fn handle_beehive( player: &Player, location: &BlockPos, block: &Block, @@ -207,14 +196,14 @@ async fn handle_beehive( ItemStack::new(3, &Item::HONEYCOMB), )); world.spawn_entity(item_entity); - player.damage_held_item(1).await; + player.damage_held_item(1); return true; } false } -async fn handle_pumpkin(player: &Player, location: &BlockPos, block: &Block) { +fn handle_pumpkin(player: &Player, location: &BlockPos, block: &Block) { if block.id == Block::PUMPKIN.id { let world = player.world(); let carved_state = Block::CARVED_PUMPKIN.default_state.id; @@ -235,6 +224,6 @@ async fn handle_pumpkin(player: &Player, location: &BlockPos, block: &Block) { ItemStack::new(4, &Item::PUMPKIN_SEEDS), )); world.spawn_entity(item_entity); - player.damage_held_item(1).await; + player.damage_held_item(1); } } diff --git a/crates/pumpkin/src/item/items/shovel.rs b/crates/pumpkin/src/item/items/shovel.rs index 452f40159..87c812892 100644 --- a/crates/pumpkin/src/item/items/shovel.rs +++ b/crates/pumpkin/src/item/items/shovel.rs @@ -1,5 +1,3 @@ -use std::pin::Pin; - use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; @@ -13,7 +11,6 @@ use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::BlockFlags; -use rand::{RngExt, rng}; pub struct ShovelItem; @@ -24,71 +21,63 @@ impl ItemMetadata for ShovelItem { } impl ItemBehaviour for ShovelItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - // Yes, Minecraft does hardcode these - let mut changed = if (block == &Block::GRASS_BLOCK - || block == &Block::DIRT - || block == &Block::COARSE_DIRT - || block == &Block::ROOTED_DIRT - || block == &Block::PODZOL - || block == &Block::MYCELIUM) - && face != BlockDirection::Down - && world.get_block_state(&location.up()).is_air() - { + block: &Block, + _server: &Server, + ) { + let world = player.world(); + // Yes, Minecraft does hardcode these + let mut changed = if (block == &Block::GRASS_BLOCK + || block == &Block::DIRT + || block == &Block::COARSE_DIRT + || block == &Block::ROOTED_DIRT + || block == &Block::PODZOL + || block == &Block::MYCELIUM) + && face != BlockDirection::Down + && world.get_block_state(&location.up()).is_air() + { + world.set_block_state( + &location, + Block::DIRT_PATH.default_state.id, + BlockFlags::NOTIFY_ALL, + ); + true + } else { + false + }; + if block == &Block::CAMPFIRE || block == &Block::SOUL_CAMPFIRE { + let mut campfire_props = + CampfireLikeProperties::from_state_id(world.get_block_state(&location).id, block); + if campfire_props.lit { + world.sync_world_event(WorldEvent::SoundExtinguishFire, location, 0); + + campfire_props.lit = false; world.set_block_state( &location, - Block::DIRT_PATH.default_state.id, + campfire_props.to_state_id(block), BlockFlags::NOTIFY_ALL, ); - true - } else { - false - }; - if block == &Block::CAMPFIRE || block == &Block::SOUL_CAMPFIRE { - let mut campfire_props = CampfireLikeProperties::from_state_id( - world.get_block_state(&location).id, - block, + world.play_sound_fine( + Sound::BlockFireExtinguish, + SoundCategory::Ambient, + &location.to_f64(), + 0.5, + 2.0, ); - if campfire_props.lit { - world.sync_world_event(WorldEvent::SoundExtinguishFire, location, 0); - - campfire_props.lit = false; - world.set_block_state( - &location, - campfire_props.to_state_id(block), - BlockFlags::NOTIFY_ALL, - ); - let seed = rng().random::(); - player - .play_sound( - Sound::BlockFireExtinguish as u16, - SoundCategory::Ambient, - &location.to_f64(), - 0.5, - 2.0, - seed, - ) - .await; - changed = true; - } + changed = true; } + } - if changed && player.gamemode.load() != GameMode::Creative { - // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. - let _ = item.damage_item(1); - } - }) + if changed && player.gamemode.load() != GameMode::Creative { + // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. + let _ = item.damage_item(1); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/snowball.rs b/crates/pumpkin/src/item/items/snowball.rs index 43b8f49e3..3a8d2ae4e 100644 --- a/crates/pumpkin/src/item/items/snowball.rs +++ b/crates/pumpkin/src/item/items/snowball.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -21,47 +20,41 @@ impl ItemMetadata for SnowBallItem { const POWER: f32 = 1.5; impl ItemBehaviour for SnowBallItem { - fn normal_use<'a>( - &'a self, - _block: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let position = player.position(); - let world = player.world(); - world.play_sound( - Sound::EntitySnowballThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ); - let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); - let snowball = SnowballEntity::new_shot(entity, player.get_entity()); - let (yaw, pitch) = player.rotation(); - snowball - .thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(snowball)); + fn normal_use(&self, _block: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + world.play_sound( + Sound::EntitySnowballThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ); + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball + .thrown + .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(snowball)); - // Consume item - let mut main_hand = player.inventory.held_item(); - let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::SNOWBALL.id { - main_hand.decrement_unless_creative(player.gamemode.load(), 1); - player.inventory.set_held_item(main_hand); - true - } else { - false - }; + // Consume item + let mut main_hand = player.inventory.held_item(); + let consumed = if !main_hand.is_empty() && main_hand.item.id == Item::SNOWBALL.id { + main_hand.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(main_hand); + true + } else { + false + }; - if !consumed { - let mut off_hand = player.inventory.off_hand_item(); - if !off_hand.is_empty() && off_hand.item.id == Item::SNOWBALL.id { - off_hand.decrement_unless_creative(player.gamemode.load(), 1); - player - .inventory - .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); - } + if !consumed { + let mut off_hand = player.inventory.off_hand_item(); + if !off_hand.is_empty() && off_hand.item.id == Item::SNOWBALL.id { + off_hand.decrement_unless_creative(player.gamemode.load(), 1); + player + .inventory + .set_stack_in_hand(pumpkin_util::Hand::Left, off_hand); } - }) + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/spawn_egg.rs b/crates/pumpkin/src/item/items/spawn_egg.rs index 73b9166f5..116aa2045 100644 --- a/crates/pumpkin/src/item/items/spawn_egg.rs +++ b/crates/pumpkin/src/item/items/spawn_egg.rs @@ -1,5 +1,3 @@ -use std::pin::Pin; - use crate::block::entities::mob_spawner::MobSpawnerBlockEntity; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -62,51 +60,49 @@ pub(crate) fn apply_entity_variant(item: &ItemStack, mob: &dyn EntityBase) { } impl ItemBehaviour for SpawnEggItem { - fn use_on_block<'a>( - &'a self, - item: &'a mut ItemStack, - player: &'a Player, + fn use_on_block( + &self, + item: &mut ItemStack, + player: &Player, location: BlockPos, face: BlockDirection, _cursor_pos: Vector3, - _block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(entity_type) = entity_from_egg(item.item.id) { - let world = player.world(); + _block: &Block, + _server: &Server, + ) { + if let Some(entity_type) = entity_from_egg(item.item.id) { + let world = player.world(); - if let Some(block_entity) = player.world().get_block_entity(&location) - && let Some(spawner) = block_entity - .as_any() - .downcast_ref::() - { - spawner.set_entity_type(entity_type); - world.update_block_entity(&block_entity); - item.decrement_unless_creative(player.gamemode.load(), 1); - return; - } - let pos = BlockPos(location.0 + face.to_offset()); - let pos = Vector3::new( - f64::from(pos.0.x) + 0.5, - f64::from(pos.0.y), - f64::from(pos.0.z) + 0.5, - ); - // Create rotation like Vanilla - let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; - - let mob = from_type(entity_type, pos, &world, Uuid::new_v4()); - - // Set the rotation - mob.get_entity().set_rotation(yaw, 0.0); - - apply_entity_variant(item, mob.as_ref()); - - // Broadcast the new mob to all players - world.spawn_entity(mob); + if let Some(block_entity) = player.world().get_block_entity(&location) + && let Some(spawner) = block_entity + .as_any() + .downcast_ref::() + { + spawner.set_entity_type(entity_type); + world.update_block_entity(&block_entity); item.decrement_unless_creative(player.gamemode.load(), 1); + return; } - }) + let pos = BlockPos(location.0 + face.to_offset()); + let pos = Vector3::new( + f64::from(pos.0.x) + 0.5, + f64::from(pos.0.y), + f64::from(pos.0.z) + 0.5, + ); + // Create rotation like Vanilla + let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; + + let mob = from_type(entity_type, pos, &world, Uuid::new_v4()); + + // Set the rotation + mob.get_entity().set_rotation(yaw, 0.0); + + apply_entity_variant(item, mob.as_ref()); + + // Broadcast the new mob to all players + world.spawn_entity(mob); + item.decrement_unless_creative(player.gamemode.load(), 1); + } } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/spyglass.rs b/crates/pumpkin/src/item/items/spyglass.rs index 76da0a08e..12bd87664 100644 --- a/crates/pumpkin/src/item/items/spyglass.rs +++ b/crates/pumpkin/src/item/items/spyglass.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -16,38 +14,24 @@ impl ItemMetadata for SpyglassItem { } impl ItemBehaviour for SpyglassItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - player.world().play_sound( - Sound::ItemSpyglassUse, - SoundCategory::Players, - &player.position(), - ); - let stack = player.inventory().held_item(); - player.living_entity.set_active_hand( - pumpkin_util::Hand::Right, - stack, - Self::USE_DURATION, - ); - }) + fn normal_use(&self, _item: &Item, player: &Player) { + player.world().play_sound( + Sound::ItemSpyglassUse, + SoundCategory::Players, + &player.position(), + ); + let stack = player.inventory().held_item(); + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, Self::USE_DURATION); } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a pumpkin_data::item_stack::ItemStack, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - player.world().play_sound( - Sound::ItemSpyglassStopUsing, - SoundCategory::Players, - &player.position(), - ); - }) + fn on_stopped_using(&self, _stack: &pumpkin_data::item_stack::ItemStack, player: &Player) { + player.world().play_sound( + Sound::ItemSpyglassStopUsing, + SoundCategory::Players, + &player.position(), + ); } fn get_use_duration(&self) -> i32 { diff --git a/crates/pumpkin/src/item/items/trident.rs b/crates/pumpkin/src/item/items/trident.rs index fd243b3d7..ff9176f01 100644 --- a/crates/pumpkin/src/item/items/trident.rs +++ b/crates/pumpkin/src/item/items/trident.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use std::sync::Arc; use crate::entity::player::Player; @@ -14,7 +12,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_util::GameMode; use pumpkin_util::math::vector3::Vector3; -use pumpkin_world::inventory::Inventory; pub struct TridentItem; @@ -25,131 +22,111 @@ impl ItemMetadata for TridentItem { } impl ItemBehaviour for TridentItem { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let inventory = player.inventory(); - let stack = inventory.held_item(); + fn normal_use(&self, _item: &Item, player: &Player) { + let inventory = player.inventory(); + let stack = inventory.held_item(); - player - .living_entity - .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); - }) + player + .living_entity + .set_active_hand(pumpkin_util::Hand::Right, stack, 72000); } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a ItemStack, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let use_ticks = player - .living_entity - .item_use_time - .load(std::sync::atomic::Ordering::Relaxed); - let use_ticks = 72000 - use_ticks; + fn on_stopped_using(&self, _stack: &ItemStack, player: &Player) { + let use_ticks = player + .living_entity + .item_use_time + .load(std::sync::atomic::Ordering::Relaxed); + let use_ticks = 72000 - use_ticks; - if use_ticks < 10 { - return; - } + if use_ticks < 10 { + return; + } - let world = player.world(); - let stack_guard = player.inventory().held_item(); + let world = player.world(); + let stack_guard = player.inventory().held_item(); - // Check Riptide level - let mut riptide_level = 0u32; - if let Some(enchantments) = stack_guard - .get_data_component::( - ) { - for (enchantment, level) in enchantments.enchantment.iter() { - if **enchantment == pumpkin_data::Enchantment::RIPTIDE { - riptide_level = *level as u32; - } + // Check Riptide level + let mut riptide_level = 0u32; + if let Some(enchantments) = + stack_guard.get_data_component::() + { + for (enchantment, level) in enchantments.enchantment.iter() { + if **enchantment == pumpkin_data::Enchantment::RIPTIDE { + riptide_level = *level as u32; } } + } - if riptide_level > 0 { - let in_water = world.get_block_state(&player.position().to_block_pos()).id - == pumpkin_data::Block::WATER.default_state.id; - if !in_water { - player.living_entity.clear_active_hand(); - return; - } - - let f = f64::from(riptide_level); - let (yaw, pitch) = player.rotation(); - let f_yaw = f32::to_radians(yaw); - let f_pitch = f32::to_radians(pitch); - - let vx = f64::from(-f32::sin(f_yaw) * f32::cos(f_pitch)); - let vy = f64::from(-f32::sin(f_pitch)); - let vz = f64::from(f32::cos(f_yaw) * f32::cos(f_pitch)); - - let sq = (vx * vx + vy * vy + vz * vz).sqrt(); - if sq > 0.0 { - let mult = (1.0 + f * 0.75) / sq; - player.living_entity.entity.velocity.store(Vector3::new( - vx * mult, - vy * mult, - vz * mult, - )); - } - - player.damage_held_item(1).await; + if riptide_level > 0 { + let in_water = world.get_block_state(&player.position().to_block_pos()).id + == pumpkin_data::Block::WATER.default_state.id; + if !in_water { player.living_entity.clear_active_hand(); return; } - // Normal throw - spawn thrown trident + let f = f64::from(riptide_level); let (yaw, pitch) = player.rotation(); - let entity = Entity::new(world.clone(), player.position(), &EntityType::TRIDENT); - let trident = TridentEntity::new_shot( - entity, - player.get_entity(), - stack_guard.clone(), - ArrowPickup::Allowed, - ); - trident.set_velocity_from_rotation(pitch, yaw, 0.0, 2.5, 1.0); - world.spawn_entity(Arc::new(trident)); + let f_yaw = f32::to_radians(yaw); + let f_pitch = f32::to_radians(pitch); - world.play_sound( - Sound::ItemTridentThrow, - pumpkin_data::sound::SoundCategory::Players, - &player.position(), - ); + let vx = f64::from(-f32::sin(f_yaw) * f32::cos(f_pitch)); + let vy = f64::from(-f32::sin(f_pitch)); + let vz = f64::from(f32::cos(f_yaw) * f32::cos(f_pitch)); - if player.gamemode.load() != GameMode::Creative { - let inventory = player.inventory(); - let selected_slot = inventory.get_selected_slot() as usize; - - let main_hand_item = inventory.get_stack(selected_slot).await; - if main_hand_item.item.id == Item::TRIDENT.id { - inventory - .set_stack(selected_slot, ItemStack::EMPTY.clone()) - .await; - player - .sync_hand_slot(selected_slot, ItemStack::EMPTY.clone()) - .await; - } else { - let off_hand_slot = - pumpkin_inventory::player::player_inventory::PlayerInventory::OFF_HAND_SLOT; - let off_hand_item = inventory.get_stack(off_hand_slot).await; - if off_hand_item.item.id == Item::TRIDENT.id { - inventory - .set_stack(off_hand_slot, ItemStack::EMPTY.clone()) - .await; - player - .sync_hand_slot(off_hand_slot, ItemStack::EMPTY.clone()) - .await; - } - } + let sq = (vx * vx + vy * vy + vz * vz).sqrt(); + if sq > 0.0 { + let mult = (1.0 + f * 0.75) / sq; + player.living_entity.entity.velocity.store(Vector3::new( + vx * mult, + vy * mult, + vz * mult, + )); } + player.damage_held_item(1); player.living_entity.clear_active_hand(); - }) + return; + } + + // Normal throw - spawn thrown trident + let (yaw, pitch) = player.rotation(); + let entity = Entity::new(world.clone(), player.position(), &EntityType::TRIDENT); + let trident = TridentEntity::new_shot( + entity, + player.get_entity(), + stack_guard.clone(), + ArrowPickup::Allowed, + ); + trident.set_velocity_from_rotation(pitch, yaw, 0.0, 2.5, 1.0); + world.spawn_entity(Arc::new(trident)); + + world.play_sound( + Sound::ItemTridentThrow, + pumpkin_data::sound::SoundCategory::Players, + &player.position(), + ); + + if player.gamemode.load() != GameMode::Creative { + let inventory = player.inventory(); + let selected_slot = inventory.get_selected_slot() as usize; + + let main_hand_item = inventory.get_slot(selected_slot); + if main_hand_item.item.id == Item::TRIDENT.id { + inventory.set_slot(selected_slot, ItemStack::EMPTY.clone()); + player.sync_hand_slot(selected_slot, ItemStack::EMPTY.clone()); + } else { + let off_hand_slot = + pumpkin_inventory::player::player_inventory::PlayerInventory::OFF_HAND_SLOT; + let off_hand_item = inventory.get_slot(off_hand_slot); + if off_hand_item.item.id == Item::TRIDENT.id { + inventory.set_slot(off_hand_slot, ItemStack::EMPTY.clone()); + player.sync_hand_slot(off_hand_slot, ItemStack::EMPTY.clone()); + } + } + } + + player.living_entity.clear_active_hand(); } fn can_mine(&self, player: &Player) -> bool { diff --git a/crates/pumpkin/src/item/items/wind_charge.rs b/crates/pumpkin/src/item/items/wind_charge.rs index 56dd6ca0a..96b54b2f5 100644 --- a/crates/pumpkin/src/item/items/wind_charge.rs +++ b/crates/pumpkin/src/item/items/wind_charge.rs @@ -1,4 +1,3 @@ -use std::pin::Pin; use std::sync::Arc; use crate::entity::player::Player; @@ -23,33 +22,26 @@ impl ItemMetadata for WindChargeItem { const POWER: f32 = 1.5; impl ItemBehaviour for WindChargeItem { - fn normal_use<'a>( - &'a self, - _block: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let world = player.world(); - let position = player.position(); + fn normal_use(&self, _block: &Item, player: &Player) { + let world = player.world(); + let position = player.position(); - world.play_sound( - Sound::EntityWindChargeThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ); + world.play_sound( + Sound::EntityWindChargeThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ); - let entity = Entity::new(world.clone(), position, &EntityType::WIND_CHARGE); + let entity = Entity::new(world.clone(), position, &EntityType::WIND_CHARGE); - let wind_charge = - ThrownItemEntity::new(entity, player.get_entity(), WIND_CHARGE_GRAVITY); - let (yaw, pitch) = player.rotation(); + let wind_charge = ThrownItemEntity::new(entity, player.get_entity(), WIND_CHARGE_GRAVITY); + let (yaw, pitch) = player.rotation(); - wind_charge.set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); - // TODO: player.incrementStat(Stats.USED) + wind_charge.set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + // TODO: player.incrementStat(Stats.USED) - // TODO: Implement that the projectile will explode on impact - world.spawn_entity(Arc::new(WindChargeEntity::new_normal(wind_charge))); - }) + // TODO: Implement that the projectile will explode on impact + world.spawn_entity(Arc::new(WindChargeEntity::new_normal(wind_charge))); } fn as_any(&self) -> &dyn std::any::Any { diff --git a/crates/pumpkin/src/item/items/writable_book.rs b/crates/pumpkin/src/item/items/writable_book.rs index 00717dd5f..756f37e18 100644 --- a/crates/pumpkin/src/item/items/writable_book.rs +++ b/crates/pumpkin/src/item/items/writable_book.rs @@ -1,6 +1,4 @@ use std::any::Any; -use std::future::Future; -use std::pin::Pin; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -18,23 +16,15 @@ impl ItemMetadata for WritableBookItem { } impl ItemBehaviour for WritableBookItem { - fn normal_use<'a>( - &'a self, - item: &'a Item, - player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if item.id == Item::WRITTEN_BOOK.id { - player - .send_client_packet(&COpenBook::new(VarInt(0))) // 0 = main hand - .await; - player.world().play_sound( - Sound::ItemBookPageTurn, - SoundCategory::Players, - &player.position(), - ); - } - }) + fn normal_use(&self, item: &Item, player: &Player) { + if item.id == Item::WRITTEN_BOOK.id { + player.try_send_client_packet(&COpenBook::new(VarInt(0))); // 0 = main hand + player.world().play_sound( + Sound::ItemBookPageTurn, + SoundCategory::Players, + &player.position(), + ); + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/item/mod.rs b/crates/pumpkin/src/item/mod.rs index 70dbba23b..13603e4d7 100644 --- a/crates/pumpkin/src/item/mod.rs +++ b/crates/pumpkin/src/item/mod.rs @@ -3,7 +3,6 @@ pub mod potion; pub mod registry; use std::any::Any; -use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; @@ -21,44 +20,25 @@ pub trait ItemMetadata { } pub trait ItemBehaviour: Send + Sync { - fn normal_use<'a>( - &'a self, - _item: &'a Item, - _player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) - } + fn normal_use(&self, _item: &Item, _player: &Player) {} #[expect(clippy::too_many_arguments)] - fn use_on_block<'a>( - &'a self, - _item: &'a mut ItemStack, - _player: &'a Player, + fn use_on_block( + &self, + _item: &mut ItemStack, + _player: &Player, _location: BlockPos, _face: BlockDirection, _cursor_pos: Vector3, - _block: &'a Block, - _server: &'a Server, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) + _block: &Block, + _server: &Server, + ) { } - fn use_on_entity<'a>( - &'a self, - _item: &'a mut ItemStack, - _player: &'a Player, - _entity: Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) + fn use_on_entity(&self, _item: &mut ItemStack, _player: &Player, _entity: Arc) { } - fn on_stopped_using<'a>( - &'a self, - _stack: &'a ItemStack, - _player: &'a Player, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) - } + fn on_stopped_using(&self, _stack: &ItemStack, _player: &Player) {} /// Returns the maximum number of ticks this item can be used for. /// Return 0 if the item does not have a behaviour-driven use duration. diff --git a/crates/pumpkin/src/item/registry.rs b/crates/pumpkin/src/item/registry.rs index fb4d07522..708510159 100644 --- a/crates/pumpkin/src/item/registry.rs +++ b/crates/pumpkin/src/item/registry.rs @@ -26,32 +26,30 @@ impl ItemRegistry { } } - pub async fn on_use(&self, stack: &ItemStack, player: &Player) { + pub fn on_use(&self, stack: &ItemStack, player: &Player) { let item = stack.item; let cooldown = stack.get_use_cooldown(); let cooldown_group = cooldown .and_then(|c| c.cooldown_group.clone()) .unwrap_or_else(|| item.registry_key.to_string()); - if player.is_on_cooldown(&cooldown_group).await { + if player.is_on_cooldown(&cooldown_group) { return; } let pumpkin_item = self.get_pumpkin_item(item.id); if let Some(pumpkin_item) = pumpkin_item { - pumpkin_item.normal_use(item, player).await; + pumpkin_item.normal_use(item, player); } if let Some(cooldown) = cooldown { - player - .start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32) - .await; + player.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32); } } - pub async fn on_stopped_using(&self, stack: &ItemStack, player: &Player) { + pub fn on_stopped_using(&self, stack: &ItemStack, player: &Player) { if let Some(behaviour) = self.get_pumpkin_item(stack.item.id) { - behaviour.on_stopped_using(stack, player).await; + behaviour.on_stopped_using(stack, player); } } @@ -65,7 +63,7 @@ impl ItemRegistry { } #[expect(clippy::too_many_arguments)] - pub async fn use_on_block( + pub fn use_on_block( &self, stack: &mut ItemStack, player: &Player, @@ -81,25 +79,21 @@ impl ItemRegistry { .and_then(|c| c.cooldown_group.clone()) .unwrap_or_else(|| stack.item.registry_key.to_string()); - if player.is_on_cooldown(&cooldown_group).await { + if player.is_on_cooldown(&cooldown_group) { return; } let pumpkin_item = self.get_pumpkin_item(stack.item.id); if let Some(pumpkin_item) = pumpkin_item { - pumpkin_item - .use_on_block(stack, player, location, face, cursor_pos, block, server) - .await; + pumpkin_item.use_on_block(stack, player, location, face, cursor_pos, block, server); } if let Some(cooldown) = cooldown { - player - .start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32) - .await; + player.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32); } } - pub async fn use_on_entity( + pub fn use_on_entity( &self, stack: &mut ItemStack, player: &Player, @@ -111,19 +105,17 @@ impl ItemRegistry { .and_then(|c| c.cooldown_group.clone()) .unwrap_or_else(|| stack.item.registry_key.to_string()); - if player.is_on_cooldown(&cooldown_group).await { + if player.is_on_cooldown(&cooldown_group) { return; } let pumpkin_item = self.get_pumpkin_item(stack.item.id); if let Some(pumpkin_item) = pumpkin_item { - pumpkin_item.use_on_entity(stack, player, entity).await; + pumpkin_item.use_on_entity(stack, player, entity); } if let Some(cooldown) = cooldown { - player - .start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32) - .await; + player.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32); } } diff --git a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs index e4797a918..9b11ec13c 100644 --- a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs @@ -211,18 +211,15 @@ impl BedrockClient { let mut stack = held_item; if !stack.is_empty() { - server - .item_registry - .use_on_block( - &mut stack, - player, - data.block_position, - face, - data.click_position, - block, - &server, - ) - .await; + server.item_registry.use_on_block( + &mut stack, + player, + data.block_position, + face, + data.click_position, + block, + &server, + ); let item_id = stack.item.id; if let Some(placed_block) = pumpkin_data::Block::from_item_id(item_id) { @@ -284,7 +281,7 @@ impl BedrockClient { .cooldown_group .clone() .unwrap_or_else(|| held.item.registry_key.to_string()); - if player.is_on_cooldown(&group).await { + if player.is_on_cooldown(&group) { cooldown_active = true; } } @@ -294,7 +291,11 @@ impl BedrockClient { || held.get_data_component::().is_some() { if let Some(food) = held.get_data_component::() { - if player.abilities.lock().await.invulnerable + if player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .invulnerable || food.can_always_eat || player.hunger_manager.level.load() < 20 { @@ -355,7 +356,7 @@ impl BedrockClient { &server; event; 'after: { - server.item_registry.on_use(&stack_for_use, player).await; + server.item_registry.on_use(&stack_for_use, player); } }} } @@ -369,14 +370,13 @@ impl BedrockClient { let world = player.world(); if let Some(target) = world.get_entity_by_id(target_runtime_id) { let mut stack = player.inventory().held_item(); - if !target.interact(player, &mut stack).await { + if !target.interact(player, &mut stack) { let Some(server) = world.server.upgrade() else { return; }; server .item_registry - .use_on_entity(&mut stack, player, target) - .await; + .use_on_entity(&mut stack, player, target); player.inventory().set_held_item(stack); } } @@ -408,7 +408,7 @@ impl BedrockClient { let Some(server) = player.world().server.upgrade() else { return; }; - server.item_registry.on_stopped_using(&stack, player).await; + server.item_registry.on_stopped_using(&stack, player); } player.living_entity.clear_active_hand(); } diff --git a/crates/pumpkin/src/net/bedrock/play/player_action.rs b/crates/pumpkin/src/net/bedrock/play/player_action.rs index 98f793d78..64d273824 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_action.rs @@ -69,7 +69,7 @@ impl BedrockClient { server, broken_state, ); - player.apply_tool_damage_for_block_break(broken_state).await; + player.apply_tool_damage_for_block_break(broken_state); if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } @@ -94,30 +94,26 @@ impl BedrockClient { .current_block_breaking_speed .swap(speed.to_bits(), Ordering::Relaxed); if starts_breaking { - world - .set_block_breaking( - entity, - location, - BlockBreakingProgress::Start { - stage: progress, - speed, - }, - ) - .await; + world.set_block_breaking( + entity, + location, + BlockBreakingProgress::Start { + stage: progress, + speed, + }, + ); player .current_block_destroy_stage .store(progress, Ordering::Relaxed); } else if old_speed != speed.to_bits() { - world - .set_block_breaking( - entity, - location, - BlockBreakingProgress::Update { - stage: progress, - speed: Some(speed), - }, - ) - .await; + world.set_block_breaking( + entity, + location, + BlockBreakingProgress::Update { + stage: progress, + speed: Some(speed), + }, + ); } } } @@ -157,7 +153,7 @@ impl BedrockClient { server .block_registry .broken(&world, block, player, &location, server, state); - player.apply_tool_damage_for_block_break(state).await; + player.apply_tool_damage_for_block_break(state); if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } @@ -169,18 +165,16 @@ impl BedrockClient { if matches!(action, PlayerAction::StopDestroyBlock) { player.stop_mining().await; } else { - world - .set_block_breaking( - entity, - location, - BlockBreakingProgress::Update { - stage: player - .current_block_destroy_stage - .load(Ordering::Relaxed), - speed: Some(speed), - }, - ) - .await; + world.set_block_breaking( + entity, + location, + BlockBreakingProgress::Update { + stage: player + .current_block_destroy_stage + .load(Ordering::Relaxed), + speed: Some(speed), + }, + ); } } } else if matches!(action, PlayerAction::StopDestroyBlock) { diff --git a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs index 91c716be2..ca15455a7 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs @@ -170,7 +170,13 @@ impl BedrockClient { } if input_data.get(InputData::StartFlying as usize) { - let flying = { player.abilities.lock().await.flying }; + let flying = { + player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .flying + }; if !flying { send_cancellable! {{ server; @@ -178,7 +184,7 @@ impl BedrockClient { 'after: { player.living_entity.fall_distance.store(0.0); { - player.abilities.lock().await.flying = true; + player.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).flying = true; }; player.send_abilities_update().await; } @@ -188,14 +194,20 @@ impl BedrockClient { }} } } else if input_data.get(InputData::StopFlying as usize) { - let flying = { player.abilities.lock().await.flying }; + let flying = { + player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .flying + }; if flying { send_cancellable! {{ server; PlayerToggleFlightEvent::new(player.clone(), false); 'after: { { - player.abilities.lock().await.flying = false; + player.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).flying = false; }; player.send_abilities_update().await; } diff --git a/crates/pumpkin/src/net/bedrock/play/request_ability.rs b/crates/pumpkin/src/net/bedrock/play/request_ability.rs index 9fba99324..e43855f71 100644 --- a/crates/pumpkin/src/net/bedrock/play/request_ability.rs +++ b/crates/pumpkin/src/net/bedrock/play/request_ability.rs @@ -16,13 +16,17 @@ impl BedrockClient { requested_flying, ) = packet.value { - let mut abilities = player.abilities.lock().await; - if abilities.allow_flying { - abilities.flying = requested_flying; - } else { - abilities.flying = false; + { + let mut abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if abilities.allow_flying { + abilities.flying = requested_flying; + } else { + abilities.flying = false; + } } - drop(abilities); player.send_abilities_update().await; } } diff --git a/crates/pumpkin/src/net/java/play/client_command.rs b/crates/pumpkin/src/net/java/play/client_command.rs index 3c022bc8f..43382b463 100644 --- a/crates/pumpkin/src/net/java/play/client_command.rs +++ b/crates/pumpkin/src/net/java/play/client_command.rs @@ -22,7 +22,10 @@ impl JavaClient { // Restore abilities based on gamemode after respawn { - let mut abilities = player.abilities.lock().await; + let mut abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); abilities.set_for_gamemode(player.gamemode.load()); }; player.send_abilities_update().await; diff --git a/crates/pumpkin/src/net/java/play/interact.rs b/crates/pumpkin/src/net/java/play/interact.rs index 2cd126b1c..a214740af 100644 --- a/crates/pumpkin/src/net/java/play/interact.rs +++ b/crates/pumpkin/src/net/java/play/interact.rs @@ -107,12 +107,11 @@ impl JavaClient { player.trigger_advancement(crate::entity::player::advancement::trigger::AdvancementTrigger::CuredZombieVillager); } - let interacted = event.target.interact(player, &mut stack).await; + let interacted = event.target.interact(player, &mut stack); if !interacted { server .item_registry - .use_on_entity(&mut stack, player, event.target) - .await; + .use_on_entity(&mut stack, player, event.target); } player.inventory().set_held_item(stack); } diff --git a/crates/pumpkin/src/net/java/play/move_vehicle.rs b/crates/pumpkin/src/net/java/play/move_vehicle.rs index 0c8fc689b..2a8bb8fb2 100644 --- a/crates/pumpkin/src/net/java/play/move_vehicle.rs +++ b/crates/pumpkin/src/net/java/play/move_vehicle.rs @@ -5,13 +5,16 @@ impl JavaClient { pub async fn handle_move_vehicle(&self, player: &Arc, packet: SMoveVehicle) { let entity = player.get_entity(); let pos = Vector3::new(packet.x, packet.y, packet.z); - let vehicle = entity.vehicle.lock().await; - if let Some(vehicle) = vehicle.as_ref() { + let vehicle = entity + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + if let Some(vehicle) = vehicle { let vehicle_entity = vehicle.get_entity(); vehicle_entity.set_pos(pos); vehicle_entity.set_rotation(packet.yaw, packet.pitch); } - drop(vehicle); entity.set_pos(pos); chunker::update_position(player).await; } diff --git a/crates/pumpkin/src/net/java/play/paddle_boat.rs b/crates/pumpkin/src/net/java/play/paddle_boat.rs index 09d77a87a..83d104e4e 100644 --- a/crates/pumpkin/src/net/java/play/paddle_boat.rs +++ b/crates/pumpkin/src/net/java/play/paddle_boat.rs @@ -3,7 +3,12 @@ use super::*; impl JavaClient { pub async fn handle_paddle_boat(&self, player: &Arc, packet: SPaddleBoat) { - let vehicle = player.get_entity().vehicle.lock().await.clone(); + let vehicle = player + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(vehicle) = vehicle { vehicle.set_paddle_state(packet.left_paddle, packet.right_paddle); } diff --git a/crates/pumpkin/src/net/java/play/player_abilities.rs b/crates/pumpkin/src/net/java/play/player_abilities.rs index 4f64e37dd..80806ee46 100644 --- a/crates/pumpkin/src/net/java/play/player_abilities.rs +++ b/crates/pumpkin/src/net/java/play/player_abilities.rs @@ -9,7 +9,10 @@ impl JavaClient { server: &Arc, ) { let (flying, allow_flying) = { - let abilities = player.abilities.lock().await; + let abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); (abilities.flying, abilities.allow_flying) }; @@ -23,7 +26,7 @@ impl JavaClient { if event.is_flying { player.living_entity.fall_distance.store(0.0); } - player.abilities.lock().await.flying = event.is_flying; + player.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).flying = event.is_flying; } 'cancelled: { player.send_abilities_update().await; diff --git a/crates/pumpkin/src/net/java/play/player_action.rs b/crates/pumpkin/src/net/java/play/player_action.rs index dd75542ce..b22e18db7 100644 --- a/crates/pumpkin/src/net/java/play/player_action.rs +++ b/crates/pumpkin/src/net/java/play/player_action.rs @@ -114,7 +114,7 @@ impl JavaClient { server, broken_state, ); - player.apply_tool_damage_for_block_break(broken_state).await; + player.apply_tool_damage_for_block_break(broken_state); if can_harvest { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } @@ -134,16 +134,14 @@ impl JavaClient { player .current_block_breaking_speed .store(speed.to_bits(), Ordering::Relaxed); - world - .set_block_breaking( - entity, - position, - BlockBreakingProgress::Start { - stage: progress, - speed, - }, - ) - .await; + world.set_block_breaking( + entity, + position, + BlockBreakingProgress::Start { + stage: progress, + speed, + }, + ); player .current_block_destroy_stage .store(progress, Ordering::Relaxed); @@ -162,15 +160,11 @@ impl JavaClient { } player.mining.store(false, Ordering::Relaxed); let entity = &player.get_entity(); - entity - .world - .load() - .set_block_breaking( - entity, - player_action.position, - BlockBreakingProgress::Stop, - ) - .await; + entity.world.load().set_block_breaking( + entity, + player_action.position, + BlockBreakingProgress::Stop, + ); self.update_sequence(player, player_action.sequence.0); } Status::FinishedDigging => { @@ -190,9 +184,7 @@ impl JavaClient { let world = entity.world.load_full(); player.mining.store(false, Ordering::Relaxed); - world - .set_block_breaking(entity, location, BlockBreakingProgress::Stop) - .await; + world.set_block_breaking(entity, location, BlockBreakingProgress::Stop); let (block, state) = world.get_block_and_state(&location); let block_drop = player.gamemode.load() != GameMode::Creative @@ -212,7 +204,7 @@ impl JavaClient { .block_registry .broken(&world, block, player, &location, server, state); - player.apply_tool_damage_for_block_break(state).await; + player.apply_tool_damage_for_block_break(state); if block_drop { player.add_exhaustion(MINE_BLOCK_EXHAUSTION).await; } @@ -243,7 +235,7 @@ impl JavaClient { .unwrap_or_else(std::sync::PoisonError::into_inner) .clone(); if let Some(stack) = item_in_use { - server.item_registry.on_stopped_using(&stack, player).await; + server.item_registry.on_stopped_using(&stack, player); } player.living_entity.clear_active_hand(); diff --git a/crates/pumpkin/src/net/java/play/player_input.rs b/crates/pumpkin/src/net/java/play/player_input.rs index 45e23622a..09acbc8ff 100644 --- a/crates/pumpkin/src/net/java/play/player_input.rs +++ b/crates/pumpkin/src/net/java/play/player_input.rs @@ -38,7 +38,12 @@ impl JavaClient { 'after: { player.get_entity().set_sneaking(event.is_sneaking).await; if event.is_sneaking { - let vehicle = player.get_entity().vehicle.lock().await.clone(); + let vehicle = player + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(vehicle) = vehicle { vehicle .get_entity() @@ -49,7 +54,12 @@ impl JavaClient { } }} } else if sneak { - let vehicle = player.get_entity().vehicle.lock().await.clone(); + let vehicle = player + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(vehicle) = vehicle { vehicle .get_entity() diff --git a/crates/pumpkin/src/net/java/play/player_position.rs b/crates/pumpkin/src/net/java/play/player_position.rs index 31639b31a..8f8e261da 100644 --- a/crates/pumpkin/src/net/java/play/player_position.rs +++ b/crates/pumpkin/src/net/java/play/player_position.rs @@ -94,7 +94,7 @@ impl JavaClient { let distance = last_pos.squared_distance_to_vec(&pos).sqrt(); let cm = (distance * 100.0) as i32; if cm > 0 { - let stat = player.get_movement_statistic().await; + let stat = player.get_movement_statistic(); player.increment_stat(StatisticCategory::Custom, stat as i32, cm); } @@ -141,7 +141,7 @@ impl JavaClient { } // Only process fall damage if player is alive - if !player.abilities.lock().await.flying + if !player.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).flying && player.living_entity.health.load() > 0.0 && !player.living_entity.dead.load(Ordering::Relaxed) { @@ -228,7 +228,7 @@ impl JavaClient { let distance = last_pos.squared_distance_to_vec(&pos).sqrt(); let cm = (distance * 100.0) as i32; if cm > 0 { - let stat = player.get_movement_statistic().await; + let stat = player.get_movement_statistic(); player.increment_stat(StatisticCategory::Custom, stat as i32, cm); } @@ -293,7 +293,7 @@ impl JavaClient { ) ; // Only process fall damage if player is alive - if !player.abilities.lock().await.flying + if !player.abilities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).flying && player.living_entity.health.load() > 0.0 && !player.living_entity.dead.load(Ordering::Relaxed) { diff --git a/crates/pumpkin/src/net/java/play/tag_query.rs b/crates/pumpkin/src/net/java/play/tag_query.rs index c362407cb..c94326d31 100644 --- a/crates/pumpkin/src/net/java/play/tag_query.rs +++ b/crates/pumpkin/src/net/java/play/tag_query.rs @@ -33,7 +33,7 @@ impl JavaClient { let mut compound = NbtCompound::new(); if let Some(entity) = player.world().get_entity_by_id(packet.entity_id.0) { - entity.write_nbt(&mut compound).await; + entity.write_nbt(&mut compound); } let nbt_bytes = Nbt::new(String::new(), compound).write_unnamed(); diff --git a/crates/pumpkin/src/net/java/play/use_item.rs b/crates/pumpkin/src/net/java/play/use_item.rs index 7509a4d31..0deefb18f 100644 --- a/crates/pumpkin/src/net/java/play/use_item.rs +++ b/crates/pumpkin/src/net/java/play/use_item.rs @@ -72,7 +72,7 @@ impl JavaClient { server; event; 'after: { - server.item_registry.on_use(&stack_for_use, player).await; + server.item_registry.on_use(&stack_for_use, player); } }} } @@ -90,7 +90,7 @@ impl JavaClient { .cooldown_group .clone() .unwrap_or_else(|| held.item.registry_key.to_string()); - if player.is_on_cooldown(&group).await { + if player.is_on_cooldown(&group) { return; } } @@ -100,7 +100,11 @@ impl JavaClient { { // If its food we want to make sure we can actually consume it if let Some(food) = held.get_data_component::() { - if player.abilities.lock().await.invulnerable + if player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .invulnerable || food.can_always_eat || player.hunger_manager.level.load() < 20 { diff --git a/crates/pumpkin/src/net/java/play/use_item_on.rs b/crates/pumpkin/src/net/java/play/use_item_on.rs index 71cf67afd..9db0ce41f 100644 --- a/crates/pumpkin/src/net/java/play/use_item_on.rs +++ b/crates/pumpkin/src/net/java/play/use_item_on.rs @@ -120,8 +120,7 @@ impl JavaClient { server .item_registry - .use_on_block(&mut item, player, position, face, cursor_pos, block, server) - .await; + .use_on_block(&mut item, player, position, face, cursor_pos, block, server); // Check if the item is a block, because not every item can be placed :D let item_id = item.item.id; @@ -157,7 +156,7 @@ impl JavaClient { } if !after.are_equal(&before) { - player.sync_hand_slot(slot_index, after.clone()).await; + player.sync_hand_slot(slot_index, after.clone()); inventory.set_stack_in_hand(hand, after); } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs index 548b33c09..da7e7cf02 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs @@ -909,7 +909,11 @@ impl HostEntity for PluginHostState { entity: Resource, ) -> wasmtime::Result>> { let entity = entity_from_resource(self, &entity)?; - let vehicle = entity.get_entity().vehicle.lock().await; + let vehicle = entity + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(v) = vehicle.as_ref() { Ok(Some(self.add_entity(Arc::clone(v)).map_err(|_| { wasmtime::Error::msg("failed to add entity resource") @@ -927,7 +931,12 @@ impl HostEntity for PluginHostState { let entity_base = entity_from_resource(self, &entity)?; // Remove from current vehicle if any - let current_vehicle = entity_base.get_entity().vehicle.lock().await.clone(); + let current_vehicle = entity_base + .get_entity() + .vehicle + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); if let Some(v) = current_vehicle { v.get_entity() .remove_passenger(entity_base.get_entity().entity_id) @@ -938,8 +947,7 @@ impl HostEntity for PluginHostState { let vehicle_base = entity_from_resource(self, &vehicle_res)?; vehicle_base .get_entity() - .add_passenger(vehicle_base.clone(), entity_base) - .await; + .add_passenger(vehicle_base.clone(), entity_base); } Ok(()) @@ -950,7 +958,11 @@ impl HostEntity for PluginHostState { entity: Resource, ) -> wasmtime::Result>> { let entity = entity_from_resource(self, &entity)?; - let passengers = entity.get_entity().passengers.lock().await; + let passengers = entity + .get_entity() + .passengers + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut result = Vec::new(); for p in passengers.iter() { result.push( @@ -970,8 +982,7 @@ impl HostEntity for PluginHostState { let passenger = entity_from_resource(self, &passenger)?; entity .get_entity() - .add_passenger(Arc::clone(&entity), passenger) - .await; + .add_passenger(Arc::clone(&entity), passenger); Ok(()) } @@ -995,7 +1006,7 @@ impl HostEntity for PluginHostState { .get_entity() .passengers .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .iter() .map(|p| p.get_entity().entity_id) .collect(); @@ -1379,7 +1390,7 @@ impl HostEntity for PluginHostState { let entity = entity_from_resource(self, &this)?; let base_entity = entity.get_entity(); let tag = super::common::from_wit_nbt_tree(&value).map_err(wasmtime::Error::msg)?; - base_entity.set_custom_data(&namespace, &key, tag).await; + base_entity.set_custom_data(&namespace, &key, tag); Ok(()) } @@ -1391,7 +1402,7 @@ impl HostEntity for PluginHostState { ) -> wasmtime::Result> { let entity = entity_from_resource(self, &this)?; let base_entity = entity.get_entity(); - let tag = base_entity.get_custom_data(&namespace, &key).await; + let tag = base_entity.get_custom_data(&namespace, &key); Ok(tag.map(super::common::to_wit_nbt_tree)) } @@ -1403,7 +1414,7 @@ impl HostEntity for PluginHostState { ) -> wasmtime::Result<()> { let entity = entity_from_resource(self, &this)?; let base_entity = entity.get_entity(); - base_entity.remove_custom_data(&namespace, &key).await; + base_entity.remove_custom_data(&namespace, &key); Ok(()) } @@ -1415,7 +1426,7 @@ impl HostEntity for PluginHostState { ) -> wasmtime::Result { let entity = entity_from_resource(self, &this)?; let base_entity = entity.get_entity(); - Ok(base_entity.has_custom_data(&namespace, &key).await) + Ok(base_entity.has_custom_data(&namespace, &key)) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs index ac544c05f..5b677963b 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs @@ -1411,7 +1411,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { player: Resource, ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; - let display_name = player.get_display_name().await; + let display_name = player.get_display_name(); self.add_text_component(display_name) .map_err(|_| wasmtime::Error::msg("failed to add text-component resource")) } @@ -1585,9 +1585,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { stat_id: i32, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player - .get_stat(from_wit_statistic_category(category), stat_id) - .await) + Ok(player.get_stat(from_wit_statistic_category(category), stat_id)) } async fn set_statistic( @@ -1620,9 +1618,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { stat: WitCustomStatistic, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player - .get_custom_stat(from_wit_custom_statistic(stat)) - .await) + Ok(player.get_custom_stat(from_wit_custom_statistic(stat))) } async fn set_custom_statistic( @@ -1666,7 +1662,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { duration_ticks: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.start_cooldown(group, duration_ticks).await; + player.start_cooldown(group, duration_ticks); Ok(()) } @@ -1676,7 +1672,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { group: String, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player.get_cooldown(&group).await) + Ok(player.get_cooldown(&group)) } async fn is_on_cooldown( @@ -1685,7 +1681,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { group: String, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player.is_on_cooldown(&group).await) + Ok(player.is_on_cooldown(&group)) } async fn set_allow_flight( @@ -1917,7 +1913,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { ticks: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_item_cooldown(&item_id, ticks).await; + player.set_item_cooldown(&item_id, ticks); Ok(()) } @@ -1927,7 +1923,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { item_id: String, ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; - Ok(player.get_item_cooldown(&item_id).await) + Ok(player.get_item_cooldown(&item_id)) } async fn has_item_cooldown( @@ -1936,7 +1932,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { item_id: String, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - Ok(player.has_item_cooldown(&item_id).await) + Ok(player.has_item_cooldown(&item_id)) } async fn ray_trace_block( @@ -2384,7 +2380,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { level: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_experience_level(level, true).await; + player.set_experience_level(level, true); Ok(()) } @@ -2394,13 +2390,11 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { progress: f32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .set_experience( - player.experience_level.load(Ordering::Relaxed), - progress, - player.experience_points.load(Ordering::Relaxed), - ) - .await; + player.set_experience( + player.experience_level.load(Ordering::Relaxed), + progress, + player.experience_points.load(Ordering::Relaxed), + ); Ok(()) } @@ -2410,13 +2404,11 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { points: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player - .set_experience( - player.experience_level.load(Ordering::Relaxed), - player.experience_progress.load(), - points, - ) - .await; + player.set_experience( + player.experience_level.load(Ordering::Relaxed), + player.experience_progress.load(), + points, + ); Ok(()) } @@ -2426,7 +2418,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { levels: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.add_experience_levels(levels).await; + player.add_experience_levels(levels); Ok(()) } @@ -2436,7 +2428,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { points: i32, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.add_experience_points(points).await; + player.add_experience_points(points); Ok(()) } @@ -2448,7 +2440,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn set_flying(&mut self, player: Resource, flying: bool) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; { - let mut abilities = player.abilities.lock().await; + let mut abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); abilities.flying = flying; }; player.send_abilities_update().await; @@ -2460,7 +2455,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { player: Resource, ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let abilities = player.abilities.lock().await; + let abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); Ok(pumpkin::plugin::player::PlayerAbilities { invulnerable: abilities.invulnerable, flying: abilities.flying, @@ -2479,7 +2477,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; { - let mut a = player.abilities.lock().await; + let mut a = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); a.invulnerable = abilities.invulnerable; a.flying = abilities.flying; a.allow_flying = abilities.allow_flying; @@ -3312,7 +3313,10 @@ impl pumpkin::plugin::player::HostBedrockPlayer for PluginHostState { .clone(); let ability = from_wasm_bedrock_ability(ability); - let abilities = player.abilities.lock().await; + let abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); match ability { pumpkin_protocol::bedrock::client::update_abilities::Ability::Build @@ -3352,7 +3356,10 @@ impl pumpkin::plugin::player::HostBedrockPlayer for PluginHostState { let ability = from_wasm_bedrock_ability(ability); { - let mut abilities = player.abilities.lock().await; + let mut abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); match ability { pumpkin_protocol::bedrock::client::update_abilities::Ability::Build | pumpkin_protocol::bedrock::client::update_abilities::Ability::Mine => { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs index d9aa675ef..94db14714 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs @@ -816,10 +816,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { } async fn set_raining(&mut self, world: Resource, raining: bool) -> wasmtime::Result<()> { - self.get_world_res(&world)? - .provider - .set_raining(raining) - .await; + self.get_world_res(&world)?.provider.set_raining(raining); Ok(()) } @@ -834,8 +831,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { ) -> wasmtime::Result<()> { self.get_world_res(&world)? .provider - .set_thundering(thundering) - .await; + .set_thundering(thundering); Ok(()) } @@ -966,14 +962,11 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { pumpkin::plugin::world::ExplosionInteraction::Tnt => ExplosionInteraction::Tnt, pumpkin::plugin::world::ExplosionInteraction::Trigger => ExplosionInteraction::Trigger, }; - world_ref - .provider - .explode( - pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2), - power, - interaction, - ) - .await; + world_ref.provider.explode( + pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2), + power, + interaction, + ); Ok(()) } @@ -1111,9 +1104,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { ) -> wasmtime::Result<()> { let world_provider = self.get_world_res(&world)?.provider.clone(); let internal_pos = super::events::from_wasm_position(pos); - world_provider - .strike_lightning(internal_pos, effect_only) - .await; + world_provider.strike_lightning(internal_pos, effect_only); Ok(()) } @@ -1693,12 +1684,22 @@ impl pumpkin::plugin::world::HostChunk for PluginHostState { impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { async fn get_center_x(&mut self, border: Resource) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.center_x) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .center_x) } async fn get_center_z(&mut self, border: Resource) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.center_z) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .center_z) } async fn get_center( @@ -1706,7 +1707,11 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { border: Resource, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - let guard = border_res.provider.worldborder.lock().await; + let guard = border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); Ok(super::events::to_wasm_position( pumpkin_util::math::vector3::Vector3::new(guard.center_x, 0.0, guard.center_z), )) @@ -1720,13 +1725,22 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { ) -> wasmtime::Result<()> { let border_res = self.get_world_border_res(&border)?; let world = border_res.provider.clone(); - world.worldborder.lock().await.set_center(&world, x, z); + world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .set_center(&world, x, z); Ok(()) } async fn get_diameter(&mut self, border: Resource) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.new_diameter) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .new_diameter) } async fn get_size(&mut self, border: Resource) -> wasmtime::Result { @@ -1744,7 +1758,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { world .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .set_diameter(&world, diameter, speed.map(|s| s as i64)); Ok(()) } @@ -1773,7 +1787,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { border: Resource, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.new_diameter) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .new_diameter) } async fn get_target_speed( @@ -1781,7 +1800,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { border: Resource, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.speed) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .speed) } async fn get_warning_distance( @@ -1789,7 +1813,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { border: Resource, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.warning_blocks) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .warning_blocks) } async fn set_warning_distance( @@ -1802,7 +1831,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { world .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .set_warning_distance(&world, distance); Ok(()) } @@ -1812,7 +1841,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { border: Resource, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.warning_time) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .warning_time) } async fn set_warning_delay( @@ -1825,7 +1859,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { world .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .set_warning_delay(&world, delay); Ok(()) } @@ -1851,7 +1885,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; Ok(f64::from( - border_res.provider.worldborder.lock().await.buffer, + border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .buffer, )) } @@ -1865,7 +1904,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { .provider .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .set_damage_buffer(buffer as f32); Ok(()) } @@ -1880,7 +1919,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { .provider .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .damage_per_block, )) } @@ -1895,7 +1934,7 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { .provider .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .set_damage_per_block(damage as f32); Ok(()) } @@ -1907,7 +1946,12 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { z: f64, ) -> wasmtime::Result { let border_res = self.get_world_border_res(&border)?; - Ok(border_res.provider.worldborder.lock().await.contains(x, z)) + Ok(border_res + .provider + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .contains(x, z)) } async fn contains_pos( @@ -1920,14 +1964,18 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState { .provider .worldborder .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .contains(pos.0, pos.2)) } async fn reset(&mut self, border: Resource) -> wasmtime::Result<()> { let border_res = self.get_world_border_res(&border)?; let world = border_res.provider.clone(); - world.worldborder.lock().await.reset(&world); + world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .reset(&world); Ok(()) } diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs index 20ef3e069..93a54332d 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -703,7 +703,7 @@ impl Server { .await; if let Some(mut nbt_data) = nbt { - player.read_nbt(&mut nbt_data).await; + player.read_nbt(&mut nbt_data); // The data file itself proves this is a returning player. Older Bedrock // sessions could persist HasPlayedBefore as false and mask a valid Pos. player.has_played_before.store(true, Ordering::Relaxed); diff --git a/crates/pumpkin/src/world/border.rs b/crates/pumpkin/src/world/border.rs index 77f2015b0..3cd7ad4d8 100644 --- a/crates/pumpkin/src/world/border.rs +++ b/crates/pumpkin/src/world/border.rs @@ -44,19 +44,19 @@ impl Worldborder { } } - pub async fn init_client(&self, client: &JavaClient) { - client - .enqueue_client_packet(&CInitializeWorldBorder::new( - self.center_x, - self.center_z, - self.old_diameter, - self.new_diameter, - self.speed.into(), - self.portal_teleport_boundary.into(), - self.warning_blocks.into(), - self.warning_time.into(), - )) - .await; + pub fn init_client(&self, client: &JavaClient) { + if let Ok(data) = client.serialize_packet(&CInitializeWorldBorder::new( + self.center_x, + self.center_z, + self.old_diameter, + self.new_diameter, + self.speed.into(), + self.portal_teleport_boundary.into(), + self.warning_blocks.into(), + self.warning_time.into(), + )) { + client.try_enqueue_packet(data); + } } pub fn set_center(&mut self, world: &World, x: f64, z: f64) { diff --git a/crates/pumpkin/src/world/explosion.rs b/crates/pumpkin/src/world/explosion.rs index a41406eb5..900a764f7 100644 --- a/crates/pumpkin/src/world/explosion.rs +++ b/crates/pumpkin/src/world/explosion.rs @@ -499,7 +499,7 @@ impl Explosion { } /// Returns the removed block count - pub async fn explode(&self, world: &Arc) -> u32 { + pub fn explode(&self, world: &Arc) -> u32 { self.damage_entities(world); match self.block_interaction { @@ -530,7 +530,7 @@ impl Explosion { }, ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return 0; @@ -542,7 +542,7 @@ impl Explosion { for (pos, (block, state)) in &blocks { world.set_block_state(pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL); - world.close_container_screens_at(pos).await; + world.close_container_screens_at(pos); let pumpkin_block = world.block_registry.get_pumpkin_block(block.id); @@ -562,7 +562,7 @@ impl Explosion { is_thundering: Some(is_thundering), ..Default::default() }; - drop_loot(world, block, pos, false, params).await; + drop_loot(world, block, pos, false, params); } if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.explode(ExplodeArgs { diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index a00a77427..49c76aed5 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -249,7 +249,7 @@ pub struct World { /// The world's scoreboard, used for tracking scores, objectives, and display information. pub scoreboard: Mutex, /// The world's worldborder, defining the playable area and controlling its expansion or contraction. - pub worldborder: Mutex, + pub worldborder: std::sync::Mutex, /// The world's time, including counting ticks for weather, time cycles, and statistics. pub level_time: std::sync::Mutex, /// The type of dimension the world is in. @@ -378,7 +378,14 @@ impl World { players: ArcSwap::new(Arc::new(Vec::new())), entities: ArcSwap::new(Arc::new(Vec::new())), scoreboard: Mutex::new(Scoreboard::default()), - worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 5.999_996_8E7, 0, 5, 300)), + worldborder: std::sync::Mutex::new(Worldborder::new( + 0.0, + 0.0, + 5.999_996_8E7, + 0, + 5, + 300, + )), level_time: std::sync::Mutex::new(LevelTime::new()), dimension, weather: std::sync::Mutex::new(Weather::new()), @@ -503,7 +510,7 @@ impl World { } let current_chunk = base_entity.block_pos.load().chunk_position(); let mut nbt = NbtCompound::new(); - entity.write_nbt(&mut nbt).await; + entity.write_nbt(&mut nbt); let chunk = self.level.get_entity_chunk(current_chunk).await; chunk.data.lock().await.push(nbt); chunk.mark_dirty(true); @@ -2158,14 +2165,14 @@ impl World { .is_rain_at(pos.0.x, pos.0.y, pos.0.z, self.sea_level) } - pub async fn set_raining(&self, raining: bool) { + pub fn set_raining(&self, raining: bool) { if let Some(server) = self.server.upgrade() { let world_arc = server.get_world_from_dimension(&self.dimension); let mut event = crate::plugin::api::events::world::weather_change::WeatherChangeEvent::new( world_arc, raining, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -2187,14 +2194,14 @@ impl World { .thundering } - pub async fn set_thundering(&self, thundering: bool) { + pub fn set_thundering(&self, thundering: bool) { if let Some(server) = self.server.upgrade() { let world_arc = server.get_world_from_dimension(&self.dimension); let mut event = crate::plugin::api::events::world::weather_change::ThunderChangeEvent::new( world_arc, thundering, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -2708,7 +2715,10 @@ impl World { .await; { - let mut abilities = player.abilities.lock().await; + let mut abilities = player + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); abilities.set_for_gamemode(player.gamemode.load()); }; @@ -3626,7 +3636,10 @@ impl World { .await; } - self.worldborder.lock().await.init_client(client).await; + self.worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .init_client(client); // Sends initial time player.send_time(self).await; @@ -3781,7 +3794,10 @@ impl World { pitch: f32, ) { if let ClientPlatform::Java(client) = player.client.as_ref() { - self.worldborder.lock().await.init_client(client).await; + self.worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .init_client(client); } // TODO: World spawn (compass stuff) @@ -3820,17 +3836,16 @@ impl World { player.set_health(20.0); } - pub async fn explode( + pub fn explode( self: &Arc, position: Vector3, power: f32, interaction: ExplosionInteraction, ) { - self.explode_with_calculator(position, power, interaction, None) - .await; + self.explode_with_calculator(position, power, interaction, None); } - pub async fn explode_with_calculator( + pub fn explode_with_calculator( self: &Arc, position: Vector3, power: f32, @@ -3842,13 +3857,13 @@ impl World { if let Some(calc) = damage_calculator { explosion = explosion.with_damage_calculator(calc); } - self.run_explosion(explosion, position, power).await; + self.run_explosion(explosion, position, power); } - pub async fn explode_tnt_minecart(self: &Arc, position: Vector3, power: f32) { + pub fn explode_tnt_minecart(self: &Arc, position: Vector3, power: f32) { let block_interaction = self.get_block_interaction(ExplosionInteraction::Tnt); let explosion = Explosion::new(power, position, block_interaction).preserving_rails(); - self.run_explosion(explosion, position, power).await; + self.run_explosion(explosion, position, power); } #[must_use] @@ -3882,23 +3897,18 @@ impl World { } } - async fn run_explosion( - self: &Arc, - explosion: Explosion, - position: Vector3, - power: f32, - ) { + fn run_explosion(self: &Arc, explosion: Explosion, position: Vector3, power: f32) { let mut event = crate::plugin::api::events::entity::entity_explode::EntityExplodeEvent::new( 0, position, power, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; } - let block_count = explosion.explode(self).await; + let block_count = explosion.explode(self); let particle = if power < 2.0 { Particle::Explosion } else { @@ -3913,16 +3923,14 @@ impl World { if player.position().squared_distance_to_vec(&position) > 4096.0 { continue; } - player - .send_client_packet(&CExplosion::new( - position, - power, - block_count as i32, - None, - VarInt(particle as i32), - sound.clone(), - )) - .await; + player.try_send_client_packet(&CExplosion::new( + position, + power, + block_count as i32, + None, + VarInt(particle as i32), + sound, + )); } } @@ -3969,11 +3977,19 @@ impl World { ) } else { // No valid respawn point - send notification if player had one set - if player.respawn_point.lock().await.is_some() { + if player + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() + { player .send_client_packet(&CGameEvent::new(GameEvent::NoRespawnBlockAvailable, 0.0)) .await; - let mut guard = player.respawn_point.lock().await; + let mut guard = player + .respawn_point + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(point) = guard.as_ref() && !point.force { @@ -4181,7 +4197,7 @@ impl World { player.hunger_manager.restart(); if !keep_inventory { - player.set_experience(0, 0.0, 0).await; + player.set_experience(0, 0.0, 0); player.inventory.clear().await; } @@ -4342,7 +4358,7 @@ impl World { // Pos is zero since it will be read from nbt. let entity = from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, uuid); - entity.read_nbt_non_mut(entity_nbt).await; + entity.read_nbt_non_mut(entity_nbt); entity.init_data_tracker(); let base_entity = entity.get_entity(); @@ -4924,7 +4940,7 @@ impl World { } } - pub(crate) async fn set_block_breaking( + pub(crate) fn set_block_breaking( &self, from: &Entity, location: BlockPos, @@ -4965,8 +4981,9 @@ impl World { if let Some(player) = self.get_player_by_uuid(from.entity_uuid) && let ClientPlatform::Bedrock(client) = player.client.as_ref() + && let Ok(packet_data) = client.serialize_packet(&be_packet) { - client.enqueue_client_packet(&be_packet).await; + client.try_enqueue_packet(packet_data); } self.broadcast_to_chunk_except_editioned( @@ -5166,11 +5183,14 @@ impl World { } /// Close container screens for all players who have a container open at the given block position. - pub async fn close_container_screens_at(&self, position: &BlockPos) { + pub fn close_container_screens_at(&self, position: &BlockPos) { let players = self.players.load(); for player in players.iter() { if player.open_container_pos.load() == Some(*position) { - player.close_handled_screen().await; + let player = player.clone(); + tokio::spawn(async move { + player.close_handled_screen().await; + }); } } } @@ -5205,7 +5225,7 @@ impl World { self.spawn_entity(item_entity); } - pub async fn strike_lightning(self: &Arc, pos: Vector3, effect_only: bool) { + pub fn strike_lightning(self: &Arc, pos: Vector3, effect_only: bool) { use pumpkin_data::entity::EntityType; use uuid::Uuid; @@ -5218,8 +5238,7 @@ impl World { ); server_ref .plugin_manager - .fire(&server_ref, &mut event) - .await; + .fire_blocking(&server_ref, &mut event); if event.cancelled { return; } @@ -6384,13 +6403,13 @@ impl World { Self::broadcast_bedrock_grouped(be_packet, bedrock_recipients.into_iter()); } - pub async fn emit_game_event(&self, event_key: impl Into, position: Vector3) { + pub fn emit_game_event(&self, event_key: impl Into, position: Vector3) { let mut event = crate::plugin::api::events::world::generic_game::GenericGameEvent::new( event_key.into(), position, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } } @@ -6569,96 +6588,113 @@ impl World { .is_some() } - pub async fn populate_chunk(&self, chunk_pos: Vector2) { + pub fn populate_chunk(&self, chunk_pos: Vector2) { let mut populate_event = crate::plugin::api::events::world::chunk_populate::ChunkPopulateEvent::new(chunk_pos); if let Some(server) = self.server.upgrade() { server .plugin_manager - .fire(&server, &mut populate_event) - .await; + .fire_blocking(&server, &mut populate_event); } } - pub async fn unload_chunk(&self, chunk_pos: Vector2) { + pub fn unload_chunk(&self, chunk_pos: Vector2) { let mut unload_event = crate::plugin::api::events::world::chunk_unload::ChunkUnloadEvent::new(chunk_pos); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut unload_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut unload_event); } } - pub async fn load_entities(&self, chunk_pos: Vector2, entity_count: usize) { + pub fn load_entities(&self, chunk_pos: Vector2, entity_count: usize) { let mut load_event = crate::plugin::api::events::world::entities_load::EntitiesLoadEvent::new( chunk_pos, entity_count, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut load_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut load_event); } } - pub async fn unload_entities(&self, chunk_pos: Vector2, entity_count: usize) { + pub fn unload_entities(&self, chunk_pos: Vector2, entity_count: usize) { let mut unload_event = crate::plugin::api::events::world::entities_unload::EntitiesUnloadEvent::new( chunk_pos, entity_count, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut unload_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut unload_event); } } - pub async fn generate_loot(&self, loot_table: String) { + pub fn generate_loot(&self, loot_table: String) { let mut loot_event = crate::plugin::api::events::world::loot_generate::LootGenerateEvent::new(loot_table); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut loot_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut loot_event); } } - pub async fn skip_time(&self, skip_amount: i64) { + pub fn skip_time(&self, skip_amount: i64) { let mut time_event = crate::plugin::api::events::world::time_skip::TimeSkipEvent::new(skip_amount); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut time_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut time_event); } } - pub async fn trigger_raid(&self, pos: BlockPos) { + pub fn trigger_raid(&self, pos: BlockPos) { let mut raid_event = crate::plugin::api::events::raid::raid_trigger::RaidTriggerEvent::new(pos); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut raid_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut raid_event); } } - pub async fn spawn_raid_wave(&self, wave: u32, pos: BlockPos) { + pub fn spawn_raid_wave(&self, wave: u32, pos: BlockPos) { let mut wave_event = crate::plugin::api::events::raid::raid_spawn_wave::RaidSpawnWaveEvent::new(wave, pos); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut wave_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut wave_event); } } - pub async fn finish_raid(&self, victory: bool) { + pub fn finish_raid(&self, victory: bool) { let mut raid_event = crate::plugin::api::events::raid::raid_finish::RaidFinishEvent::new(victory); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut raid_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut raid_event); } } - pub async fn stop_raid(&self, reason: String) { + pub fn stop_raid(&self, reason: String) { let mut raid_event = crate::plugin::api::events::raid::raid_stop::RaidStopEvent::new(reason); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut raid_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut raid_event); } } - pub async fn async_structure_generate( + pub fn async_structure_generate( &self, world_name: String, structure_name: String, @@ -6670,16 +6706,11 @@ impl World { pos, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } } - pub async fn async_structure_spawn( - &self, - world_name: String, - structure_name: String, - pos: BlockPos, - ) { + pub fn async_structure_spawn(&self, world_name: String, structure_name: String, pos: BlockPos) { let mut event = crate::plugin::api::events::world::async_structure_spawn::AsyncStructureSpawnEvent::new( world_name, @@ -6687,7 +6718,7 @@ impl World { pos, ); if let Some(server) = self.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } } } @@ -6788,8 +6819,8 @@ impl WorldPortalExt for WorldPortal { &world, Uuid::new_v4(), ); - entity.get_entity().read_nbt_non_mut(&nbt).await; - entity.read_nbt_non_mut(&nbt).await; + entity.get_entity().read_nbt_non_mut(&nbt); + entity.read_nbt_non_mut(&nbt); world.spawn_entity(entity); } }); diff --git a/crates/pumpkin/src/world/portal/mod.rs b/crates/pumpkin/src/world/portal/mod.rs index 889ff5a8c..c170715aa 100644 --- a/crates/pumpkin/src/world/portal/mod.rs +++ b/crates/pumpkin/src/world/portal/mod.rs @@ -68,7 +68,7 @@ impl PortalType { } #[expect(clippy::too_many_lines)] - pub async fn get_portal_destination( + pub fn get_portal_destination( &self, current_level: &World, dest_world: Arc, @@ -90,12 +90,8 @@ impl PortalType { let y = if is_player { 49.0 } else { 50.0 }; // Ensure chunks covering the platform are loaded/generated - dest_world - .get_block_state_async(&BlockPos::new(98, 49, -2)) - .await; - dest_world - .get_block_state_async(&BlockPos::new(102, 49, 2)) - .await; + dest_world.get_block_state(&BlockPos::new(98, 49, -2)); + dest_world.get_block_state(&BlockPos::new(102, 49, 2)); // Generate/regenerate the obsidian platform (5x5 obsidian at Y=48, and 5x5x3 air above it) let platform_pos = BlockPos::new(100, 49, 0); @@ -134,24 +130,24 @@ impl PortalType { { match player.client.as_ref() { crate::net::ClientPlatform::Java(client) => { - client - .enqueue_client_packet(&pumpkin_protocol::java::client::play::CGameEvent::new( - pumpkin_protocol::java::client::play::GameEvent::WinGame, - 1.0, - )) - .await; + if let Ok(data) = client.serialize_packet(&pumpkin_protocol::java::client::play::CGameEvent::new( + pumpkin_protocol::java::client::play::GameEvent::WinGame, + 1.0, + )) { + client.try_enqueue_packet(data); + } } crate::net::ClientPlatform::Bedrock(client) => { - client - .send_packet( - &pumpkin_protocol::bedrock::client::CShowCredits { - player_runtime_id: (caller.get_entity().entity_id - as u64) - .into(), - credits_state: 0.into(), - }, - ) - .await; + if let Ok(data) = client.serialize_packet( + &pumpkin_protocol::bedrock::client::CShowCredits { + player_runtime_id: (caller.get_entity().entity_id + as u64) + .into(), + credits_state: 0.into(), + }, + ) { + client.try_enqueue_packet(data); + } } } } @@ -186,7 +182,7 @@ impl PortalType { let source_axis = source_portal.as_ref().map(|p| p.axis); let (final_pos, yaw) = if let Some(dest_result) = - NetherPortal::search_for_portal(&dest_world, target_pos).await + NetherPortal::search_for_portal(&dest_world, target_pos) { let base_pos = source_portal.as_ref().map_or_else( || dest_result.get_teleport_position(), @@ -211,7 +207,6 @@ impl PortalType { target_pos, pumpkin_data::block_properties::HorizontalAxis::X, ) - .await { NetherPortal::build_portal_frame(&dest_world, build_pos, axis, is_fallback); let new_portal = PortalSearchResult { diff --git a/crates/pumpkin/src/world/portal/nether.rs b/crates/pumpkin/src/world/portal/nether.rs index b9529406a..18c50d54c 100644 --- a/crates/pumpkin/src/world/portal/nether.rs +++ b/crates/pumpkin/src/world/portal/nether.rs @@ -463,7 +463,7 @@ impl NetherPortal { || block == &Block::NETHER_PORTAL } - pub async fn search_for_portal( + pub fn search_for_portal( world: &Arc, target_pos: BlockPos, ) -> Option { @@ -474,7 +474,10 @@ impl NetherPortal { ); let min_y = world.min_y; let max_y = min_y + world.dimension.height - 1; - let worldborder = world.worldborder.lock().await; + let worldborder = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let search_radius = if world.dimension.has_ceiling { SEARCH_RADIUS_NETHER @@ -507,7 +510,7 @@ impl NetherPortal { continue; } - if world.get_block_state_id_async(&pos).await.to_block() != &Block::NETHER_PORTAL { + if world.get_block_state_id(&pos).to_block() != &Block::NETHER_PORTAL { continue; } @@ -548,7 +551,7 @@ impl NetherPortal { } #[allow(clippy::too_many_lines)] - pub async fn find_safe_location( + pub fn find_safe_location( world: &Arc, target_pos: BlockPos, axis: HorizontalAxis, @@ -560,7 +563,10 @@ impl NetherPortal { ); let min_y = world.min_y; let max_y = min_y + world.dimension.height - 1; - let worldborder = world.worldborder.lock().await; + let worldborder = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let top_y_limit = if world.dimension.has_ceiling { (min_y + world.dimension.logical_height - 1).min(max_y) @@ -592,25 +598,23 @@ impl NetherPortal { continue; } - let heightmap_y = world - .get_heightmap_height_async( - ChunkHeightmapType::MotionBlocking, - check_x, - check_z, - ) - .await; + let heightmap_y = world.get_heightmap_height( + ChunkHeightmapType::MotionBlocking, + check_x, + check_z, + ); let start_y = heightmap_y.min(top_y_limit); let mut y = start_y; while y >= min_y { let pos = BlockPos(Vector3::new(check_x, y, check_z)); - let state = world.get_block_state_async(&pos).await; + let state = world.get_block_state(&pos); if Self::is_valid_portal_air(state) { let mut bottom_y = y; while bottom_y > min_y { let below = BlockPos(Vector3::new(check_x, bottom_y - 1, check_z)); - let below_state = world.get_block_state_async(&below).await; + let below_state = world.get_block_state(&below); if !Self::is_valid_portal_air(below_state) { break; } @@ -622,23 +626,18 @@ impl NetherPortal { let floor_pos = BlockPos(Vector3::new(check_x, bottom_y, check_z)); for check_axis in [HorizontalAxis::X, HorizontalAxis::Z] { - if Self::is_valid_portal_pos_async(world, floor_pos, check_axis, 0) - .await - { + if Self::is_valid_portal_pos(world, floor_pos, check_axis, 0) { let dist = f64::from(target_pos.0.squared_distance_to( floor_pos.0.x, floor_pos.0.y, floor_pos.0.z, )); - let is_ideal = Self::is_valid_portal_pos_async( - world, floor_pos, check_axis, -1, - ) - .await - && Self::is_valid_portal_pos_async( - world, floor_pos, check_axis, 1, - ) - .await; + let is_ideal = + Self::is_valid_portal_pos(world, floor_pos, check_axis, -1) + && Self::is_valid_portal_pos( + world, floor_pos, check_axis, 1, + ); if is_ideal { if ideal_pos.as_ref().is_none_or(|p| dist < p.2) { @@ -686,7 +685,7 @@ impl NetherPortal { state.replaceable() && !state.is_liquid() } - async fn is_valid_portal_pos_async( + fn is_valid_portal_pos( world: &Arc, floor_pos: BlockPos, axis: HorizontalAxis, @@ -710,7 +709,7 @@ impl NetherPortal { .offset_dir(perpendicular.to_offset(), perpendicular_offset) .offset_dir(BlockDirection::Up.to_offset(), height); - let state = world.get_block_state_async(&pos).await; + let state = world.get_block_state(&pos); if height < 0 { if !state.is_solid_block() {