diff --git a/crates/pumpkin-data/src/item_stack/mod.rs b/crates/pumpkin-data/src/item_stack/mod.rs index a5f3f7c12..40e8acc4e 100644 --- a/crates/pumpkin-data/src/item_stack/mod.rs +++ b/crates/pumpkin-data/src/item_stack/mod.rs @@ -219,6 +219,16 @@ impl ItemStack { } } + pub fn set_data_component(&mut self, component: T) { + let to_set_id = T::get_enum(); + let boxed = Some(Box::new(component) as _); + if let Some((_, c)) = self.patch.iter_mut().find(|(id, _)| *id == to_set_id) { + *c = boxed; + } else { + self.patch.push((to_set_id, boxed)); + } + } + pub fn add_lore(&mut self, line: pumpkin_util::text::TextComponent) { let mut lines = self .get_data_component::() diff --git a/crates/pumpkin-inventory/src/anvil/anvil_screen_handler.rs b/crates/pumpkin-inventory/src/anvil/anvil_screen_handler.rs index e10c3d427..80b12f2f0 100644 --- a/crates/pumpkin-inventory/src/anvil/anvil_screen_handler.rs +++ b/crates/pumpkin-inventory/src/anvil/anvil_screen_handler.rs @@ -6,10 +6,7 @@ use pumpkin_world::inventory::Inventory; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, offer_or_drop_stack, - }, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, offer_or_drop_stack}, slot::NormalSlot, window_property::{Anvil, WindowProperty}, }; @@ -46,22 +43,22 @@ impl AnvilScreenHandler { handler } - pub async fn update_item_name(&mut self, name: String) { + pub fn update_item_name(&mut self, name: String) { self.rename_text = name; - self.update_result_slot().await; - self.send_content_updates().await; + self.update_result_slot(); + self.send_content_updates(); } - pub async fn update_result_slot(&mut self) { - let input_a = self.inventory.get_stack(0).await; + pub fn update_result_slot(&mut self) { + let input_a = self.inventory.get_stack(0); if input_a.is_empty() { - self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await; - self.set_repair_cost(0).await; + self.inventory.set_stack(2, ItemStack::EMPTY.clone()); + self.set_repair_cost(0); return; } - let mut result_item = input_a.clone(); + let mut result_item = input_a; let mut cost = 0; // Basic renaming logic for now @@ -73,22 +70,24 @@ impl AnvilScreenHandler { // If combining with another item... we'll skip complex anvil logic for now // and just support renaming. if cost > 0 { - self.inventory.set_stack(2, result_item).await; - self.set_repair_cost(cost).await; + self.inventory.set_stack(2, result_item); + self.set_repair_cost(cost); } else { - self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await; - self.set_repair_cost(0).await; + self.inventory.set_stack(2, ItemStack::EMPTY.clone()); + self.set_repair_cost(0); } } - pub async fn set_repair_cost(&mut self, cost: i16) { + pub fn set_repair_cost(&mut self, cost: i16) { self.repair_cost = cost; if let Some(sync_handler) = self.behaviour.sync_handler.as_ref() { let (property_id, property_value) = WindowProperty::new(Anvil::RepairCost, cost).into_tuple(); - sync_handler - .update_property(&self.behaviour, property_id as i32, property_value as i32) - .await; + sync_handler.update_property( + &self.behaviour, + property_id as i32, + property_value as i32, + ); } } } @@ -110,104 +109,88 @@ impl ScreenHandler for AnvilScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - // Drop inputs from anvil - for i in 0..2 { - let stack = self.inventory.remove_stack(i).await; - if !stack.is_empty() { - offer_or_drop_stack(player, stack).await; - } + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); + // Drop inputs from anvil + for i in 0..2 { + let stack = self.inventory.remove_stack(i); + if !stack.is_empty() { + offer_or_drop_stack(player, stack); } - self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await; - }) + } + self.inventory.set_stack(2, ItemStack::EMPTY.clone()); } - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - stack_left = slot_stack.clone(); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + stack_left = slot_stack.clone(); - if slot_index < 3 { - // From anvil to player - if !self.insert_item(&mut slot_stack, 3, 39, true).await { - return ItemStack::EMPTY.clone(); - } - slot.on_quick_move_crafted(slot_stack.clone(), stack_left.clone()) - .await; - } else { - // From player to anvil - if !self.insert_item(&mut slot_stack, 0, 2, false).await { - return ItemStack::EMPTY.clone(); - } - } - - if slot_stack.item_count == stack_left.item_count { + if slot_index < 3 { + // From anvil to player + if !self.insert_item(&mut slot_stack, 3, 39, true) { + return ItemStack::EMPTY.clone(); + } + slot.on_quick_move_crafted(slot_stack.clone(), stack_left.clone()); + } else { + // From player to anvil + if !self.insert_item(&mut slot_stack, 0, 2, false) { return ItemStack::EMPTY.clone(); } - - slot.set_stack_prev(slot_stack.clone(), stack_left.clone()) - .await; - slot.on_take_item(player, &slot_stack).await; - slot.mark_dirty().await; } - stack_left - }) + if slot_stack.item_count == stack_left.item_count { + return ItemStack::EMPTY.clone(); + } + + slot.set_stack_prev(slot_stack.clone(), stack_left.clone()); + slot.on_take_item(player, &slot_stack); + slot.mark_dirty(); + } + + stack_left } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: pumpkin_protocol::java::server::play::SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - if slot_index == 2 { - // Taking from output slot - let result_slot = self.get_behaviour().slots[2].clone(); - if result_slot.has_stack().await { - let result_stack = result_slot.get_cloned_stack().await; - if !result_stack.is_empty() { - if player.experience_level() >= self.repair_cost as i32 - || player.is_creative() - { - // Consume experience - if !player.is_creative() { - player - .add_experience_levels(-(self.repair_cost as i32)) - .await; - } - - // Consume inputs - self.inventory.set_stack(0, ItemStack::EMPTY.clone()).await; - self.get_behaviour().slots[0].mark_dirty().await; - } else { - // Cancel click - self.send_content_updates().await; - return; + player: &dyn InventoryPlayer, + ) { + if slot_index == 2 { + // Taking from output slot + let result_slot = self.get_behaviour().slots[2].clone(); + if result_slot.has_stack() { + let result_stack = result_slot.get_cloned_stack(); + if !result_stack.is_empty() { + if player.experience_level() >= self.repair_cost as i32 || player.is_creative() + { + // Consume experience + if !player.is_creative() { + player.add_experience_levels(-(self.repair_cost as i32)); } + + // Consume inputs + self.inventory.set_stack(0, ItemStack::EMPTY.clone()); + self.get_behaviour().slots[0].mark_dirty(); + } else { + // Cancel click + self.send_content_updates(); + return; } } } + } - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - if slot_index == 0 || slot_index == 1 || slot_index == 2 { - self.update_result_slot().await; - self.send_content_updates().await; - } - }) + self.internal_on_slot_click(slot_index, button, action_type, player); + if slot_index == 0 || slot_index == 1 || slot_index == 2 { + self.update_result_slot(); + self.send_content_updates(); + } } } diff --git a/crates/pumpkin-inventory/src/beacon_screen_handler.rs b/crates/pumpkin-inventory/src/beacon_screen_handler.rs index be5498b92..f8617ad94 100644 --- a/crates/pumpkin-inventory/src/beacon_screen_handler.rs +++ b/crates/pumpkin-inventory/src/beacon_screen_handler.rs @@ -5,22 +5,19 @@ use pumpkin_world::inventory::Inventory; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, - }, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, slot::NormalSlot, }; /// Creates a beacon container screen handler. /// /// Beacons feature a single payment slot and a specialized UI for selecting status effects. -pub async fn create_beacon_handler( +pub fn create_beacon_handler( sync_id: u8, player_inventory: &Arc, inventory: Arc, ) -> BeaconScreenHandler { - BeaconScreenHandler::new(sync_id, player_inventory, inventory).await + BeaconScreenHandler::new(sync_id, player_inventory, inventory) } /// Screen handler specifically for Beacon blocks. @@ -33,17 +30,17 @@ pub struct BeaconScreenHandler { impl BeaconScreenHandler { /// Creates a new beacon screen handler. - async fn new( + fn new( sync_id: u8, player_inventory: &Arc, inventory: Arc, ) -> Self { let mut handler = Self { - inventory: inventory.clone(), + inventory, behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Beacon)), }; - inventory.on_open().await; + handler.inventory.on_open(); // Add the single payment slot for the beacon (slot 0) handler.add_slot(Arc::new(NormalSlot::new(handler.inventory.clone(), 0))); @@ -73,58 +70,47 @@ impl ScreenHandler for BeaconScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); } /// Quick move logic specifically for the beacon UI. /// /// - From beacon payment slot (0): Move to player inventory /// - From player inventory (1+): Move to beacon payment slot - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - stack_left = slot_stack.clone(); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + stack_left = slot_stack.clone(); - if slot_index == 0 { - // Move from the single beacon slot to the player inventory (slots 1 to end) - if !self - .insert_item( - &mut slot_stack, - 1, - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else { - // Move from player inventory into the beacon payment slot (slot 0) - if !self.insert_item(&mut slot_stack, 0, 1, false).await { - return ItemStack::EMPTY.clone(); - } + if slot_index == 0 { + // Move from the single beacon slot to the player inventory (slots 1 to end) + if !self.insert_item( + &mut slot_stack, + 1, + self.get_behaviour().slots.len() as i32, + true, + ) { + return ItemStack::EMPTY.clone(); } - - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; + } else { + // Move from player inventory into the beacon payment slot (slot 0) + if !self.insert_item(&mut slot_stack, 0, 1, false) { + return ItemStack::EMPTY.clone(); } } - stack_left - }) + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); + } + } + + stack_left } } diff --git a/crates/pumpkin-inventory/src/brewing/brewing_screen_handler.rs b/crates/pumpkin-inventory/src/brewing/brewing_screen_handler.rs index 5e21526a9..75e5012eb 100644 --- a/crates/pumpkin-inventory/src/brewing/brewing_screen_handler.rs +++ b/crates/pumpkin-inventory/src/brewing/brewing_screen_handler.rs @@ -10,7 +10,7 @@ //! - Brew time (0-400): Progress of the current brewing operation //! - Fuel time (0-20): Amount of fuel remaining -use std::{any::Any, pin::Pin, sync::Arc}; +use std::{any::Any, sync::Arc}; use pumpkin_data::tag::Taggable; use pumpkin_data::{screen::WindowType, tag}; @@ -18,7 +18,7 @@ use pumpkin_world::{block::entities::PropertyDelegate, inventory::Inventory}; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, ScreenProperty}, + screen_handler::{ScreenHandler, ScreenHandlerBehaviour, ScreenProperty}, }; use pumpkin_data::item_stack::ItemStack; @@ -46,27 +46,23 @@ impl BrewingScreenHandler { /// - `player_inventory` - The player's inventory /// - `inventory` - The brewing stand's inventory (5 slots) /// - `property_delegate` - Delegate for accessing brew time and fuel properties - pub async fn new( + pub fn new( sync_id: u8, - player_inventory: Arc, + player_inventory: &Arc, inventory: Arc, - property_delegate: Arc, + property_delegate: &Arc, ) -> Self { struct BrewingScreenListener; impl crate::screen_handler::ScreenHandlerListener for BrewingScreenListener { - fn on_property_update<'a>( - &'a self, - screen_handler: &'a ScreenHandlerBehaviour, + fn on_property_update( + &self, + screen_handler: &ScreenHandlerBehaviour, property: u8, value: i32, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { - sync_handler - .update_property(screen_handler, i32::from(property), value) - .await; - } - }) + ) { + if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { + sync_handler.update_property(screen_handler, i32::from(property), value); + } } } @@ -80,7 +76,7 @@ impl BrewingScreenHandler { handler.add_property(ScreenProperty::new(property_delegate.clone(), 0)); handler.add_property(ScreenProperty::new(property_delegate.clone(), 1)); - handler.add_listener(Arc::new(BrewingScreenListener)).await; + handler.add_listener(Arc::new(BrewingScreenListener)); // Add all 5 brewing stand slots: 0-2 = potions, 3 = ingredient, 4 = fuel for i in 0..5 { @@ -115,11 +111,8 @@ impl ScreenHandler for BrewingScreenHandler { &mut self.behaviour } - fn on_closed<'a>( - &'a mut self, - player: &'a dyn crate::screen_handler::InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { self.default_on_closed(player).await }) + fn on_closed(&mut self, player: &dyn crate::screen_handler::InventoryPlayer) { + self.default_on_closed(player); } /// Quick move logic for brewing stand. @@ -128,76 +121,73 @@ impl ScreenHandler for BrewingScreenHandler { /// - Ingredient (3) -> Player inventory /// - Fuel (4) -> Player inventory /// - From player: potions -> potion slots, fuel -> fuel slot, else -> ingredient slot - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn crate::screen_handler::InventoryPlayer, + fn quick_move( + &mut self, + _player: &dyn crate::screen_handler::InventoryPlayer, slot_index: i32, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); + ) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if !slot.has_stack().await { - return stack_left; - } + if !slot.has_stack() { + return stack_left; + } - let mut stack = slot.get_stack().await; - stack_left = stack.clone(); + let mut stack = slot.get_stack(); + stack_left = stack.clone(); - let success = if slot_index < 5 { - // Moving from brewing stand to player inventory - self.insert_item(&mut stack, 5, self.get_behaviour().slots.len() as i32, true) - .await + let success = if slot_index < 5 { + // Moving from brewing stand to player inventory + self.insert_item(&mut stack, 5, self.get_behaviour().slots.len() as i32, true) + } else { + // Moving from player inventory to brewing stand + // Check item type to determine target slot + + // Check if item has potion contents (for slots 0-2) + let has_potion_contents = stack + .get_data_component::() + .is_some(); + + // Check if item is brewing fuel (for slot 4) + let is_fuel = stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL); + + if has_potion_contents { + // Try to insert into potion slots (0-2) + self.insert_item(&mut stack, 0, 3, false) + } else if is_fuel { + // Try to insert into fuel slot (4) + self.insert_item(&mut stack, 4, 5, false) } else { - // Moving from player inventory to brewing stand - // Check item type to determine target slot - - // Check if item has potion contents (for slots 0-2) - let has_potion_contents = stack - .get_data_component::() - .is_some(); - - // Check if item is brewing fuel (for slot 4) - let is_fuel = stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL); - - if has_potion_contents { - // Try to insert into potion slots (0-2) - self.insert_item(&mut stack, 0, 3, false).await - } else if is_fuel { - // Try to insert into fuel slot (4) - self.insert_item(&mut stack, 4, 5, false).await - } else { - // Try to insert into ingredient slot (3) - self.insert_item(&mut stack, 3, 4, false).await - } - }; - - if !success { - return ItemStack::EMPTY.clone(); + // Try to insert into ingredient slot (3) + self.insert_item(&mut stack, 3, 4, false) } + }; - if stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(stack).await; - } + if !success { + return ItemStack::EMPTY.clone(); + } - stack_left - }) + if stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(stack); + } + + stack_left } } /// Creates a new brewing stand screen handler. /// /// Factory function used by the server when a player opens a brewing stand. -pub async fn create_brewing( +pub fn create_brewing( sync_id: u8, - player_inventory: Arc, + player_inventory: &Arc, inventory: Arc, - property_delegate: Arc, -) -> Option { + property_delegate: &Arc, +) -> Option { let handler = - BrewingScreenHandler::new(sync_id, player_inventory, inventory, property_delegate).await; + BrewingScreenHandler::new(sync_id, player_inventory, inventory, property_delegate); Some(handler) } diff --git a/crates/pumpkin-inventory/src/cartography_table_screen_handler.rs b/crates/pumpkin-inventory/src/cartography_table_screen_handler.rs index cbf01b9a9..1cb129854 100644 --- a/crates/pumpkin-inventory/src/cartography_table_screen_handler.rs +++ b/crates/pumpkin-inventory/src/cartography_table_screen_handler.rs @@ -2,9 +2,7 @@ use std::any::Any; use std::sync::Arc; use crate::player::player_inventory::PlayerInventory; -use crate::screen_handler::{ - InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; +use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use crate::slot::NormalSlot; use pumpkin_data::item_stack::ItemStack; @@ -66,48 +64,39 @@ impl ScreenHandler for CartographyTableScreenHandler { self } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ScreenHandlerFuture<'a, ItemStack> { - Box::pin(async move { - let mut stack = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); - if let Some(slot) = slot { - let mut slot_stack = slot.get_cloned_stack().await; - if !slot_stack.is_empty() { - stack = slot_stack.clone(); - if slot_index < 3 { - if !self.insert_item(&mut slot_stack, 3, 39, true).await { - return ItemStack::EMPTY.clone(); - } - } else if !self.insert_item(&mut slot_stack, 0, 2, false).await { + if let Some(slot) = slot { + let mut slot_stack = slot.get_cloned_stack(); + if !slot_stack.is_empty() { + stack = slot_stack.clone(); + if slot_index < 3 { + if !self.insert_item(&mut slot_stack, 3, 39, true) { return ItemStack::EMPTY.clone(); } + } else if !self.insert_item(&mut slot_stack, 0, 2, false) { + return ItemStack::EMPTY.clone(); + } - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; - } + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); } } - stack - }) + } + stack } } diff --git a/crates/pumpkin-inventory/src/crafting/crafting_inventory.rs b/crates/pumpkin-inventory/src/crafting/crafting_inventory.rs index 1a09f4d67..8a05e142d 100644 --- a/crates/pumpkin-inventory/src/crafting/crafting_inventory.rs +++ b/crates/pumpkin-inventory/src/crafting/crafting_inventory.rs @@ -9,12 +9,11 @@ //! Unlike regular inventories, crafting grids are typically cleared when //! the container closes, and their contents are used up when crafting. -use std::{any::Any, pin::Pin}; +use std::any::Any; +use std::sync::RwLock; use pumpkin_data::item_stack::ItemStack; -use tokio::sync::RwLock; - -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; use super::recipes::RecipeInputInventory; @@ -71,52 +70,57 @@ impl Inventory for CraftingInventory { (self.width as usize) * (self.height as usize) } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items - .get(slot) - .cloned() - .unwrap_or_else(|| ItemStack::EMPTY.clone()) - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items + .get(slot) + .cloned() + .unwrap_or_else(|| ItemStack::EMPTY.clone()) } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - if slot < items.len() { - std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < items.len() { + std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - if slot < items.len() && !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < items.len() && !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - if slot < items.len() { - items[slot] = stack; - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < items.len() { + items[slot] = stack; + } } fn as_any(&self) -> &dyn Any { @@ -135,10 +139,11 @@ impl RecipeInputInventory for CraftingInventory { } impl Clearable for CraftingInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); } } diff --git a/crates/pumpkin-inventory/src/crafting/crafting_screen_handler.rs b/crates/pumpkin-inventory/src/crafting/crafting_screen_handler.rs index a0837b453..5f22a24b3 100644 --- a/crates/pumpkin-inventory/src/crafting/crafting_screen_handler.rs +++ b/crates/pumpkin-inventory/src/crafting/crafting_screen_handler.rs @@ -15,19 +15,17 @@ //! - Special recipes (like decorated pots) use std::any::Any; -use std::pin::Pin; -use std::sync::Arc; use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::{Arc, Mutex}; use super::recipe_provider::{GenericRecipe, RecipeProvider}; use super::recipes::{RecipeFinderScreenHandler, RecipeInputInventory}; use crate::crafting::crafting_inventory::CraftingInventory; use crate::player::player_inventory::PlayerInventory; use crate::screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, - ScreenHandlerListener, + InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener, }; -use crate::slot::{BoxFuture, NormalSlot, Slot}; +use crate::slot::{NormalSlot, Slot}; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::recipes::{CraftingRecipeTypes, RECIPES_CRAFTING}; @@ -37,7 +35,6 @@ use pumpkin_data::tag; use pumpkin_data::tag::Taggable; use pumpkin_protocol::codec::recipe::{DynamicRecipe, OwnedCraftingRecipe}; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; /// The result slot in a crafting screen. pub struct ResultSlot { @@ -74,7 +71,7 @@ fn is_symmetrical_horizontally(pattern: &[&str]) -> bool { /// Checks if a crafting recipe matches the current inventory state. #[expect(clippy::too_many_lines)] -async fn recipe_matches( +fn recipe_matches( recipe: GenericRecipe<'_>, input_height: usize, input_width: usize, @@ -113,8 +110,7 @@ async fn recipe_matches( 'outer: for (y, row_str) in pattern.iter().enumerate() { for (x, current_key) in row_str.chars().enumerate() { let slot = inventory - .get_stack((y + y_offset) * inventory.get_width() + (x + x_offset)) - .await; + .get_stack((y + y_offset) * inventory.get_width() + (x + x_offset)); if current_key == ' ' { if !slot.is_empty() { matched = false; @@ -146,12 +142,10 @@ async fn recipe_matches( matched = false; break 'outer; }; - let slot = inventory - .get_stack( - (y + y_offset) * inventory.get_height() - + (x_offset + input_width - 1 - x), - ) - .await; + let slot = inventory.get_stack( + (y + y_offset) * inventory.get_height() + + (x_offset + input_width - 1 - x), + ); if current_key == ' ' { if !slot.is_empty() { matched = false; @@ -189,7 +183,7 @@ async fn recipe_matches( } let mut ingredient_used = vec![false; ingredients.len()]; 'next_slot: for i in 0..inventory.size() { - let slot = inventory.get_stack(i).await; + let slot = inventory.get_stack(i); if slot.is_empty() { continue 'next_slot; } @@ -216,7 +210,7 @@ async fn recipe_matches( return None; } 'item_stack: for i in 0..inventory.size() { - let slot = inventory.get_stack(i).await; + let slot = inventory.get_stack(i); if slot.is_empty() { continue 'item_stack; } @@ -234,7 +228,7 @@ async fn recipe_matches( return None; } for position in (1..=7).step_by(2) { - let slot = inventory.get_stack(position).await; + let slot = inventory.get_stack(position); if slot.is_empty() || !slot .item @@ -274,8 +268,7 @@ async fn recipe_matches( 'outer: for (y, row_str) in pattern.iter().enumerate() { for (x, current_key) in row_str.chars().enumerate() { let slot = inventory - .get_stack((y + y_offset) * inventory.get_width() + (x + x_offset)) - .await; + .get_stack((y + y_offset) * inventory.get_width() + (x + x_offset)); if current_key == ' ' { if !slot.is_empty() { matched = false; @@ -310,7 +303,7 @@ async fn recipe_matches( } let mut ingredient_used = vec![false; ingredients.len()]; 'next_slot: for i in 0..inventory.size() { - let slot = inventory.get_stack(i).await; + let slot = inventory.get_stack(i); if slot.is_empty() { continue 'next_slot; } @@ -344,7 +337,7 @@ impl ResultSlot { } } - async fn match_recipe(&self) -> Option { + fn match_recipe(&self) -> Option { let mut count: usize = 0; let inventory_width = self.inventory.get_width(); let mut top_x = 9; @@ -354,7 +347,7 @@ impl ResultSlot { for i in 0..self.inventory.size() { let x = i % inventory_width; let y = i / inventory_width; - let slot = self.inventory.get_stack(i).await; + let slot = self.inventory.get_stack(i); if !slot.is_empty() { top_x = top_x.min(x); top_y = top_y.min(y); @@ -378,15 +371,13 @@ impl ResultSlot { top_y, count, &*self.inventory, - ) - .await - { + ) { return Some(result); } } if let Some(provider) = &self.recipe_provider { - let dynamic = provider.get_dynamic_recipes().await; + let dynamic = provider.get_dynamic_recipes(); for recipe in &dynamic { if let DynamicRecipe::Crafting(crafting) = recipe && let Some(result) = recipe_matches( @@ -398,7 +389,6 @@ impl ResultSlot { count, &*self.inventory, ) - .await { return Some(result); } @@ -407,8 +397,8 @@ impl ResultSlot { None } - async fn refill_output(&self) -> ItemStack { - let result = if let Some(matched) = self.match_recipe().await { + fn refill_output(&self) -> ItemStack { + let result = if let Some(matched) = self.match_recipe() { let key = matched .item_id .strip_prefix("minecraft:") @@ -419,7 +409,10 @@ impl ResultSlot { } else { ItemStack::EMPTY.clone() }; - *self.result.lock().await = result.clone(); + *self + .result + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = result.clone(); result } } @@ -434,128 +427,106 @@ impl Slot for ResultSlot { fn set_id(&self, id: usize) { self.id.store(id as u8, Ordering::Relaxed); } - fn on_quick_move_crafted( - &self, - _stack: ItemStack, - _stack_prev: ItemStack, - ) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.refill_output().await; - }) + fn on_quick_move_crafted(&self, _stack: ItemStack, _stack_prev: ItemStack) { + self.refill_output(); } - fn on_take_item<'a>( - &'a self, - player: &'a dyn InventoryPlayer, - stack: &'a ItemStack, - ) -> BoxFuture<'a, ()> { - Box::pin(async move { - player - .increment_stat( - StatisticCategory::Crafted, - stack.item.id as i32, - stack.item_count as i32, - ) - .await; - for i in 0..self.inventory.size() { - self.inventory.remove_stack_specific(i, 1).await; + fn on_take_item(&self, player: &dyn InventoryPlayer, stack: &ItemStack) { + player.increment_stat( + StatisticCategory::Crafted, + stack.item.id as i32, + stack.item_count as i32, + ); + for i in 0..self.inventory.size() { + self.inventory.remove_stack_specific(i, 1); + } + self.mark_dirty(); + } + fn can_insert(&self, _stack: &ItemStack) -> bool { + false + } + fn get_stack(&self) -> ItemStack { + self.result + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + fn get_cloned_stack(&self) -> ItemStack { + self.result + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + fn has_stack(&self) -> bool { + !self + .result + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() + } + fn set_stack(&self, _stack: ItemStack) { + self.refill_output(); + } + fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) { + self.refill_output(); + } + fn mark_dirty(&self) { + self.inventory.mark_dirty(); + } + fn get_max_item_count(&self) -> u8 { + let mut count = u8::MAX; + for i in 0..self.inventory.size() { + let slot = self.inventory.get_stack(i); + if !slot.is_empty() { + count = count.min(slot.item_count); } - self.mark_dirty().await; - }) + } + count } - fn can_insert(&self, _stack: &ItemStack) -> BoxFuture<'_, bool> { - Box::pin(async move { false }) - } - fn get_stack(&self) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { self.result.lock().await.clone() }) - } - fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { self.result.lock().await.clone() }) - } - fn has_stack(&self) -> BoxFuture<'_, bool> { - Box::pin(async move { !self.result.lock().await.is_empty() }) - } - fn set_stack(&self, _stack: ItemStack) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.refill_output().await; - }) - } - fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.refill_output().await; - }) - } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) - } - fn get_max_item_count(&self) -> BoxFuture<'_, u8> { - Box::pin(async move { - let mut count = u8::MAX; - for i in 0..self.inventory.size() { - let slot = self.inventory.get_stack(i).await; - if !slot.is_empty() { - count = count.min(slot.item_count); - } - } - count - }) - } - fn take_stack(&self, _amount: u8) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { - if self.has_stack().await { - self.result.lock().await.clone() - } else { - ItemStack::EMPTY.clone() - } - }) + fn take_stack(&self, _amount: u8) -> ItemStack { + if self.has_stack() { + self.result + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } else { + ItemStack::EMPTY.clone() + } } } impl ScreenHandlerListener for ResultSlot { - fn on_slot_update<'a>( - &'a self, - screen_handler: &'a ScreenHandlerBehaviour, - slot: u8, - _stack: ItemStack, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if (0..=(self.inventory.get_width() * self.inventory.get_height())) - .contains(&(slot as usize)) - { - let result = self.refill_output().await; - let next_revision = screen_handler.next_revision(); - if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { - sync_handler - .update_slot(screen_handler, 0, &result, next_revision) - .await; - } + fn on_slot_update(&self, screen_handler: &ScreenHandlerBehaviour, slot: u8, _stack: ItemStack) { + if (0..=(self.inventory.get_width() * self.inventory.get_height())) + .contains(&(slot as usize)) + { + let result = self.refill_output(); + let next_revision = screen_handler.next_revision(); + if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { + sync_handler.update_slot(screen_handler, 0, &result, next_revision); } - }) + } } } pub trait CraftingScreenHandler: RecipeFinderScreenHandler + ScreenHandler { - fn add_recipe_slots<'a>( - &'a mut self, + fn add_recipe_slots( + &mut self, crafing_inventory: Arc, provider: Option>, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone(), provider)); - self.add_slot(result_slot.clone()); - let width = crafing_inventory.get_width(); - let height = crafing_inventory.get_height(); - for i in 0..width { - for j in 0..height { - let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width); - self.add_slot(Arc::new(input_slot)); - } + ) { + let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone(), provider)); + self.add_slot(result_slot.clone()); + let width = crafing_inventory.get_width(); + let height = crafing_inventory.get_height(); + for i in 0..width { + for j in 0..height { + let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width); + self.add_slot(Arc::new(input_slot)); } - self.add_listener(result_slot).await; - }) + } + self.add_listener(result_slot); } } @@ -565,7 +536,7 @@ pub struct CraftingTableScreenHandler { } impl CraftingTableScreenHandler { - pub async fn new( + pub fn new( sync_id: u8, player_inventory: &Arc, provider: Option>, @@ -576,9 +547,7 @@ impl CraftingTableScreenHandler { behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Crafting)), crafting_inventory: crafting_inventory.clone(), }; - crafting_table_handler - .add_recipe_slots(crafting_inventory, provider) - .await; + crafting_table_handler.add_recipe_slots(crafting_inventory, provider); let player_inventory: Arc = player_inventory.clone(); crafting_table_handler.add_player_slots(&player_inventory); crafting_table_handler @@ -600,71 +569,60 @@ impl ScreenHandler for CraftingTableScreenHandler { fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.drop_inventory(player, self.crafting_inventory.clone()) - .await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.drop_inventory(player, self.crafting_inventory.clone()); } - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - let stack_prev = slot_stack.clone(); - if slot_index == 0 { - if !self.insert_item(&mut slot_stack, 10, 46, true).await { - return ItemStack::EMPTY.clone(); - } - } else if (1..=9).contains(&slot_index) { - if !self.insert_item(&mut slot_stack, 10, 46, false).await { - return ItemStack::EMPTY.clone(); - } - } else if (10..46).contains(&slot_index) { - if !self.insert_item(&mut slot_stack, 1, 10, false).await { - if slot_index < 37 { - if !self.insert_item(&mut slot_stack, 37, 46, false).await { - return ItemStack::EMPTY.clone(); - } - } else if !self.insert_item(&mut slot_stack, 10, 37, false).await { + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + let stack_prev = slot_stack.clone(); + if slot_index == 0 { + if !self.insert_item(&mut slot_stack, 10, 46, true) { + return ItemStack::EMPTY.clone(); + } + } else if (1..=9).contains(&slot_index) { + if !self.insert_item(&mut slot_stack, 10, 46, false) { + return ItemStack::EMPTY.clone(); + } + } else if (10..46).contains(&slot_index) { + if !self.insert_item(&mut slot_stack, 1, 10, false) { + if slot_index < 37 { + if !self.insert_item(&mut slot_stack, 37, 46, false) { return ItemStack::EMPTY.clone(); } - } - } else if !self.insert_item(&mut slot_stack, 10, 46, false).await { - return ItemStack::EMPTY.clone(); - } - let stack = slot_stack.clone(); - drop(slot_stack); - if stack.is_empty() { - slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()) - .await; - } else { - slot.mark_dirty().await; - } - if stack.item_count == stack_prev.item_count { - return ItemStack::EMPTY.clone(); - } - - let mut taken_stack = stack_prev.clone(); - taken_stack.set_count(stack_prev.item_count - stack.item_count); - slot.on_take_item(player, &taken_stack).await; - - if slot_index == 0 { - slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()) - .await; - if !stack.is_empty() { - player.drop_item(stack, false).await; + } else if !self.insert_item(&mut slot_stack, 10, 37, false) { + return ItemStack::EMPTY.clone(); } } - return stack_prev; + } else if !self.insert_item(&mut slot_stack, 10, 46, false) { + return ItemStack::EMPTY.clone(); } - ItemStack::EMPTY.clone() - }) + let stack = slot_stack.clone(); + drop(slot_stack); + if stack.is_empty() { + slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()); + } else { + slot.mark_dirty(); + } + if stack.item_count == stack_prev.item_count { + return ItemStack::EMPTY.clone(); + } + + let mut taken_stack = stack_prev.clone(); + taken_stack.set_count(stack_prev.item_count - stack.item_count); + slot.on_take_item(player, &taken_stack); + + if slot_index == 0 { + slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()); + if !stack.is_empty() { + player.drop_item(stack, false); + } + } + return stack_prev; + } + ItemStack::EMPTY.clone() } } diff --git a/crates/pumpkin-inventory/src/crafting/recipe_provider.rs b/crates/pumpkin-inventory/src/crafting/recipe_provider.rs index 5a0224677..addaff484 100644 --- a/crates/pumpkin-inventory/src/crafting/recipe_provider.rs +++ b/crates/pumpkin-inventory/src/crafting/recipe_provider.rs @@ -1,15 +1,16 @@ -use crate::slot::BoxFuture; use pumpkin_protocol::codec::recipe::DynamicRecipe; pub trait RecipeProvider: Send + Sync { - fn get_dynamic_recipes(&self) -> BoxFuture<'_, Vec>; + fn get_dynamic_recipes(&self) -> Vec; } +#[derive(Clone, Copy)] pub enum GenericRecipe<'a> { Vanilla(&'a pumpkin_data::recipes::CraftingRecipeTypes), Dynamic(&'a pumpkin_protocol::codec::recipe::OwnedCraftingRecipe), } +#[derive(Clone, Copy)] pub enum IngredientRef<'a> { Vanilla(&'a pumpkin_data::recipes::RecipeIngredientTypes), Dynamic(&'a pumpkin_protocol::codec::recipe::OwnedRecipeIngredient), diff --git a/crates/pumpkin-inventory/src/double.rs b/crates/pumpkin-inventory/src/double.rs index 4ab19cd45..6ed1edef9 100644 --- a/crates/pumpkin-inventory/src/double.rs +++ b/crates/pumpkin-inventory/src/double.rs @@ -8,10 +8,10 @@ //! slots. Operations are delegated to the appropriate underlying inventory //! based on the slot index. -use std::{any::Any, pin::Pin, sync::Arc}; +use std::{any::Any, sync::Arc}; use pumpkin_data::item_stack::ItemStack; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; /// A composite inventory combining two inventories. /// @@ -44,64 +44,51 @@ impl Inventory for DoubleInventory { self.first.size() + self.second.size() } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { self.first.is_empty().await && self.second.is_empty().await }) + fn is_empty(&self) -> bool { + self.first.is_empty() && self.second.is_empty() } - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot >= self.first.size() { - self.second.get_stack(slot - self.first.size()).await - } else { - self.first.get_stack(slot).await - } - }) + fn get_stack(&self, slot: usize) -> ItemStack { + if slot >= self.first.size() { + self.second.get_stack(slot - self.first.size()) + } else { + self.first.get_stack(slot) + } } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot >= self.first.size() { - self.second.remove_stack(slot - self.first.size()).await - } else { - self.first.remove_stack(slot).await - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + if slot >= self.first.size() { + self.second.remove_stack(slot - self.first.size()) + } else { + self.first.remove_stack(slot) + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot >= self.first.size() { - self.second - .remove_stack_specific(slot - self.first.size(), amount) - .await - } else { - self.first.remove_stack_specific(slot, amount).await - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + if slot >= self.first.size() { + self.second + .remove_stack_specific(slot - self.first.size(), amount) + } else { + self.first.remove_stack_specific(slot, amount) + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - if slot >= self.first.size() { - self.second.set_stack(slot - self.first.size(), stack).await; - } else { - self.first.set_stack(slot, stack).await; - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + if slot >= self.first.size() { + self.second.set_stack(slot - self.first.size(), stack); + } else { + self.first.set_stack(slot, stack); + } } - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.first.on_open().await; - self.second.on_open().await; - }) + fn on_open(&self) { + self.first.on_open(); + self.second.on_open(); } - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.first.on_close().await; - self.second.on_close().await; - }) + fn on_close(&self) { + self.first.on_close(); + self.second.on_close(); } fn get_max_count_per_stack(&self) -> u8 { @@ -128,10 +115,8 @@ impl Inventory for DoubleInventory { } impl Clearable for DoubleInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - self.first.clear().await; - self.second.clear().await; - }) + fn clear(&self) { + self.first.clear(); + self.second.clear(); } } diff --git a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs index 6bfca91b0..73f29dc65 100644 --- a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs +++ b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs @@ -13,11 +13,8 @@ use pumpkin_world::inventory::Inventory; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, offer_or_drop_stack, - }, - slot::{BoxFuture, NormalSlot, Slot}, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, offer_or_drop_stack}, + slot::{NormalSlot, Slot}, window_property::{EnchantmentTable, WindowProperty}, }; @@ -46,12 +43,12 @@ impl Slot for LapisSlot { self.0.set_id(id); } - fn can_insert<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, bool> { - Box::pin(async move { is_lapis(stack) }) + fn can_insert(&self, stack: &ItemStack) -> bool { + is_lapis(stack) } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - self.0.mark_dirty() + fn mark_dirty(&self) { + self.0.mark_dirty(); } } @@ -93,8 +90,8 @@ impl EnchantingTableScreenHandler { handler } - pub async fn update_enchantments(&mut self, player: &dyn InventoryPlayer) { - let item = self.inventory.get_stack(0).await; + pub fn update_enchantments(&mut self, player: &dyn InventoryPlayer) { + let item = self.inventory.get_stack(0); if item.is_empty() || item.has_enchantments() { for i in 0..3 { @@ -146,16 +143,13 @@ impl EnchantingTableScreenHandler { } } - if player - .fire_prepare_item_enchant_event( - &item, - &mut self.level_requirements, - &mut self.enchantment_id, - &mut self.enchantment_level, - self.bookshelf_count, - ) - .await - { + if player.fire_prepare_item_enchant_event( + &item, + &mut self.level_requirements, + &mut self.enchantment_id, + &mut self.enchantment_level, + self.bookshelf_count, + ) { for i in 0..3 { self.level_requirements[i] = 0; self.enchantment_id[i] = -1; @@ -164,7 +158,7 @@ impl EnchantingTableScreenHandler { } } } - self.send_property_updates().await; + self.send_property_updates(); } fn calculate_level_requirement( @@ -276,7 +270,7 @@ impl EnchantingTableScreenHandler { result } - async fn send_property_updates(&self) { + fn send_property_updates(&self) { if let Some(sync_handler) = self.behaviour.sync_handler.as_ref() { for i in 0..3 { let (id, val) = WindowProperty::new( @@ -284,27 +278,21 @@ impl EnchantingTableScreenHandler { self.level_requirements[i] as i16, ) .into_tuple(); - sync_handler - .update_property(&self.behaviour, id as i32, val as i32) - .await; + sync_handler.update_property(&self.behaviour, id as i32, val as i32); let (id, val) = WindowProperty::new( EnchantmentTable::EnchantmentId { slot: i as u8 }, self.enchantment_id[i] as i16, ) .into_tuple(); - sync_handler - .update_property(&self.behaviour, id as i32, val as i32) - .await; + sync_handler.update_property(&self.behaviour, id as i32, val as i32); let (id, val) = WindowProperty::new( EnchantmentTable::EnchantmentLevel { slot: i as u8 }, self.enchantment_level[i] as i16, ) .into_tuple(); - sync_handler - .update_property(&self.behaviour, id as i32, val as i32) - .await; + sync_handler.update_property(&self.behaviour, id as i32, val as i32); } let (id, val) = WindowProperty::new( @@ -312,9 +300,7 @@ impl EnchantingTableScreenHandler { (self.enchantment_seed & 0xFFFF) as i16, ) .into_tuple(); - sync_handler - .update_property(&self.behaviour, id as i32, val as i32) - .await; + sync_handler.update_property(&self.behaviour, id as i32, val as i32); } } } @@ -336,165 +322,141 @@ impl ScreenHandler for EnchantingTableScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - // Return items to player - for i in 0..2 { - let stack = self.inventory.remove_stack(i).await; - if !stack.is_empty() { - offer_or_drop_stack(player, stack).await; + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); + // Return items to player + for i in 0..2 { + let stack = self.inventory.remove_stack(i); + if !stack.is_empty() { + offer_or_drop_stack(player, stack); + } + } + } + + fn on_button_click(&mut self, player: &dyn InventoryPlayer, id: i32) -> bool { + if !(0..3).contains(&id) { + return false; + } + + let level_req = self.level_requirements[id as usize]; + if player.experience_level() < level_req && !player.is_creative() { + return false; + } + + let mut lapis_stack = self.inventory.get_stack(1); + let lapis_cost = (id + 1) as u8; + + if !player.is_creative() + && (lapis_stack.is_empty() + || !is_lapis(&lapis_stack) + || lapis_stack.item_count < lapis_cost) + { + return false; + } + + // Perform enchantment + let mut item_stack = self.inventory.get_stack(0); + + if item_stack.is_empty() || item_stack.has_enchantments() { + return false; + } + + let mut random = self.create_enchantment_random(id as usize); + let mut enchantments = + Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req); + + if enchantments.is_empty() { + return false; + } + + if player.fire_enchant_item_event(&item_stack, id, level_req, &mut enchantments) + || enchantments.is_empty() + { + return false; + } + + if !player.is_creative() { + player.add_experience_levels(-(id + 1)); + lapis_stack.decrement(lapis_cost); + self.inventory.set_stack(1, lapis_stack); + } + + for (enchant, level) in enchantments { + item_stack.add_enchantment(enchant, level as u16); + } + self.inventory.set_stack(0, item_stack); + + // Update seed + player.set_enchantment_seed(rand::random()); + self.enchantment_seed = player.enchantment_seed(); + + self.update_enchantments(player); + self.send_content_updates(); + + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::EnchantItem as i32, + 1, + ); + + true + } + + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + stack_left = slot_stack.clone(); + + if slot_index < 2 { + // From enchanting to player + if !self.insert_item( + &mut slot_stack, + 2, + self.get_behaviour().slots.len() as i32, + true, + ) { + return ItemStack::EMPTY.clone(); } - } - }) - } - - fn on_button_click<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - id: i32, - ) -> ScreenHandlerFuture<'a, bool> { - Box::pin(async move { - if !(0..3).contains(&id) { - return false; - } - - let level_req = self.level_requirements[id as usize]; - if player.experience_level() < level_req && !player.is_creative() { - return false; - } - - let mut lapis_stack = self.inventory.get_stack(1).await; - let lapis_cost = (id + 1) as u8; - - if !player.is_creative() - && (lapis_stack.is_empty() - || !is_lapis(&lapis_stack) - || lapis_stack.item_count < lapis_cost) - { - return false; - } - - // Perform enchantment - let mut item_stack = self.inventory.get_stack(0).await; - - if item_stack.is_empty() || item_stack.has_enchantments() { - return false; - } - - let mut random = self.create_enchantment_random(id as usize); - let mut enchantments = - Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req); - - if enchantments.is_empty() { - return false; - } - - if player - .fire_enchant_item_event(&item_stack, id, level_req, &mut enchantments) - .await - || enchantments.is_empty() - { - return false; - } - - if !player.is_creative() { - player.add_experience_levels(-(id + 1)).await; - lapis_stack.decrement(lapis_cost); - self.inventory.set_stack(1, lapis_stack).await; - } - - for (enchant, level) in enchantments { - item_stack.add_enchantment(enchant, level as u16); - } - self.inventory.set_stack(0, item_stack).await; - - // Update seed - player.set_enchantment_seed(rand::random()).await; - self.enchantment_seed = player.enchantment_seed(); - - self.update_enchantments(player).await; - self.send_content_updates().await; - - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::EnchantItem as i32, - 1, - ) - .await; - - true - }) - } - - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, // FIX: Changed _player to player - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - stack_left = slot_stack.clone(); - - if slot_index < 2 { - // From enchanting to player - if !self - .insert_item( - &mut slot_stack, - 2, - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else { - // From player to enchanting - // Lapis check - if slot_stack.item == &Item::LAPIS_LAZULI { - if !self.insert_item(&mut slot_stack, 1, 2, false).await { - return ItemStack::EMPTY.clone(); - } - } else if !self.insert_item(&mut slot_stack, 0, 1, false).await { + } else { + // From player to enchanting + // Lapis check + if slot_stack.item == &Item::LAPIS_LAZULI { + if !self.insert_item(&mut slot_stack, 1, 2, false) { return ItemStack::EMPTY.clone(); } + } else if !self.insert_item(&mut slot_stack, 0, 1, false) { + return ItemStack::EMPTY.clone(); } - - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; - } - - // CRITICAL FIX: Ensure the client is notified when shift-clicking items into the slots - self.update_enchantments(player).await; } - stack_left - }) + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); + } + + // CRITICAL FIX: Ensure the client is notified when shift-clicking items into the slots + self.update_enchantments(player); + } + + stack_left } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: pumpkin_protocol::java::server::play::SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - if slot_index == 0 || slot_index == 1 { - self.update_enchantments(player).await; - } - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); + if slot_index == 0 || slot_index == 1 { + self.update_enchantments(player); + } } } diff --git a/crates/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs b/crates/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs index 912484ceb..392746410 100644 --- a/crates/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs +++ b/crates/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs @@ -16,7 +16,7 @@ //! - Property 2: Progress arrow (cooking/smelt time) //! - Property 3: Maximum progress (typically 200 ticks for furnace) -use std::{any::Any, pin::Pin, sync::Arc}; +use std::{any::Any, sync::Arc}; use pumpkin_data::{fuels::is_fuel, item_stack::ItemStack, screen::WindowType}; use pumpkin_world::{ @@ -27,8 +27,8 @@ use pumpkin_world::{ use crate::{ player::player_inventory::PlayerInventory, screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, ScreenHandlerListener, ScreenProperty, + InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener, + ScreenProperty, }, }; use tracing::debug; @@ -60,29 +60,25 @@ impl FurnaceLikeScreenHandler { /// - `property_delegate` - Delegate for accessing furnace properties /// - `experience_container` - Container that tracks smelting experience /// - `window_type` - The window type (Furnace, Smoker, or `BlastFurnace`) - pub async fn new( + pub fn new( sync_id: u8, player_inventory: &Arc, inventory: Arc, - property_delegate: Arc, + property_delegate: &Arc, experience_container: Arc, window_type: WindowType, ) -> Self { struct FurnaceLikeScreenListener; impl ScreenHandlerListener for FurnaceLikeScreenListener { - fn on_property_update<'a>( - &'a self, - screen_handler: &'a ScreenHandlerBehaviour, + fn on_property_update( + &self, + screen_handler: &ScreenHandlerBehaviour, property: u8, value: i32, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { - sync_handler - .update_property(screen_handler, i32::from(property), value) - .await; - } - }) + ) { + if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { + sync_handler.update_property(screen_handler, i32::from(property), value); + } } } let mut handler = Self { @@ -99,9 +95,7 @@ impl FurnaceLikeScreenHandler { handler.add_property(ScreenProperty::new(property_delegate.clone(), i)); } - handler - .add_listener(Arc::new(FurnaceLikeScreenListener)) - .await; + handler.add_listener(Arc::new(FurnaceLikeScreenListener)); handler.add_inventory_slots(); let player_inventory: Arc = player_inventory.clone(); handler.add_player_slots(&player_inventory); @@ -148,11 +142,9 @@ impl ScreenHandler for FurnaceLikeScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - // TODO: self.inventory.on_closed(player).await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + // TODO: self.inventory.on_closed(player); } /// Quick move logic for furnace-like containers. @@ -160,60 +152,53 @@ impl ScreenHandler for FurnaceLikeScreenHandler { /// - From furnace slots (0-2): Move to player inventory /// - Fuel items: Move to fuel slot (1) /// - Other items: Move to input slot (0) - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - const FUEL_SLOT: i32 = 1; // Note: Slots 0, 1, 2 are Furnace slots. - const OUTPUT_SLOT: i32 = 2; + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + const FUEL_SLOT: i32 = 1; // Note: Slots 0, 1, 2 are Furnace slots. + const OUTPUT_SLOT: i32 = 2; - debug!("FurnaceLikeScreenHandler::quick_move slot_index={slot_index}"); + debug!("FurnaceLikeScreenHandler::quick_move slot_index={slot_index}"); - let mut stack_left = ItemStack::EMPTY.clone(); + let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if !slot.has_stack().await { - return stack_left; - } + if !slot.has_stack() { + return stack_left; + } - let mut stack = slot.get_stack().await; - stack_left = stack.clone(); + let mut stack = slot.get_stack(); + stack_left = stack.clone(); - let success = if slot_index < 3 { - // If clicked slot is one of the Furnace slots (0, 1, 2): - // Try to move to player inventory (slots 3 onwards, starting from the end) - self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true) - .await - } else if is_fuel(stack.item.id) { - // If clicked slot is in the player inventory (3+) and contains fuel: - // Try to move to the Furnace's Fuel slot (slot 1) - self.insert_item(&mut stack, FUEL_SLOT, 3, false).await - } else { - // If clicked slot is in the player inventory (3+) and NOT fuel (must be a smeltable item): - // Try to move to the Furnace's Input/Smelting slot (slot 0) - self.insert_item(&mut stack, 0, 3, false).await - }; + let success = if slot_index < 3 { + // If clicked slot is one of the Furnace slots (0, 1, 2): + // Try to move to player inventory (slots 3 onwards, starting from the end) + self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true) + } else if is_fuel(stack.item.id) { + // If clicked slot is in the player inventory (3+) and contains fuel: + // Try to move to the Furnace's Fuel slot (slot 1) + self.insert_item(&mut stack, FUEL_SLOT, 3, false) + } else { + // If clicked slot is in the player inventory (3+) and NOT fuel (must be a smeltable item): + // Try to move to the Furnace's Input/Smelting slot (slot 0) + self.insert_item(&mut stack, 0, 3, false) + }; - if !success { - return ItemStack::EMPTY.clone(); - } + if !success { + return ItemStack::EMPTY.clone(); + } - if stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(stack).await; - } + if stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(stack); + } - // Award XP when taking from output slot (slot 2) - if slot_index == OUTPUT_SLOT { - debug!("quick_move: taking from output slot, calling on_take_item"); - slot.on_take_item(player, &stack_left).await; - } + // Award XP when taking from output slot (slot 2) + if slot_index == OUTPUT_SLOT { + debug!("quick_move: taking from output slot, calling on_take_item"); + slot.on_take_item(player, &stack_left); + } - stack_left - }) + stack_left } } diff --git a/crates/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs b/crates/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs index f66ba0846..8168421b7 100644 --- a/crates/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs +++ b/crates/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs @@ -13,10 +13,7 @@ use pumpkin_world::{block::entities::ExperienceContainer, inventory::Inventory}; use tracing::debug; -use crate::{ - screen_handler::InventoryPlayer, - slot::{BoxFuture, Slot}, -}; +use crate::{screen_handler::InventoryPlayer, slot::Slot}; /// Type of furnace slot. #[derive(Debug, Clone, Copy)] @@ -72,24 +69,17 @@ impl Slot for FurnaceLikeSlot { /// /// - Top slot: accepts any item (smeltables) /// - Bottom slot: only accepts fuel items and buckets - fn can_insert<'a>( - &'a self, - stack: &'a pumpkin_data::item_stack::ItemStack, - ) -> BoxFuture<'a, bool> { - Box::pin(async move { - match self.slot_type { - FurnaceLikeSlotType::Top => true, - FurnaceLikeSlotType::Bottom => { - is_fuel(stack.item.id) || stack.item.id == Item::BUCKET.id - } + fn can_insert(&self, stack: &pumpkin_data::item_stack::ItemStack) -> bool { + match self.slot_type { + FurnaceLikeSlotType::Top => true, + FurnaceLikeSlotType::Bottom => { + is_fuel(stack.item.id) || stack.item.id == Item::BUCKET.id } - }) + } } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } } @@ -137,43 +127,34 @@ impl Slot for FurnaceOutputSlot { } /// Awards experience when items are taken from this slot. - fn on_take_item<'a>( - &'a self, - player: &'a dyn InventoryPlayer, - stack: &'a pumpkin_data::item_stack::ItemStack, - ) -> BoxFuture<'a, ()> { - Box::pin(async move { - debug!("FurnaceOutputSlot: on_take_item called"); - player - .increment_stat( - StatisticCategory::Crafted, - stack.item.id as i32, - stack.item_count as i32, - ) - .await; - // Extract accumulated experience and award to player - let experience = self.experience_container.extract_experience(); - debug!("FurnaceOutputSlot: extracted experience = {experience}"); - if experience > 0 { - debug!("FurnaceOutputSlot: awarding {experience} xp to player"); - player.award_experience(experience).await; - } - self.mark_dirty().await; - }) + fn on_take_item( + &self, + player: &dyn InventoryPlayer, + stack: &pumpkin_data::item_stack::ItemStack, + ) { + debug!("FurnaceOutputSlot: on_take_item called"); + player.increment_stat( + StatisticCategory::Crafted, + stack.item.id as i32, + stack.item_count as i32, + ); + // Extract accumulated experience and award to player + let experience = self.experience_container.extract_experience(); + debug!("FurnaceOutputSlot: extracted experience = {experience}"); + if experience > 0 { + debug!("FurnaceOutputSlot: awarding {experience} xp to player"); + player.award_experience(experience); + } + self.mark_dirty(); } /// Output slot cannot receive inserted items. - fn can_insert<'a>( - &'a self, - _stack: &'a pumpkin_data::item_stack::ItemStack, - ) -> BoxFuture<'a, bool> { + fn can_insert(&self, _stack: &pumpkin_data::item_stack::ItemStack) -> bool { // Cannot insert items into the output slot - Box::pin(async move { false }) + false } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } } diff --git a/crates/pumpkin-inventory/src/generic_container_screen_handler.rs b/crates/pumpkin-inventory/src/generic_container_screen_handler.rs index 2f2cf4a16..7673bf2f0 100644 --- a/crates/pumpkin-inventory/src/generic_container_screen_handler.rs +++ b/crates/pumpkin-inventory/src/generic_container_screen_handler.rs @@ -16,17 +16,14 @@ use pumpkin_world::inventory::Inventory; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, - }, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, slot::NormalSlot, }; /// Creates a generic 9x3 container (single chest). /// /// Used for single chests, ender chests, and similar containers. -pub async fn create_generic_9x3( +pub fn create_generic_9x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -39,13 +36,12 @@ pub async fn create_generic_9x3( 3, 9, ) - .await } /// Creates a generic 9x6 container (double chest). /// /// Used for double chests and similar large containers. -pub async fn create_generic_9x6( +pub fn create_generic_9x6( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -58,13 +54,12 @@ pub async fn create_generic_9x6( 6, 9, ) - .await } /// Creates a generic 3x3 container. /// /// Used for dispensers, droppers, and similar containers. -pub async fn create_generic_3x3( +pub fn create_generic_3x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -77,11 +72,10 @@ pub async fn create_generic_3x3( 3, 3, ) - .await } /// Creates a crafter container (9 slots, 3x3 layout). -pub async fn create_crafter_3x3( +pub fn create_crafter_3x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -94,13 +88,12 @@ pub async fn create_crafter_3x3( 3, 3, ) - .await } /// Creates a hopper container (5 slots). /// /// Hoppers have a single row of 5 slots. -pub async fn create_hopper( +pub fn create_hopper( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -113,7 +106,6 @@ pub async fn create_hopper( 1, 5, ) - .await } /// Generic container screen handler. @@ -141,7 +133,7 @@ impl GenericContainerScreenHandler { /// - `inventory` - The container's inventory /// - `rows` - Number of rows in the container /// - `columns` - Number of columns in the container - async fn new( + fn new( screen_type: WindowType, sync_id: u8, player_inventory: &Arc, @@ -150,14 +142,14 @@ impl GenericContainerScreenHandler { columns: u8, ) -> Self { let mut handler = Self { - inventory: inventory.clone(), + inventory, rows, columns, behaviour: ScreenHandlerBehaviour::new(sync_id, Some(screen_type)), }; // TODO: Add player entity as a parameter - inventory.on_open().await; + handler.inventory.on_open(); handler.add_inventory_slots(); let player_inventory: Arc = player_inventory.clone(); @@ -196,61 +188,47 @@ impl ScreenHandler for GenericContainerScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); } /// Quick move logic for generic containers. /// /// - From container: Move to player inventory (end first) /// - From player inventory: Move to container (start first) - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - // Assuming bounds check passed for slot_index by caller or within quick_move spec - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); + // Assuming bounds check passed for slot_index by caller or within quick_move spec + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - stack_left = slot_stack.clone(); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + stack_left = slot_stack.clone(); - if slot_index < i32::from(self.rows * 9) { - // Move from inventory to player area (end) - if !self - .insert_item( - &mut slot_stack, - (self.rows * 9).into(), - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if !self - .insert_item(&mut slot_stack, 0, (self.rows * 9).into(), false) - .await - { - // Move from player area to inventory (start) + if slot_index < i32::from(self.rows * 9) { + // Move from inventory to player area (end) + if !self.insert_item( + &mut slot_stack, + (self.rows * 9).into(), + self.get_behaviour().slots.len() as i32, + true, + ) { return ItemStack::EMPTY.clone(); } - - // Check the resulting state of the slot stack after insert_item - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; - } + } else if !self.insert_item(&mut slot_stack, 0, (self.rows * 9).into(), false) { + // Move from player area to inventory (start) + return ItemStack::EMPTY.clone(); } - stack_left - }) + // Check the resulting state of the slot stack after insert_item + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); + } + } + + stack_left } } diff --git a/crates/pumpkin-inventory/src/gui_builder.rs b/crates/pumpkin-inventory/src/gui_builder.rs index 85ffd8d76..64c76e489 100644 --- a/crates/pumpkin-inventory/src/gui_builder.rs +++ b/crates/pumpkin-inventory/src/gui_builder.rs @@ -1,9 +1,6 @@ use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, - }, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, slot::NormalSlot, }; use pumpkin_data::{item_stack::ItemStack, screen::WindowType}; @@ -59,11 +56,7 @@ impl GUIBuilder { } /// Builds the `GUIScreenHandler`. - pub async fn build( - self, - sync_id: u8, - player_inventory: &Arc, - ) -> GUIScreenHandler { + pub fn build(self, sync_id: u8, player_inventory: &Arc) -> GUIScreenHandler { let mut behaviour = ScreenHandlerBehaviour::new(sync_id, Some(self.screen_type)); behaviour.allow_grab_items = self.allow_grab_items; behaviour.allow_put_items = self.allow_put_items; @@ -76,7 +69,7 @@ impl GUIBuilder { behaviour, }; - self.inventory.on_open().await; + self.inventory.on_open(); handler.add_inventory_slots(); let player_inventory_trait: Arc = player_inventory.clone(); @@ -123,65 +116,51 @@ impl ScreenHandler for GUIScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack_left = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - stack_left = slot_stack.clone(); - let container_slots = i32::from(self.rows * self.columns); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + stack_left = slot_stack.clone(); + let container_slots = i32::from(self.rows * self.columns); - if slot_index < container_slots { - // From container to player - if !self.get_behaviour().allow_grab_items { - return ItemStack::EMPTY.clone(); - } - if !self - .insert_item( - &mut slot_stack, - container_slots, - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else { - // From player to container - if !self.get_behaviour().allow_put_items { - return ItemStack::EMPTY.clone(); - } - if !self - .insert_item(&mut slot_stack, 0, container_slots, false) - .await - { - // Move from player area to inventory (start) - return ItemStack::EMPTY.clone(); - } + if slot_index < container_slots { + // From container to player + if !self.get_behaviour().allow_grab_items { + return ItemStack::EMPTY.clone(); } - - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; + if !self.insert_item( + &mut slot_stack, + container_slots, + self.get_behaviour().slots.len() as i32, + true, + ) { + return ItemStack::EMPTY.clone(); + } + } else { + // From player to container + if !self.get_behaviour().allow_put_items { + return ItemStack::EMPTY.clone(); + } + if !self.insert_item(&mut slot_stack, 0, container_slots, false) { + // Move from player area to inventory (start) + return ItemStack::EMPTY.clone(); } } - stack_left - }) + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); + } + } + + stack_left } } diff --git a/crates/pumpkin-inventory/src/lectern_screen_handler.rs b/crates/pumpkin-inventory/src/lectern_screen_handler.rs index c3b866bda..0f595d1db 100644 --- a/crates/pumpkin-inventory/src/lectern_screen_handler.rs +++ b/crates/pumpkin-inventory/src/lectern_screen_handler.rs @@ -2,8 +2,7 @@ use std::any::Any; use std::sync::Arc; use crate::screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, - ScreenProperty, offer_or_drop_stack, + InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenProperty, offer_or_drop_stack, }; use crate::slot::NormalSlot; @@ -19,10 +18,10 @@ pub trait LecternController: Send + Sync { fn current_page(&self) -> i32; /// Clamps and persists `page`, emitting a redstone pulse when it changes. - fn set_page(&self, page: i32) -> ScreenHandlerFuture<'_, ()>; + fn set_page(&self, page: i32); /// Restores the bookless block state after the book was taken. - fn on_book_taken(&self) -> ScreenHandlerFuture<'_, ()>; + fn on_book_taken(&self); } /// Exposes the current page as container property 0 (see `window_property::Lectern`). @@ -91,53 +90,37 @@ impl ScreenHandler for LecternScreenHandler { &mut self.behaviour } - fn on_button_click<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - id: i32, - ) -> ScreenHandlerFuture<'a, bool> { - Box::pin(async move { - match id { - Self::PREVIOUS_PAGE_BUTTON_ID => { - self.controller - .set_page(self.controller.current_page() - 1) - .await; - true - } - Self::NEXT_PAGE_BUTTON_ID => { - self.controller - .set_page(self.controller.current_page() + 1) - .await; - true - } - Self::TAKE_BOOK_BUTTON_ID => { - let stack = self.inventory.remove_stack(0).await; - if stack.is_empty() { - return false; - } - self.inventory.mark_dirty(); - self.controller.on_book_taken().await; - offer_or_drop_stack(player, stack).await; - self.send_content_updates().await; - true - } - _ if id >= Self::JUMP_TO_PAGE_OFFSET => { - self.controller - .set_page(id - Self::JUMP_TO_PAGE_OFFSET) - .await; - true - } - _ => false, + fn on_button_click(&mut self, player: &dyn InventoryPlayer, id: i32) -> bool { + match id { + Self::PREVIOUS_PAGE_BUTTON_ID => { + self.controller.set_page(self.controller.current_page() - 1); + true } - }) + Self::NEXT_PAGE_BUTTON_ID => { + self.controller.set_page(self.controller.current_page() + 1); + true + } + Self::TAKE_BOOK_BUTTON_ID => { + let stack = self.inventory.remove_stack(0); + if stack.is_empty() { + return false; + } + self.inventory.mark_dirty(); + self.controller.on_book_taken(); + offer_or_drop_stack(player, stack); + self.send_content_updates(); + true + } + _ if id >= Self::JUMP_TO_PAGE_OFFSET => { + self.controller.set_page(id - Self::JUMP_TO_PAGE_OFFSET); + true + } + _ => false, + } } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - _slot_index: i32, - ) -> ItemStackFuture<'a> { + fn quick_move(&mut self, _player: &dyn InventoryPlayer, _slot_index: i32) -> ItemStack { // The lectern screen has no player slots, so nothing can be shift-clicked. - Box::pin(async move { ItemStack::EMPTY.clone() }) + ItemStack::EMPTY.clone() } } diff --git a/crates/pumpkin-inventory/src/loom_screen_handler.rs b/crates/pumpkin-inventory/src/loom_screen_handler.rs index 8bed70524..8e673c2e8 100644 --- a/crates/pumpkin-inventory/src/loom_screen_handler.rs +++ b/crates/pumpkin-inventory/src/loom_screen_handler.rs @@ -2,9 +2,7 @@ use std::any::Any; use std::sync::Arc; use crate::player::player_inventory::PlayerInventory; -use crate::screen_handler::{ - InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; +use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use crate::slot::NormalSlot; use pumpkin_data::item_stack::ItemStack; @@ -66,48 +64,39 @@ impl ScreenHandler for LoomScreenHandler { self } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ScreenHandlerFuture<'a, ItemStack> { - Box::pin(async move { - let mut stack = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); - if let Some(slot) = slot { - let mut slot_stack = slot.get_cloned_stack().await; - if !slot_stack.is_empty() { - stack = slot_stack.clone(); - if slot_index < 4 { - if !self.insert_item(&mut slot_stack, 4, 40, true).await { - return ItemStack::EMPTY.clone(); - } - } else if !self.insert_item(&mut slot_stack, 0, 3, false).await { + if let Some(slot) = slot { + let mut slot_stack = slot.get_cloned_stack(); + if !slot_stack.is_empty() { + stack = slot_stack.clone(); + if slot_index < 4 { + if !self.insert_item(&mut slot_stack, 4, 40, true) { return ItemStack::EMPTY.clone(); } + } else if !self.insert_item(&mut slot_stack, 0, 3, false) { + return ItemStack::EMPTY.clone(); + } - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; - } + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); } } - stack - }) + } + stack } } diff --git a/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs b/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs index 617773c4f..317069155 100644 --- a/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs +++ b/crates/pumpkin-inventory/src/merchant/merchant_screen_handler.rs @@ -15,11 +15,8 @@ use pumpkin_world::inventory::Inventory; use crate::{ player::player_inventory::PlayerInventory, - screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFuture, offer_or_drop_stack, - }, - slot::{BoxFuture, NormalSlot, Slot}, + screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, offer_or_drop_stack}, + slot::{NormalSlot, Slot}, }; type MerchantValidityCheck = Box bool + Send + Sync>; @@ -30,15 +27,15 @@ pub struct MerchantScreenHandler { selected_offer: usize, active_offer: Option, pub offers: Vec, - pub on_trade: Option ScreenHandlerFuture<'static, ()> + Send + Sync>>, + pub on_trade: Option>, pub on_trade_updated: Option>, - pub on_close: Option ScreenHandlerFuture<'static, ()> + Send + Sync>>, + pub on_close: Option>, pub validity_check: Option, result_taken: Arc, } impl MerchantScreenHandler { - pub async fn new( + pub fn new( sync_id: u8, player_inventory: &Arc, inventory: Arc, @@ -48,7 +45,7 @@ impl MerchantScreenHandler { let mut behaviour = ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Merchant)); behaviour.container_slots = 3; let mut handler = Self { - inventory: inventory.clone(), + inventory, behaviour, selected_offer: 0, active_offer: None, @@ -60,13 +57,13 @@ impl MerchantScreenHandler { result_taken: result_taken.clone(), }; - inventory.on_open().await; + handler.inventory.on_open(); for i in 0..2 { - handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), i))); + handler.add_slot(Arc::new(NormalSlot::new(handler.inventory.clone(), i))); } handler.add_slot(Arc::new(MerchantResultSlot::new( - inventory.clone(), + handler.inventory.clone(), 2, result_taken, ))); @@ -77,15 +74,15 @@ impl MerchantScreenHandler { handler } - pub async fn set_selected_offer(&mut self, index: usize) { + pub fn set_selected_offer(&mut self, index: usize) { self.selected_offer = index; - self.try_move_items(index).await; - self.update_result_slot().await; - self.notify_trade_updated().await; - self.send_content_updates().await; + self.try_move_items(index); + self.update_result_slot(); + self.notify_trade_updated(); + self.send_content_updates(); } - async fn try_move_items(&mut self, index: usize) { + fn try_move_items(&mut self, index: usize) { let Some(offer) = self.offers.get(index) else { return; }; @@ -94,26 +91,20 @@ impl MerchantScreenHandler { let player_slots_end = self.get_behaviour().slots.len() as i32; for index in 0..2 { - let mut stack = self.inventory.get_stack(index).await; - if !stack.is_empty() - && !self - .insert_item(&mut stack, 3, player_slots_end, true) - .await - { + let mut stack = self.inventory.get_stack(index); + if !stack.is_empty() && !self.insert_item(&mut stack, 3, player_slots_end, true) { return; } - self.inventory.set_stack(index, stack).await; + self.inventory.set_stack(index, stack); } - if !self.inventory.get_stack(0).await.is_empty() - || !self.inventory.get_stack(1).await.is_empty() - { + if !self.inventory.get_stack(0).is_empty() || !self.inventory.get_stack(1).is_empty() { return; } - self.move_from_inventory_to_payment_slot(0, &cost_a).await; + self.move_from_inventory_to_payment_slot(0, &cost_a); if let Some(cost_b) = cost_b { - self.move_from_inventory_to_payment_slot(1, &cost_b).await; + self.move_from_inventory_to_payment_slot(1, &cost_b); } } @@ -181,17 +172,17 @@ impl MerchantScreenHandler { }) } - async fn consume_payment(&self, slot: usize, count: u8) { - let mut stack = self.inventory.get_stack(slot).await; + fn consume_payment(&self, slot: usize, count: u8) { + let mut stack = self.inventory.get_stack(slot); stack.decrement(count); if stack.is_empty() { stack = ItemStack::EMPTY.clone(); } - self.inventory.set_stack(slot, stack).await; - self.get_behaviour().slots[slot].mark_dirty().await; + self.inventory.set_stack(slot, stack); + self.get_behaviour().slots[slot].mark_dirty(); } - async fn complete_trade(&mut self, player: &dyn InventoryPlayer) -> bool { + fn complete_trade(&mut self, player: &dyn InventoryPlayer) -> bool { let Some(offer_index) = self.active_offer else { return false; }; @@ -202,30 +193,28 @@ impl MerchantScreenHandler { return false; } - let input_a = self.inventory.get_stack(0).await; - let input_b = self.inventory.get_stack(1).await; + let input_a = self.inventory.get_stack(0); + let input_b = self.inventory.get_stack(1); let Some(swapped) = Self::input_order(offer, &input_a, &input_b) else { return false; }; let cost_a = Self::adjusted_cost_a(offer).item_count; let cost_b = offer.cost_b.as_ref().map(|cost| cost.0.item_count); - self.consume_payment(usize::from(swapped), cost_a).await; + self.consume_payment(usize::from(swapped), cost_a); if let Some(cost_b) = cost_b { - self.consume_payment(usize::from(!swapped), cost_b).await; + self.consume_payment(usize::from(!swapped), cost_b); } self.offers[offer_index].uses += 1; if let Some(on_trade) = &self.on_trade { - on_trade(offer_index).await; + on_trade(offer_index); } - player - .increment_stat( - StatisticCategory::Custom, - CustomStatistic::TradedWithVillager as i32, - 1, - ) - .await; + player.increment_stat( + StatisticCategory::Custom, + CustomStatistic::TradedWithVillager as i32, + 1, + ); true } @@ -234,30 +223,29 @@ impl MerchantScreenHandler { /// Bedrock moves the result directly between named containers instead of /// sending Java's slot-click action, so the caller has already validated /// that the result can be inserted into its destination. - pub async fn complete_bedrock_trade(&mut self, player: &dyn InventoryPlayer) -> bool { - if !self.complete_trade(player).await { + pub fn complete_bedrock_trade(&mut self, player: &dyn InventoryPlayer) -> bool { + if !self.complete_trade(player) { return false; } - self.update_result_slot().await; - self.notify_trade_updated().await; - self.send_content_updates().await; + self.update_result_slot(); + self.notify_trade_updated(); + self.send_content_updates(); true } - async fn can_fully_insert(&self, stack: &ItemStack, start: usize, end: usize) -> bool { + fn can_fully_insert(&self, stack: &ItemStack, start: usize, end: usize) -> bool { let mut remaining = stack.item_count; for slot in &self.get_behaviour().slots[start..end] { - if !slot.can_insert(stack).await { + if !slot.can_insert(stack) { continue; } - let existing = slot.get_cloned_stack().await; + let existing = slot.get_cloned_stack(); let capacity = if existing.is_empty() { - slot.get_max_item_count_for_stack(stack).await + slot.get_max_item_count_for_stack(stack) } else if existing.are_items_and_components_equal(stack) && stack.are_items_and_components_equal(&existing) { slot.get_max_item_count_for_stack(&existing) - .await .saturating_sub(existing.item_count) } else { 0 @@ -270,11 +258,11 @@ impl MerchantScreenHandler { false } - async fn move_from_inventory_to_payment_slot(&self, payment_slot: usize, cost: &ItemStack) { - let mut payment = self.inventory.get_stack(payment_slot).await; + fn move_from_inventory_to_payment_slot(&self, payment_slot: usize, cost: &ItemStack) { + let mut payment = self.inventory.get_stack(payment_slot); for slot in &self.get_behaviour().slots[3..] { - let mut source = slot.get_stack().await; + let mut source = slot.get_stack(); if source.is_empty() || source.item.id != cost.item.id || !cost.are_items_and_components_equal(&source) @@ -296,32 +284,29 @@ impl MerchantScreenHandler { if source.is_empty() { source = ItemStack::EMPTY.clone(); } - slot.set_stack(source).await; + slot.set_stack(source); if payment.item_count == payment.get_max_stack_size() { break; } } - self.inventory.set_stack(payment_slot, payment).await; + self.inventory.set_stack(payment_slot, payment); } - pub async fn update_result_slot(&mut self) { - let input_a = self.inventory.get_stack(0).await; - let input_b = self.inventory.get_stack(1).await; + pub fn update_result_slot(&mut self) { + let input_a = self.inventory.get_stack(0); + let input_b = self.inventory.get_stack(1); self.active_offer = self.find_active_offer(&input_a, &input_b); if let Some(index) = self.active_offer { self.inventory - .set_stack(2, (*self.offers[index].output.0).clone()) - .await; + .set_stack(2, (*self.offers[index].output.0).clone()); } else { - self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await; + self.inventory.set_stack(2, ItemStack::EMPTY.clone()); } } - async fn notify_trade_updated(&self) { - if self.inventory.get_stack(0).await.is_empty() - && self.inventory.get_stack(1).await.is_empty() - { + fn notify_trade_updated(&self) { + if self.inventory.get_stack(0).is_empty() && self.inventory.get_stack(1).is_empty() { return; } if let Some(on_trade_updated) = &self.on_trade_updated { @@ -353,129 +338,104 @@ impl ScreenHandler for MerchantScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - if let Some(on_close) = &self.on_close { - on_close().await; + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + if let Some(on_close) = &self.on_close { + on_close(); + } + self.default_on_closed(player); + self.inventory.on_close(); + // Vanilla drops items from merchant container on close + for i in 0..2 { + // Drop inputs only, output is virtual/ghost in some sense or just cleared + let stack = self.inventory.remove_stack(i); + if !stack.is_empty() { + offer_or_drop_stack(player, stack); } - self.default_on_closed(player).await; - self.inventory.on_close().await; - // Vanilla drops items from merchant container on close - for i in 0..2 { - // Drop inputs only, output is virtual/ghost in some sense or just cleared - let stack = self.inventory.remove_stack(i).await; - if !stack.is_empty() { - offer_or_drop_stack(player, stack).await; - } - } - // Clear output slot - self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await; - }) + } + // Clear output slot + self.inventory.set_stack(2, ItemStack::EMPTY.clone()); } - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - const PLAYER_INVENTORY_START: i32 = 3; - const PLAYER_HOTBAR_START: i32 = 30; + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + const PLAYER_INVENTORY_START: i32 = 3; + const PLAYER_HOTBAR_START: i32 = 30; - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - let mut stack = slot.get_cloned_stack().await; - if stack.is_empty() { - return ItemStack::EMPTY.clone(); - } - let original = stack.clone(); - let player_slots_end = self.get_behaviour().slots.len() as i32; + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let mut stack = slot.get_cloned_stack(); + if stack.is_empty() { + return ItemStack::EMPTY.clone(); + } + let original = stack.clone(); + let player_slots_end = self.get_behaviour().slots.len() as i32; - if slot_index == 2 { - if !self - .can_fully_insert( - &stack, - PLAYER_INVENTORY_START as usize, - player_slots_end as usize, - ) - .await - || !self - .insert_item(&mut stack, PLAYER_INVENTORY_START, player_slots_end, true) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if slot_index < PLAYER_INVENTORY_START { - if !self - .insert_item(&mut stack, PLAYER_INVENTORY_START, player_slots_end, false) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if slot_index < PLAYER_HOTBAR_START { - if !self - .insert_item(&mut stack, PLAYER_HOTBAR_START, player_slots_end, false) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if !self - .insert_item( - &mut stack, - PLAYER_INVENTORY_START, - PLAYER_HOTBAR_START, - false, - ) - .await + if slot_index == 2 { + if !self.can_fully_insert( + &stack, + PLAYER_INVENTORY_START as usize, + player_slots_end as usize, + ) || !self.insert_item(&mut stack, PLAYER_INVENTORY_START, player_slots_end, true) { return ItemStack::EMPTY.clone(); } - - if stack.item_count == original.item_count { + } else if slot_index < PLAYER_INVENTORY_START { + if !self.insert_item(&mut stack, PLAYER_INVENTORY_START, player_slots_end, false) { return ItemStack::EMPTY.clone(); } - if stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(stack.clone()).await; + } else if slot_index < PLAYER_HOTBAR_START { + if !self.insert_item(&mut stack, PLAYER_HOTBAR_START, player_slots_end, false) { + return ItemStack::EMPTY.clone(); } + } else if !self.insert_item( + &mut stack, + PLAYER_INVENTORY_START, + PLAYER_HOTBAR_START, + false, + ) { + return ItemStack::EMPTY.clone(); + } - let mut taken = original.clone(); - taken.set_count(original.item_count - stack.item_count); - slot.on_take_item(player, &taken).await; - if slot_index == 2 { - slot.on_quick_move_crafted(stack, original.clone()).await; - self.result_taken.store(false, Ordering::Relaxed); - if !self.complete_trade(player).await { - return ItemStack::EMPTY.clone(); - } - self.update_result_slot().await; + if stack.item_count == original.item_count { + return ItemStack::EMPTY.clone(); + } + if stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(stack.clone()); + } + + let mut taken = original.clone(); + taken.set_count(original.item_count - stack.item_count); + slot.on_take_item(player, &taken); + if slot_index == 2 { + slot.on_quick_move_crafted(stack, original.clone()); + self.result_taken.store(false, Ordering::Relaxed); + if !self.complete_trade(player) { + return ItemStack::EMPTY.clone(); } + self.update_result_slot(); + } - original - }) + original } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: pumpkin_protocol::java::server::play::SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - if slot_index == 2 { - self.update_result_slot().await; - } - self.result_taken.store(false, Ordering::Relaxed); - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - if self.result_taken.swap(false, Ordering::Relaxed) { - self.complete_trade(player).await; - } - self.update_result_slot().await; - self.notify_trade_updated().await; - self.send_content_updates().await; - }) + player: &dyn InventoryPlayer, + ) { + if slot_index == 2 { + self.update_result_slot(); + } + self.result_taken.store(false, Ordering::Relaxed); + self.internal_on_slot_click(slot_index, button, action_type, player); + if self.result_taken.swap(false, Ordering::Relaxed) { + self.complete_trade(player); + } + self.update_result_slot(); + self.notify_trade_updated(); + self.send_content_updates(); } } @@ -510,29 +470,21 @@ impl Slot for MerchantResultSlot { self.id.store(id as u8, Ordering::Relaxed); } - fn take_stack(&self, _amount: u8) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { self.inventory.remove_stack(self.index).await }) + fn take_stack(&self, _amount: u8) -> ItemStack { + self.inventory.remove_stack(self.index) } - fn on_take_item<'a>( - &'a self, - _player: &'a dyn InventoryPlayer, - _stack: &'a ItemStack, - ) -> BoxFuture<'a, ()> { - Box::pin(async move { - self.result_taken.store(true, Ordering::Relaxed); - self.mark_dirty().await; - }) + fn on_take_item(&self, _player: &dyn InventoryPlayer, _stack: &ItemStack) { + self.result_taken.store(true, Ordering::Relaxed); + self.mark_dirty(); } - fn can_insert(&self, _stack: &ItemStack) -> BoxFuture<'_, bool> { - Box::pin(async { false }) + fn can_insert(&self, _stack: &ItemStack) -> bool { + false } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } } @@ -558,10 +510,7 @@ mod tests { use pumpkin_world::inventory::SimpleInventory; use std::sync::Mutex; - use crate::{ - entity_equipment::EntityEquipment, - screen_handler::{InventoryPlayer, PlayerFuture}, - }; + use crate::{entity_equipment::EntityEquipment, screen_handler::InventoryPlayer}; use super::*; @@ -610,9 +559,7 @@ mod tests { self } - fn drop_item(&self, _item: ItemStack, _retain_ownership: bool) -> PlayerFuture<'_, ()> { - Box::pin(async {}) - } + fn drop_item(&self, _item: ItemStack, _retain_ownership: bool) {} fn get_inventory(&self) -> Arc { self.inventory.clone() @@ -630,90 +577,49 @@ mod tests { 0 } - fn add_experience_levels(&self, _levels: i32) -> PlayerFuture<'_, ()> { - Box::pin(async {}) - } + fn add_experience_levels(&self, _levels: i32) {} fn enchantment_seed(&self) -> i32 { 0 } - fn set_enchantment_seed(&self, _seed: i32) -> PlayerFuture<'_, ()> { - Box::pin(async {}) - } + fn set_enchantment_seed(&self, _seed: i32) {} - fn enqueue_inventory_packet<'a>( - &'a self, - _packet: &'a CSetContainerContent, + fn enqueue_inventory_packet( + &self, + _packet: &CSetContainerContent, _window_type: Option, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) + ) { } - fn enqueue_slot_packet<'a>( - &'a self, - _packet: &'a CSetContainerSlot, + fn enqueue_slot_packet( + &self, + _packet: &CSetContainerSlot, _window_type: Option, _total_slots: usize, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) + ) { } - fn enqueue_cursor_packet<'a>( - &'a self, - _packet: &'a CSetCursorItem, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) + fn enqueue_cursor_packet(&self, _packet: &CSetCursorItem) {} + + fn enqueue_property_packet(&self, _packet: &CSetContainerProperty) {} + + fn enqueue_slot_set_packet(&self, _packet: &CSetPlayerInventory) {} + + fn enqueue_set_held_item_packet(&self, _packet: &CSetSelectedSlot) {} + + fn enqueue_equipment_change(&self, _slot: &EquipmentSlot, _stack: &ItemStack) {} + + fn award_experience(&self, amount: i32) { + self.experience.fetch_add(amount, Ordering::Relaxed); } - fn enqueue_property_packet<'a>( - &'a self, - _packet: &'a CSetContainerProperty, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) - } - - fn enqueue_slot_set_packet<'a>( - &'a self, - _packet: &'a CSetPlayerInventory, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) - } - - fn enqueue_set_held_item_packet<'a>( - &'a self, - _packet: &'a CSetSelectedSlot, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) - } - - fn enqueue_equipment_change<'a>( - &'a self, - _slot: &'a EquipmentSlot, - _stack: &'a ItemStack, - ) -> PlayerFuture<'a, ()> { - Box::pin(async {}) - } - - fn award_experience(&self, amount: i32) -> PlayerFuture<'_, ()> { - Box::pin(async move { - self.experience.fetch_add(amount, Ordering::Relaxed); - }) - } - - fn increment_stat( - &self, - category: StatisticCategory, - stat_id: i32, - amount: i32, - ) -> PlayerFuture<'_, ()> { - Box::pin(async move { - if category == StatisticCategory::Custom - && stat_id == CustomStatistic::TradedWithVillager as i32 - { - self.traded.fetch_add(amount, Ordering::Relaxed); - } - }) + fn increment_stat(&self, category: StatisticCategory, stat_id: i32, amount: i32) { + if category == StatisticCategory::Custom + && stat_id == CustomStatistic::TradedWithVillager as i32 + { + self.traded.fetch_add(amount, Ordering::Relaxed); + } } } @@ -727,63 +633,58 @@ mod tests { ) } - #[tokio::test] - async fn selecting_bookshelf_trade_moves_payment_and_creates_result() { + #[test] + fn selecting_bookshelf_trade_moves_payment_and_creates_result() { let (player_inventory, merchant_inventory) = inventories(); - player_inventory - .set_stack(0, ItemStack::new(12, &Item::EMERALD)) - .await; + player_inventory.set_stack(0, ItemStack::new(12, &Item::EMERALD)); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; + ); - handler.set_selected_offer(0).await; + handler.set_selected_offer(0); - let payment = merchant_inventory.get_stack(0).await; + let payment = merchant_inventory.get_stack(0); assert_eq!(payment.item_count, 12); - let result = merchant_inventory.get_stack(2).await; + let result = merchant_inventory.get_stack(2); assert_eq!(result.item.id, Item::BOOKSHELF.id); - assert!(player_inventory.get_stack(0).await.is_empty()); + assert!(player_inventory.get_stack(0).is_empty()); } - #[tokio::test] - async fn taking_result_commits_payment_after_delivery() { + #[test] + fn taking_result_commits_payment_after_delivery() { let (player_inventory, merchant_inventory) = inventories(); - merchant_inventory - .set_stack(0, ItemStack::new(12, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(12, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; + ); let trade_count = Arc::new(AtomicUsize::new(0)); handler.on_trade = Some(Box::new({ let trade_count = trade_count.clone(); move |_| { - let trade_count = trade_count.clone(); - Box::pin(async move { - trade_count.fetch_add(1, Ordering::Relaxed); - }) + trade_count.fetch_add(1, Ordering::Relaxed); } })); - handler.update_result_slot().await; + handler.update_result_slot(); - handler - .on_slot_click(2, 0, SlotActionType::Pickup, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::Pickup, &player); assert_eq!(handler.offers[0].uses, 1); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 3); + assert_eq!(merchant_inventory.get_stack(0).item_count, 3); assert_eq!( - handler.get_behaviour().cursor_stack.lock().await.item.id, + handler + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .item + .id, Item::BOOKSHELF.id ); assert_eq!(player.traded.load(Ordering::Relaxed), 1); @@ -791,47 +692,39 @@ mod tests { assert_eq!(player.experience.load(Ordering::Relaxed), 0); } - #[tokio::test] - async fn bedrock_result_request_commits_the_trade() { + #[test] + fn bedrock_result_request_commits_the_trade() { let (player_inventory, merchant_inventory) = inventories(); - merchant_inventory - .set_stack(0, ItemStack::new(12, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(12, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; + ); let trade_count = Arc::new(AtomicUsize::new(0)); handler.on_trade = Some(Box::new({ let trade_count = trade_count.clone(); move |_| { - let trade_count = trade_count.clone(); - Box::pin(async move { - trade_count.fetch_add(1, Ordering::Relaxed); - }) + trade_count.fetch_add(1, Ordering::Relaxed); } })); - handler.update_result_slot().await; + handler.update_result_slot(); - assert!(handler.complete_bedrock_trade(&player).await); + assert!(handler.complete_bedrock_trade(&player)); assert_eq!(handler.offers[0].uses, 1); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 3); + assert_eq!(merchant_inventory.get_stack(0).item_count, 3); assert_eq!(player.traded.load(Ordering::Relaxed), 1); assert_eq!(trade_count.load(Ordering::Relaxed), 1); - assert!(merchant_inventory.get_stack(2).await.is_empty()); + assert!(merchant_inventory.get_stack(2).is_empty()); } - #[tokio::test] - async fn selection_hint_zero_resolves_matching_offer() { + #[test] + fn selection_hint_zero_resolves_matching_offer() { let (player_inventory, merchant_inventory) = inventories(); - merchant_inventory - .set_stack(0, ItemStack::new(64, &Item::PAPER)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(64, &Item::PAPER)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, @@ -841,132 +734,118 @@ mod tests { bookshelf_offer(), single_cost_offer(&Item::PAPER, 24, &Item::EMERALD, 16), ], - ) - .await; + ); let traded_offer = Arc::new(AtomicUsize::new(usize::MAX)); handler.on_trade = Some(Box::new({ let traded_offer = traded_offer.clone(); move |offer_index| { - let traded_offer = traded_offer.clone(); - Box::pin(async move { - traded_offer.store(offer_index, Ordering::Relaxed); - }) + traded_offer.store(offer_index, Ordering::Relaxed); } })); - handler.update_result_slot().await; + handler.update_result_slot(); - handler - .on_slot_click(2, 0, SlotActionType::Pickup, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::Pickup, &player); assert_eq!(handler.offers[0].uses, 0); assert_eq!(handler.offers[1].uses, 1); assert_eq!(traded_offer.load(Ordering::Relaxed), 1); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 40); + assert_eq!(merchant_inventory.get_stack(0).item_count, 40); assert_eq!( - handler.get_behaviour().cursor_stack.lock().await.item.id, + handler + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .item + .id, Item::EMERALD.id ); } - #[tokio::test] - async fn invalid_payment_does_not_notify_the_merchant() { + #[test] + fn invalid_payment_does_not_notify_the_merchant() { let (player_inventory, merchant_inventory) = inventories(); - merchant_inventory - .set_stack(0, ItemStack::new(8, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(8, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; + ); let trade_count = Arc::new(AtomicUsize::new(0)); handler.on_trade = Some(Box::new({ let trade_count = trade_count.clone(); move |_| { - let trade_count = trade_count.clone(); - Box::pin(async move { - trade_count.fetch_add(1, Ordering::Relaxed); - }) + trade_count.fetch_add(1, Ordering::Relaxed); } })); - handler - .on_slot_click(2, 0, SlotActionType::Pickup, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::Pickup, &player); assert_eq!(handler.offers[0].uses, 0); assert_eq!(trade_count.load(Ordering::Relaxed), 0); assert_eq!(player.traded.load(Ordering::Relaxed), 0); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 8); - assert!(handler.get_behaviour().cursor_stack.lock().await.is_empty()); + assert_eq!(merchant_inventory.get_stack(0).item_count, 8); + assert!( + handler + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() + ); } - #[tokio::test] - async fn full_inventory_does_not_commit_quick_moved_trade() { + #[test] + fn full_inventory_does_not_commit_quick_moved_trade() { let (player_inventory, merchant_inventory) = inventories(); player_inventory .main_inventory .write() .unwrap_or_else(std::sync::PoisonError::into_inner) .fill_with(|| ItemStack::new(64, &Item::COBBLESTONE)); - merchant_inventory - .set_stack(0, ItemStack::new(9, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(9, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; - handler.update_result_slot().await; + ); + handler.update_result_slot(); - handler - .on_slot_click(2, 0, SlotActionType::QuickMove, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::QuickMove, &player); assert_eq!(handler.offers[0].uses, 0); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 9); - assert_eq!( - merchant_inventory.get_stack(2).await.item.id, - Item::BOOKSHELF.id - ); + assert_eq!(merchant_inventory.get_stack(0).item_count, 9); + assert_eq!(merchant_inventory.get_stack(2).item.id, Item::BOOKSHELF.id); assert_eq!(player.traded.load(Ordering::Relaxed), 0); } - #[tokio::test] - async fn quick_move_repeats_while_payment_and_stock_remain() { + #[test] + fn quick_move_repeats_while_payment_and_stock_remain() { let (player_inventory, merchant_inventory) = inventories(); - merchant_inventory - .set_stack(0, ItemStack::new(64, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(64, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![bookshelf_offer()], - ) - .await; - handler.update_result_slot().await; + ); + handler.update_result_slot(); - handler - .on_slot_click(2, 0, SlotActionType::QuickMove, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::QuickMove, &player); assert_eq!(handler.offers[0].uses, 7); assert_eq!(player.traded.load(Ordering::Relaxed), 7); - assert_eq!(merchant_inventory.get_stack(0).await.item_count, 1); - assert_eq!(player_inventory.get_stack(8).await.item_count, 7); + assert_eq!(merchant_inventory.get_stack(0).item_count, 1); + assert_eq!(player_inventory.get_stack(8).item_count, 7); } - #[tokio::test] - async fn swapped_payment_slots_are_accepted_and_consumed() { + #[test] + fn swapped_payment_slots_are_accepted_and_consumed() { let (player_inventory, merchant_inventory) = inventories(); let mut offer = bookshelf_offer(); offer.base_cost_a = ItemStackSerializer(Cow::Owned(ItemStack::new(5, &Item::EMERALD))); @@ -974,28 +853,21 @@ mod tests { 1, &Item::BOOK, )))); - merchant_inventory - .set_stack(0, ItemStack::new(1, &Item::BOOK)) - .await; - merchant_inventory - .set_stack(1, ItemStack::new(5, &Item::EMERALD)) - .await; + merchant_inventory.set_stack(0, ItemStack::new(1, &Item::BOOK)); + merchant_inventory.set_stack(1, ItemStack::new(5, &Item::EMERALD)); let player = TestPlayer::new(player_inventory.clone()); let mut handler = MerchantScreenHandler::new( 1, &player_inventory, merchant_inventory.clone(), vec![offer], - ) - .await; - handler.update_result_slot().await; + ); + handler.update_result_slot(); - handler - .on_slot_click(2, 0, SlotActionType::Pickup, &player) - .await; + handler.on_slot_click(2, 0, SlotActionType::Pickup, &player); assert_eq!(handler.offers[0].uses, 1); - assert!(merchant_inventory.get_stack(0).await.is_empty()); - assert!(merchant_inventory.get_stack(1).await.is_empty()); + assert!(merchant_inventory.get_stack(0).is_empty()); + assert!(merchant_inventory.get_stack(1).is_empty()); } } diff --git a/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs b/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs index 38a72877c..6d36e7c7a 100644 --- a/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs +++ b/crates/pumpkin-inventory/src/player/ender_chest_inventory.rs @@ -10,13 +10,13 @@ //! 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 std::any::Any; +use std::sync::{Arc, Mutex, RwLock}; use pumpkin_data::item_stack::ItemStack; use pumpkin_world::{ block::viewer::ViewerCountTracker, - inventory::{Clearable, Inventory, InventoryFuture}, + inventory::{Clearable, Inventory}, }; /// A player's ender chest inventory. @@ -93,93 +93,79 @@ impl Inventory for EnderChestInventory { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self - .items - .read() - .unwrap_or_else(std::sync::PoisonError::into_inner); - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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() - .unwrap_or_else(std::sync::PoisonError::into_inner); - items - .get(slot) - .cloned() - .unwrap_or_else(|| ItemStack::EMPTY.clone()) - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items + .get(slot) + .cloned() + .unwrap_or_else(|| ItemStack::EMPTY.clone()) } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - 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 { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + 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 { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - 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 { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + 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 { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self - .items - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if slot < Self::INVENTORY_SIZE { - items[slot] = stack; - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < Self::INVENTORY_SIZE { + items[slot] = stack; + } } - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - if let Some(tracker) = self - .tracker - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .as_ref() - { - tracker.open_container(); - } - }) + fn on_open(&self) { + if let Some(tracker) = self + .tracker + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + tracker.open_container(); + } } - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let tracker = self - .tracker - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .take(); - if let Some(tracker) = tracker { - tracker.close_container(); - } - }) + fn on_close(&self) { + let tracker = self + .tracker + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); + if let Some(tracker) = tracker { + tracker.close_container(); + } } fn mark_dirty(&self) {} @@ -190,14 +176,12 @@ impl Inventory for EnderChestInventory { } impl Clearable for EnderChestInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self - .items - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - items.fill_with(|| ItemStack::EMPTY.clone()); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); } } @@ -206,57 +190,57 @@ mod tests { use super::*; use pumpkin_data::item::Item; - #[tokio::test] - async fn new_inventory() { + #[test] + fn new_inventory() { let ec = EnderChestInventory::new(); assert_eq!(ec.size(), 27); - assert!(ec.is_empty().await); + assert!(ec.is_empty()); assert!(!ec.has_tracker()); } - #[tokio::test] - async fn set_and_get_stack() { + #[test] + fn set_and_get_stack() { let ec = EnderChestInventory::new(); let stack = ItemStack::new(1, &Item::DIRT); - ec.set_stack(0, stack.clone()).await; - assert_eq!(ec.get_stack(0).await.item.id, Item::DIRT.id); - assert_eq!(ec.get_stack(0).await.item_count, 1); - assert!(!ec.is_empty().await); + ec.set_stack(0, stack.clone()); + assert_eq!(ec.get_stack(0).item.id, Item::DIRT.id); + assert_eq!(ec.get_stack(0).item_count, 1); + assert!(!ec.is_empty()); // Out of bounds shouldn't panic - ec.set_stack(100, stack).await; - assert!(ec.get_stack(100).await.is_empty()); + ec.set_stack(100, stack); + assert!(ec.get_stack(100).is_empty()); } - #[tokio::test] - async fn remove_stack() { + #[test] + fn remove_stack() { let ec = EnderChestInventory::new(); let stack = ItemStack::new(5, &Item::DIAMOND); - ec.set_stack(10, stack).await; + ec.set_stack(10, stack); - let removed_specific = ec.remove_stack_specific(10, 2).await; + let removed_specific = ec.remove_stack_specific(10, 2); assert_eq!(removed_specific.item_count, 2); - assert_eq!(ec.get_stack(10).await.item_count, 3); + assert_eq!(ec.get_stack(10).item_count, 3); - let removed_all = ec.remove_stack(10).await; + let removed_all = ec.remove_stack(10); assert_eq!(removed_all.item_count, 3); - assert!(ec.get_stack(10).await.is_empty()); - assert!(ec.is_empty().await); + assert!(ec.get_stack(10).is_empty()); + assert!(ec.is_empty()); } - #[tokio::test] - async fn clear_inventory() { + #[test] + fn clear_inventory() { let ec = EnderChestInventory::new(); - ec.set_stack(0, ItemStack::new(1, &Item::STONE)).await; - ec.set_stack(26, ItemStack::new(1, &Item::OAK_LOG)).await; - assert!(!ec.is_empty().await); + ec.set_stack(0, ItemStack::new(1, &Item::STONE)); + ec.set_stack(26, ItemStack::new(1, &Item::OAK_LOG)); + assert!(!ec.is_empty()); - ec.clear().await; - assert!(ec.is_empty().await); + ec.clear(); + assert!(ec.is_empty()); } - #[tokio::test] - async fn tracker_lifecycle() { + #[test] + fn tracker_lifecycle() { let ec = EnderChestInventory::new(); let tracker1 = Arc::new(ViewerCountTracker::new()); let tracker2 = Arc::new(ViewerCountTracker::new()); @@ -266,7 +250,7 @@ mod tests { assert!(ec.is_tracker(&tracker1)); assert!(!ec.is_tracker(&tracker2)); - ec.on_open().await; + ec.on_open(); assert_eq!(tracker1.get_viewer_count(), 1); // Setting a new tracker while one is open should close the old tracker @@ -274,10 +258,10 @@ mod tests { assert_eq!(tracker1.get_viewer_count(), 0); assert!(ec.is_tracker(&tracker2)); - ec.on_open().await; + ec.on_open(); assert_eq!(tracker2.get_viewer_count(), 1); - ec.on_close().await; + ec.on_close(); assert_eq!(tracker2.get_viewer_count(), 0); 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 8f983a845..4b648c0fe 100644 --- a/crates/pumpkin-inventory/src/player/player_inventory.rs +++ b/crates/pumpkin-inventory/src/player/player_inventory.rs @@ -15,10 +15,9 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_protocol::java::client::play::CSetPlayerInventory; use pumpkin_util::Hand; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; use std::any::Any; use std::collections::HashMap; -use std::pin::Pin; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -439,8 +438,8 @@ impl PlayerInventory { } /// 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; + pub fn offer_or_drop_stack(&self, stack: ItemStack, player: &dyn InventoryPlayer) { + self.offer(stack, true, player); } /// Gives a stack to the player, optionally notifying the client. @@ -449,7 +448,7 @@ impl PlayerInventory { /// - `stack` - The stack to give /// - `notify_client` - Whether to send inventory update packets /// - `player` - The player to give the stack to - pub async fn offer(&self, stack: ItemStack, notify_client: bool, player: &dyn InventoryPlayer) { + pub fn offer(&self, stack: ItemStack, notify_client: bool, player: &dyn InventoryPlayer) { let mut stack = stack; while !stack.is_empty() { let mut room_for_stack = self.get_occupied_slot_with_room_for_stack(&stack); @@ -458,37 +457,33 @@ impl PlayerInventory { } if room_for_stack == -1 { - player.drop_item(stack, false).await; + player.drop_item(stack, false); break; } - let items_fit = stack.get_max_stack_size() - - self.get_stack(room_for_stack as usize).await.item_count; + let items_fit = + stack.get_max_stack_size() - self.get_stack(room_for_stack as usize).item_count; if self.insert_stack(room_for_stack, &mut stack.split(items_fit)) && notify_client { - player - .enqueue_slot_set_packet(&CSetPlayerInventory::new( - i32::from(room_for_stack).into(), - &self.get_stack(room_for_stack as usize).await.into(), - )) - .await; + player.enqueue_slot_set_packet(&CSetPlayerInventory::new( + i32::from(room_for_stack).into(), + &self.get_stack(room_for_stack as usize).into(), + )); } } } } impl Clearable for PlayerInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut inv = self - .main_inventory - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - inv.fill_with(|| ItemStack::EMPTY.clone()); - self.entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clear(); - }) + fn clear(&self) { + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + inv.fill_with(|| ItemStack::EMPTY.clone()); + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clear(); } } @@ -497,118 +492,108 @@ impl Inventory for PlayerInventory { Self::MAIN_SIZE + self.equipment_slots.len() } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { + fn is_empty(&self) -> bool { + let inv = self + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if inv.iter().any(|s| !s.is_empty()) { + return false; + } + + for slot in self.equipment_slots.values() { + let eq_item = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(slot); + if !eq_item.is_empty() { + return false; + } + } + + true + } + + fn get_stack(&self, slot: usize) -> ItemStack { + if slot < Self::MAIN_SIZE { let inv = self .main_inventory .read() .unwrap_or_else(std::sync::PoisonError::into_inner); - if inv.iter().any(|s| !s.is_empty()) { - return false; - } - - for slot in self.equipment_slots.values() { - let eq_item = self - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .get(slot); - if !eq_item.is_empty() { - return false; - } - } - - true - }) + inv[slot].clone() + } else if let Some(slot) = self.equipment_slots.get(&slot) { + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(slot) + } else { + ItemStack::EMPTY.clone() + } } - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - 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) = self.equipment_slots.get(&slot) { - self.entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .get(slot) + fn remove_stack(&self, slot: usize) -> ItemStack { + if slot < Self::MAIN_SIZE { + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::replace(&mut inv[slot], ItemStack::EMPTY.clone()) + } else if let Some(slot) = self.equipment_slots.get(&slot) { + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(slot, ItemStack::EMPTY.clone()) + } else { + ItemStack::EMPTY.clone() + } + } + + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + if slot < Self::MAIN_SIZE { + let mut inv = self + .main_inventory + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !inv[slot].is_empty() && amount > 0 { + inv[slot].split(amount) } else { ItemStack::EMPTY.clone() } - }) - } + } else if let Some(slot) = self.equipment_slots.get(&slot) { + let mut equipment = self + .entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut stack = equipment.get(slot); - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot < Self::MAIN_SIZE { - let mut inv = self - .main_inventory - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - std::mem::replace(&mut inv[slot], ItemStack::EMPTY.clone()) - } else if let Some(slot) = self.equipment_slots.get(&slot) { - self.entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .put(slot, ItemStack::EMPTY.clone()) + if !stack.is_empty() && amount > 0 { + let split = stack.split(amount); + equipment.put(slot, stack); + split } else { ItemStack::EMPTY.clone() } - }) + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot < Self::MAIN_SIZE { - let mut inv = self - .main_inventory - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if !inv[slot].is_empty() && amount > 0 { - inv[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - } - } else if let Some(slot) = self.equipment_slots.get(&slot) { - let mut equipment = self - .entity_equipment - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut stack = equipment.get(slot); - - if !stack.is_empty() && amount > 0 { - let split = stack.split(amount); - equipment.put(slot, stack); - split - } else { - ItemStack::EMPTY.clone() - } - } else { - ItemStack::EMPTY.clone() - } - }) - } - - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - if slot < Self::MAIN_SIZE { - let mut inv = self - .main_inventory - .write() - .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); - } else { - warn!("Failed to get Equipment Slot at {slot}"); - } - }) + fn set_stack(&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) = self.equipment_slots.get(&slot) { + self.entity_equipment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(slot, stack); + } else { + warn!("Failed to get Equipment Slot at {slot}"); + } } fn mark_dirty(&self) {} diff --git a/crates/pumpkin-inventory/src/player/player_screen_handler.rs b/crates/pumpkin-inventory/src/player/player_screen_handler.rs index 702445f99..603e0f752 100644 --- a/crates/pumpkin-inventory/src/player/player_screen_handler.rs +++ b/crates/pumpkin-inventory/src/player/player_screen_handler.rs @@ -22,9 +22,7 @@ use super::player_inventory::PlayerInventory; use crate::crafting::crafting_inventory::CraftingInventory; use crate::crafting::crafting_screen_handler::CraftingScreenHandler; use crate::crafting::recipes::{RecipeFinderScreenHandler, RecipeInputInventory}; -use crate::screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; +use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use crate::slot::{ArmorSlot, NormalSlot, Slot}; use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl}; use pumpkin_data::item_stack::ItemStack; @@ -77,7 +75,7 @@ impl PlayerScreenHandler { /// - `player_inventory` - The player's inventory /// - `window_type` - The window type (usually None for player inventory) /// - `sync_id` - The synchronization ID - pub async fn new( + pub fn new( player_inventory: &Arc, window_type: Option, sync_id: u8, @@ -91,9 +89,7 @@ impl PlayerScreenHandler { crafting_inventory: crafting_inventory.clone(), }; - player_screen_handler - .add_recipe_slots(crafting_inventory, provider) - .await; + player_screen_handler.add_recipe_slots(crafting_inventory, provider); // Add armor slots (head, chest, legs, feet) for i in 0..4 { @@ -134,13 +130,10 @@ impl ScreenHandler for PlayerScreenHandler { &mut self.behaviour } - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - //TODO: this.craftingResultInventory.clear(); - self.drop_inventory(player, self.crafting_inventory.clone()) - .await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + //TODO: this.craftingResultInventory.clear(); + self.drop_inventory(player, self.crafting_inventory.clone()); } /// Performs quick move (shift-click) for the given slot. @@ -153,116 +146,100 @@ impl ScreenHandler for PlayerScreenHandler { /// - Offhand items -> Offhand slot if empty /// - Main inventory (9-35) -> Hotbar /// - Hotbar (36-44) -> Main inventory - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let slot = self.get_behaviour().slots[slot_index as usize].clone(); + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let slot = self.get_behaviour().slots[slot_index as usize].clone(); - // TODO: Equippable component + // TODO: Equippable component - if slot.has_stack().await { - let mut slot_stack = slot.get_stack().await; - let stack_prev = slot_stack.clone(); + if slot.has_stack() { + let mut slot_stack = slot.get_stack(); + let stack_prev = slot_stack.clone(); - let equipment_slot = slot_stack - .get_data_component::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); + let equipment_slot = slot_stack + .get_data_component::() + .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); - // Quick move logic based on source slot - let success = if slot_index == 0 { - // From crafting result slot (0) -> Player Inventory (9-45, from end) - self.insert_item(&mut slot_stack, 9, 45, true).await - } else if (1..5).contains(&slot_index) { - // From craft ingredient slots (1-4) -> Player Inventory (9-45, from start) - self.insert_item(&mut slot_stack, 9, 45, false).await - } else if (5..9).contains(&slot_index) { - // From armour slots (5-8) -> Player Inventory (9-45, from start) - let result = self.insert_item(&mut slot_stack, 9, 45, false).await; + // Quick move logic based on source slot + let success = if slot_index == 0 { + // From crafting result slot (0) -> Player Inventory (9-45, from end) + self.insert_item(&mut slot_stack, 9, 45, true) + } else if (1..5).contains(&slot_index) { + // From craft ingredient slots (1-4) -> Player Inventory (9-45, from start) + self.insert_item(&mut slot_stack, 9, 45, false) + } else if (5..9).contains(&slot_index) { + // From armour slots (5-8) -> Player Inventory (9-45, from start) + let result = self.insert_item(&mut slot_stack, 9, 45, false); - if result { - player - .enqueue_equipment_change(equipment_slot, ItemStack::EMPTY) - .await; - } - result - } else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor - && self - .get_slot((8 - equipment_slot.get_entity_slot_id()) as usize) - .get_cloned_stack() - .await - .is_empty() - { - // Into empty armour slots (5-8) - let index = 8 - equipment_slot.get_entity_slot_id(); - let result = self - .insert_item(&mut slot_stack, index, index + 1, false) - .await; - - if result { - player - .enqueue_equipment_change(equipment_slot, &stack_prev) - .await; - } - result - } else if matches!(equipment_slot, EquipmentSlot::OffHand(_)) - && slot_index != 45 - && self.get_slot(45).get_cloned_stack().await.is_empty() - { - // Into empty offhand slot (45) - let index = 45; - self.insert_item(&mut slot_stack, index, index + 1, false) - .await - } else if (9..36).contains(&slot_index) { - // From main inventory (9-35) -> Hotbar (36-44) - self.insert_item(&mut slot_stack, 36, 45, false).await - } else if (36..45).contains(&slot_index) { - // From hotbar (36-44) -> Main inventory (9-35) - self.insert_item(&mut slot_stack, 9, 36, false).await - } else { - // Fallback to moving into the player inventory area - self.insert_item(&mut slot_stack, 9, 45, false).await - }; - - if !success { - return ItemStack::EMPTY.clone(); + if result { + player.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY); } + result + } else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor + && self + .get_slot((8 - equipment_slot.get_entity_slot_id()) as usize) + .get_cloned_stack() + .is_empty() + { + // Into empty armour slots (5-8) + let index = 8 - equipment_slot.get_entity_slot_id(); + let result = self.insert_item(&mut slot_stack, index, index + 1, false); - let stack = slot_stack.clone(); - - if stack.is_empty() { - slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()) - .await; - } else { - slot.set_stack(stack.clone()).await; + if result { + player.enqueue_equipment_change(equipment_slot, &stack_prev); } + result + } else if matches!(equipment_slot, EquipmentSlot::OffHand(_)) + && slot_index != 45 + && self.get_slot(45).get_cloned_stack().is_empty() + { + // Into empty offhand slot (45) + let index = 45; + self.insert_item(&mut slot_stack, index, index + 1, false) + } else if (9..36).contains(&slot_index) { + // From main inventory (9-35) -> Hotbar (36-44) + self.insert_item(&mut slot_stack, 36, 45, false) + } else if (36..45).contains(&slot_index) { + // From hotbar (36-44) -> Main inventory (9-35) + self.insert_item(&mut slot_stack, 9, 36, false) + } else { + // Fallback to moving into the player inventory area + self.insert_item(&mut slot_stack, 9, 45, false) + }; - if stack.item_count == stack_prev.item_count { - return ItemStack::EMPTY.clone(); - } - - let mut taken_stack = stack_prev.clone(); - taken_stack.set_count(stack_prev.item_count - stack.item_count); - slot.on_take_item(player, &taken_stack).await; - - if slot_index == 0 { - // From crafting result slot (0) - // Notify the result slot to refill - slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()) - .await; - // For crafting result slot, drop any remaining items - if !stack.is_empty() { - player.drop_item(stack, false).await; - } - } - - return stack_prev; + if !success { + return ItemStack::EMPTY.clone(); } - // Nothing changed - ItemStack::EMPTY.clone() - }) + let stack = slot_stack.clone(); + + if stack.is_empty() { + slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()); + } else { + slot.set_stack(stack.clone()); + } + + if stack.item_count == stack_prev.item_count { + return ItemStack::EMPTY.clone(); + } + + let mut taken_stack = stack_prev.clone(); + taken_stack.set_count(stack_prev.item_count - stack.item_count); + slot.on_take_item(player, &taken_stack); + + if slot_index == 0 { + // From crafting result slot (0) + // Notify the result slot to refill + slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()); + // For crafting result slot, drop any remaining items + if !stack.is_empty() { + player.drop_item(stack, false); + } + } + + return stack_prev; + } + + // Nothing changed + ItemStack::EMPTY.clone() } } diff --git a/crates/pumpkin-inventory/src/screen_handler.rs b/crates/pumpkin-inventory/src/screen_handler.rs index 8b32fc231..e43542e52 100644 --- a/crates/pumpkin-inventory/src/screen_handler.rs +++ b/crates/pumpkin-inventory/src/screen_handler.rs @@ -53,10 +53,10 @@ use pumpkin_world::{ block::entities::PropertyDelegate, inventory::{ComparableInventory, Inventory}, }; +use std::cmp::max; +use std::sync::Mutex; use std::sync::atomic::{AtomicU32, Ordering}; use std::{any::Any, collections::HashMap, sync::Arc}; -use std::{cmp::max, pin::Pin}; -use tokio::sync::Mutex; use tracing::warn; /// Slot index indicating a click outside the inventory. @@ -108,10 +108,6 @@ impl ScreenProperty { } } -/// Type alias for async player operations. -/// Type alias for async player operations. -pub type PlayerFuture<'a, T> = Pin + Send + 'a>>; - /// Interface for player interactions with containers. /// /// This trait abstracts the player's ability to: @@ -128,7 +124,7 @@ pub trait InventoryPlayer: Send + Sync { /// # Arguments /// - `item` - The item to drop /// - `retain_ownership` - If true, the player keeps ownership (for pickup delay) - fn drop_item(&self, item: ItemStack, retain_ownership: bool) -> PlayerFuture<'_, ()>; + fn drop_item(&self, item: ItemStack, retain_ownership: bool); /// Gets the player's inventory. fn get_inventory(&self) -> Arc; @@ -143,113 +139,86 @@ pub trait InventoryPlayer: Send + Sync { fn experience_level(&self) -> i32; /// Adds or removes experience levels. - fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()>; + fn add_experience_levels(&self, levels: i32); /// Gets the player's enchantment seed. fn enchantment_seed(&self) -> i32; /// Sets the player's enchantment seed. - fn set_enchantment_seed(&self, seed: i32) -> PlayerFuture<'_, ()>; + fn set_enchantment_seed(&self, seed: i32); /// Sends a full container content packet. - fn enqueue_inventory_packet<'a>( - &'a self, - packet: &'a CSetContainerContent, + fn enqueue_inventory_packet( + &self, + packet: &CSetContainerContent, window_type: Option, - ) -> PlayerFuture<'a, ()>; + ); /// Sends a single slot update packet. - fn enqueue_slot_packet<'a>( - &'a self, - packet: &'a CSetContainerSlot, + fn enqueue_slot_packet( + &self, + packet: &CSetContainerSlot, window_type: Option, total_slots: usize, - ) -> PlayerFuture<'a, ()>; + ); /// Sends a cursor item update packet. - fn enqueue_cursor_packet<'a>(&'a self, packet: &'a CSetCursorItem) -> PlayerFuture<'a, ()>; + fn enqueue_cursor_packet(&self, packet: &CSetCursorItem); /// Sends a property update packet. - fn enqueue_property_packet<'a>( - &'a self, - packet: &'a CSetContainerProperty, - ) -> PlayerFuture<'a, ()>; + fn enqueue_property_packet(&self, packet: &CSetContainerProperty); /// Sends a player inventory slot update. - fn enqueue_slot_set_packet<'a>( - &'a self, - packet: &'a CSetPlayerInventory, - ) -> PlayerFuture<'a, ()>; + fn enqueue_slot_set_packet(&self, packet: &CSetPlayerInventory); /// Sends a selected slot update. - fn enqueue_set_held_item_packet<'a>( - &'a self, - packet: &'a CSetSelectedSlot, - ) -> PlayerFuture<'a, ()>; + fn enqueue_set_held_item_packet(&self, packet: &CSetSelectedSlot); /// Sends an equipment change packet. - fn enqueue_equipment_change<'a>( - &'a self, - slot: &'a EquipmentSlot, - stack: &'a ItemStack, - ) -> PlayerFuture<'a, ()>; + fn enqueue_equipment_change(&self, slot: &EquipmentSlot, stack: &ItemStack); /// Awards experience points to the player (used for furnace smelting, etc.) - fn award_experience(&self, amount: i32) -> PlayerFuture<'_, ()>; + fn award_experience(&self, amount: i32); /// Increments a statistic for the player. - fn increment_stat( - &self, - category: StatisticCategory, - stat_id: i32, - amount: i32, - ) -> PlayerFuture<'_, ()>; + fn increment_stat(&self, category: StatisticCategory, stat_id: i32, amount: i32); /// Fires a prepare item enchant event. Returns true if cancelled. - fn fire_prepare_item_enchant_event<'a>( - &'a self, - _item: &'a ItemStack, - _level_requirements: &'a mut [i32; 3], - _enchantment_id: &'a mut [i32; 3], - _enchantment_level: &'a mut [i32; 3], + fn fire_prepare_item_enchant_event( + &self, + _item: &ItemStack, + _level_requirements: &mut [i32; 3], + _enchantment_id: &mut [i32; 3], + _enchantment_level: &mut [i32; 3], _bookshelf_count: i32, - ) -> PlayerFuture<'a, bool> { - Box::pin(async move { false }) + ) -> bool { + false } /// Fires an enchant item event. Returns true if cancelled. - fn fire_enchant_item_event<'a>( - &'a self, - _item: &'a ItemStack, + fn fire_enchant_item_event( + &self, + _item: &ItemStack, _option: i32, _exp_level_cost: i32, - _enchantments_to_add: &'a mut Vec<(&'static Enchantment, i32)>, - ) -> PlayerFuture<'a, bool> { - Box::pin(async move { false }) + _enchantments_to_add: &mut Vec<(&'static Enchantment, i32)>, + ) -> bool { + false } + + /// Closes the player's current handled screen. + fn close_screen_handler(&self) {} } /// Gives a stack to the player or drops it if inventory is full. /// /// Tries to insert the stack into the player's inventory first, /// and drops it in the world if there's no room. -pub async fn offer_or_drop_stack(player: &dyn InventoryPlayer, stack: ItemStack) { +pub fn offer_or_drop_stack(player: &dyn InventoryPlayer, stack: ItemStack) { // TODO: Super weird disconnect logic in vanilla, investigate this later - player - .get_inventory() - .offer_or_drop_stack(stack, player) - .await; + player.get_inventory().offer_or_drop_stack(stack, player); } -/// Type alias for async screen handler operations. -pub type ScreenHandlerFuture<'a, T> = Pin + Send + 'a>>; - -/// Future type that returns an `ItemStack` (used by `quick_move`). -pub type ItemStackFuture<'a> = ScreenHandlerFuture<'a, ItemStack>; - -/// Future type that returns an optional slot index. -pub type OptionUsizeFuture<'a> = ScreenHandlerFuture<'a, Option>; - /// The main trait for container screen handlers. /// /// Screen handlers manage the server-side state of container UIs like chests, @@ -375,312 +344,261 @@ pub trait ScreenHandler: Send + Sync { } } - // --- Asynchronous Methods --- - /// Called when the container is closed by the player. /// /// Default implementation drops the cursor item. - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); } /// Default close behavior - drops the cursor item. - fn default_on_closed<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); + fn default_on_closed(&mut self, player: &dyn InventoryPlayer) { + let behaviour = self.get_behaviour_mut(); - // Lock and clone are performed inside the async block - let mut cursor_stack_lock = behaviour.cursor_stack.lock().await; + let mut cursor_stack_lock = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - if !cursor_stack_lock.is_empty() { - offer_or_drop_stack(player, cursor_stack_lock.clone()).await; - *cursor_stack_lock = ItemStack::EMPTY.clone(); - } - }) + if !cursor_stack_lock.is_empty() { + offer_or_drop_stack(player, cursor_stack_lock.clone()); + *cursor_stack_lock = ItemStack::EMPTY.clone(); + } } /// Drops all items from an inventory into the world. - fn drop_inventory<'a>( - &'a self, - player: &'a dyn InventoryPlayer, - inventory: Arc, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - for i in 0..inventory.size() { - offer_or_drop_stack(player, inventory.remove_stack(i).await).await; - } - }) + fn drop_inventory(&self, player: &dyn InventoryPlayer, inventory: Arc) { + for i in 0..inventory.size() { + offer_or_drop_stack(player, inventory.remove_stack(i)); + } } /// Copies tracked slot state from another screen handler. /// /// Used when reopening a container to restore previous state. - fn copy_shared_slots( - &mut self, - other: Arc>, - ) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let mut table: HashMap> = HashMap::new(); - let other_binding = other.lock().await; - let other_behaviour = other_binding.get_behaviour(); + fn copy_shared_slots(&mut self, other: Arc>) { + let mut table: HashMap> = HashMap::new(); + let other_binding = other + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let other_behaviour = other_binding.get_behaviour(); - for i in 0..other_behaviour.slots.len() { - let other_slot = other_behaviour.slots[i].clone(); - let mut hash_map = HashMap::new(); - hash_map.insert(other_slot.get_index(), i); - table.insert( - ComparableInventory(other_slot.get_inventory().clone()), - hash_map, - ); + for i in 0..other_behaviour.slots.len() { + let other_slot = other_behaviour.slots[i].clone(); + let mut hash_map = HashMap::new(); + hash_map.insert(other_slot.get_index(), i); + table.insert( + ComparableInventory(other_slot.get_inventory().clone()), + hash_map, + ); + } + + for i in 0..self.get_behaviour().slots.len() { + let slot = self.get_behaviour().slots[i].clone(); + let inventory = slot.get_inventory(); + let index = slot.get_index(); + + if let Some(hash_map) = table.get(&ComparableInventory(inventory.clone())) + && let Some(other_index) = hash_map.get(&index) + { + self.get_behaviour_mut().tracked_stacks[i] = + other_behaviour.tracked_stacks[*other_index].clone(); + self.get_behaviour_mut().previous_tracked_stacks[i] = + other_behaviour.previous_tracked_stacks[*other_index].clone(); } - - for i in 0..self.get_behaviour().slots.len() { - let slot = self.get_behaviour().slots[i].clone(); - let inventory = slot.get_inventory(); - let index = slot.get_index(); - - if let Some(hash_map) = table.get(&ComparableInventory(inventory.clone())) - && let Some(other_index) = hash_map.get(&index) - { - self.get_behaviour_mut().tracked_stacks[i] = - other_behaviour.tracked_stacks[*other_index].clone(); - self.get_behaviour_mut().previous_tracked_stacks[i] = - other_behaviour.previous_tracked_stacks[*other_index].clone(); - } - } - }) + } } /// Synchronizes the full state to the client. /// /// Captures current slot states and sends a full update packet. - fn sync_state(&mut self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - let mut previous_tracked_stacks = Vec::new(); + fn sync_state(&mut self) { + let behaviour = self.get_behaviour_mut(); + let mut previous_tracked_stacks = Vec::new(); - for i in 0..behaviour.slots.len() { - let stack = behaviour.slots[i].get_cloned_stack().await; - previous_tracked_stacks.push(stack.clone()); - behaviour.previous_tracked_stacks[i].set_received_stack(stack); - } + for i in 0..behaviour.slots.len() { + let stack = behaviour.slots[i].get_cloned_stack(); + previous_tracked_stacks.push(stack.clone()); + behaviour.previous_tracked_stacks[i].set_received_stack(stack); + } - let cursor_stack = behaviour.cursor_stack.lock().await.clone(); - behaviour - .previous_cursor_stack - .set_received_stack(cursor_stack.clone()); + let cursor_stack = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + behaviour + .previous_cursor_stack + .set_received_stack(cursor_stack.clone()); - for i in 0..behaviour.properties.len() { - let property_val = behaviour.properties[i].get(); - behaviour.tracked_property_values[i] = property_val; - } + for i in 0..behaviour.properties.len() { + let property_val = behaviour.properties[i].get(); + behaviour.tracked_property_values[i] = property_val; + } - let next_revision = behaviour.next_revision(); + let next_revision = behaviour.next_revision(); - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_state( - behaviour, - &previous_tracked_stacks, - &cursor_stack, - behaviour.tracked_property_values.clone(), - next_revision, - ) - .await; - } - }) + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler.update_state( + behaviour, + &previous_tracked_stacks, + &cursor_stack, + &behaviour.tracked_property_values, + next_revision, + ); + } } /// Adds a listener for slot and property changes. - fn add_listener( - &mut self, - listener: Arc, - ) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - self.get_behaviour_mut().listeners.push(listener); - self.send_content_updates().await; - }) + fn add_listener(&mut self, listener: Arc) { + self.get_behaviour_mut().listeners.push(listener); + self.send_content_updates(); } /// Attaches a sync handler and performs initial sync. - fn update_sync_handler( - &mut self, - sync_handler: Arc, - ) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - behaviour.sync_handler = Some(sync_handler.clone()); - self.sync_state().await; - }) + fn update_sync_handler(&mut self, sync_handler: Arc) { + let behaviour = self.get_behaviour_mut(); + behaviour.sync_handler = Some(sync_handler); + self.sync_state(); } /// Sends all updates to the client. /// /// Updates tracked slots and properties. - fn update_to_client(&mut self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - for i in 0..self.get_behaviour().slots.len() { - let behaviour = self.get_behaviour_mut(); - let slot = behaviour.slots[i].clone(); - let stack = slot.get_cloned_stack().await; - self.update_tracked_slot(i, stack).await; - } - + fn update_to_client(&mut self) { + for i in 0..self.get_behaviour().slots.len() { let behaviour = self.get_behaviour_mut(); - let mut prop_vec = vec![]; - for (idx, prop) in behaviour.properties.iter_mut().enumerate() { - let value = prop.get(); - if prop.has_changed() { - prop_vec.push((idx, value)); - } - } + let slot = behaviour.slots[i].clone(); + let stack = slot.get_cloned_stack(); + self.update_tracked_slot(i, stack); + } - for (idx, value) in prop_vec { - self.update_tracked_properties(idx as i32, value).await; - self.check_property_updates(idx as i32, value).await; + let behaviour = self.get_behaviour_mut(); + let mut prop_vec = vec![]; + for (idx, prop) in behaviour.properties.iter_mut().enumerate() { + let value = prop.get(); + if prop.has_changed() { + prop_vec.push((idx, value)); } + } - self.sync_state().await; - }) + for (idx, value) in prop_vec { + self.update_tracked_properties(idx as i32, value); + self.check_property_updates(idx as i32, value); + } + + self.sync_state(); } /// Updates a tracked property value. - fn update_tracked_properties(&mut self, idx: i32, value: i32) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - if idx <= behaviour.tracked_property_values.len() as i32 { - behaviour.tracked_property_values[idx as usize] = value; - for listener in &behaviour.listeners { - listener - .on_property_update(behaviour, idx as u8, value) - .await; - } + fn update_tracked_properties(&mut self, idx: i32, value: i32) { + let behaviour = self.get_behaviour_mut(); + if idx <= behaviour.tracked_property_values.len() as i32 { + behaviour.tracked_property_values[idx as usize] = value; + for listener in &behaviour.listeners { + listener.on_property_update(behaviour, idx as u8, value); } - }) + } } /// Checks if a property needs to be synced to the client. - fn check_property_updates(&mut self, idx: i32, value: i32) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync - && let Some(old_value) = behaviour.tracked_property_values.get(idx as usize) - { - let old_value = *old_value; - if old_value != value { - behaviour - .tracked_property_values - .insert(idx as usize, value); - if let Some(ref sync_handler) = behaviour.sync_handler { - sync_handler.update_property(behaviour, idx, value).await; - } + fn check_property_updates(&mut self, idx: i32, value: i32) { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync + && let Some(old_value) = behaviour.tracked_property_values.get(idx as usize) + { + let old_value = *old_value; + if old_value != value { + behaviour + .tracked_property_values + .insert(idx as usize, value); + if let Some(ref sync_handler) = behaviour.sync_handler { + sync_handler.update_property(behaviour, idx, value); } } - }) + } } /// Updates the tracked state of a slot. - fn update_tracked_slot( - &mut self, - slot: usize, - stack: ItemStack, - ) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - let other_stack = &behaviour.tracked_stacks[slot]; - if !other_stack.are_equal(&stack) { - behaviour.tracked_stacks[slot] = stack.clone(); + fn update_tracked_slot(&mut self, slot: usize, stack: ItemStack) { + let behaviour = self.get_behaviour_mut(); + let other_stack = &behaviour.tracked_stacks[slot]; + if !other_stack.are_equal(&stack) { + behaviour.tracked_stacks[slot] = stack.clone(); - for listener in &behaviour.listeners { - listener - .on_slot_update(behaviour, slot as u8, stack.clone()) - .await; - } + for listener in &behaviour.listeners { + listener.on_slot_update(behaviour, slot as u8, stack.clone()); } - }) + } } /// Checks if a slot needs to be synced to the client. - fn check_slot_updates(&mut self, slot: usize, stack: ItemStack) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync { - let prev_stack = &mut behaviour.previous_tracked_stacks[slot]; + fn check_slot_updates(&mut self, slot: usize, stack: ItemStack) { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync { + let prev_stack = &mut behaviour.previous_tracked_stacks[slot]; - if !prev_stack.is_in_sync(&stack) { - prev_stack.set_received_stack(stack.clone()); - let next_revision = behaviour.next_revision(); - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_slot(behaviour, slot, &stack, next_revision) - .await; - } + if !prev_stack.is_in_sync(&stack) { + prev_stack.set_received_stack(stack.clone()); + let next_revision = behaviour.next_revision(); + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler.update_slot(behaviour, slot, &stack, next_revision); } } - }) + } } /// Checks if the cursor stack needs to be synced. - fn check_cursor_stack_updates(&mut self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync { - let cursor_stack = behaviour.cursor_stack.lock().await; - if !behaviour.previous_cursor_stack.is_in_sync(&cursor_stack) { - behaviour - .previous_cursor_stack - .set_received_stack(cursor_stack.clone()); - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_cursor_stack(behaviour, &cursor_stack) - .await; - } + fn check_cursor_stack_updates(&mut self) { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync { + let cursor_stack = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !behaviour.previous_cursor_stack.is_in_sync(&cursor_stack) { + behaviour + .previous_cursor_stack + .set_received_stack(cursor_stack.clone()); + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler.update_cursor_stack(behaviour, &cursor_stack); } } - }) + } } /// Sends all content updates to listeners and sync handler. - fn send_content_updates(&mut self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let slots_len = self.get_behaviour().slots.len(); + fn send_content_updates(&mut self) { + let slots_len = self.get_behaviour().slots.len(); - for i in 0..slots_len { - let slot = self.get_behaviour().slots[i].clone(); - let stack = slot.get_cloned_stack().await; + for i in 0..slots_len { + let slot = self.get_behaviour().slots[i].clone(); + let stack = slot.get_cloned_stack(); - self.update_tracked_slot(i, stack.clone()).await; - self.check_slot_updates(i, stack).await; + self.update_tracked_slot(i, stack.clone()); + self.check_slot_updates(i, stack); + } + + self.check_cursor_stack_updates(); + + let behaviour = self.get_behaviour_mut(); + let mut prop_vec = vec![]; + for (idx, prop) in behaviour.properties.iter_mut().enumerate() { + let value = prop.get(); + if prop.has_changed() { + prop_vec.push((idx, value)); } + } - self.check_cursor_stack_updates().await; - - let behaviour = self.get_behaviour_mut(); - let mut prop_vec = vec![]; - for (idx, prop) in behaviour.properties.iter_mut().enumerate() { - let value = prop.get(); - if prop.has_changed() { - prop_vec.push((idx, value)); - } - } - - for (idx, value) in prop_vec { - self.update_tracked_properties(idx as i32, value).await; - self.check_property_updates(idx as i32, value).await; - } - }) + for (idx, value) in prop_vec { + self.update_tracked_properties(idx as i32, value); + self.check_property_updates(idx as i32, value); + } } /// Checks if a slot index is valid. - fn is_slot_valid(&self, slot: i32) -> ScreenHandlerFuture<'_, bool> { - Box::pin(async move { - slot == -1 || slot == -999 || slot < self.get_behaviour().slots.len() as i32 - }) + fn is_slot_valid(&self, slot: i32) -> bool { + slot == -1 || slot == -999 || slot < self.get_behaviour().slots.len() as i32 } /// Disables synchronization (for batch operations). @@ -696,16 +614,10 @@ pub trait ScreenHandler: Send + Sync { } /// Gets the screen handler slot index for an inventory slot. - fn get_slot_index<'a>( - &'a self, - inventory: &'a Arc, - slot: usize, - ) -> OptionUsizeFuture<'a> { - Box::pin(async move { - (0..self.get_behaviour().slots.len()).find(|&i| { - Arc::ptr_eq(&self.get_behaviour().slots[i].get_inventory(), inventory) - && self.get_behaviour().slots[i].get_index() == slot - }) + fn get_slot_index(&self, inventory: &Arc, slot: usize) -> Option { + (0..self.get_behaviour().slots.len()).find(|&i| { + Arc::ptr_eq(&self.get_behaviour().slots[i].get_inventory(), inventory) + && self.get_behaviour().slots[i].get_index() == slot }) } @@ -713,610 +625,577 @@ pub trait ScreenHandler: Send + Sync { /// /// Must be implemented by concrete screen handlers to define /// where items go when shift-clicked from specific slots. - fn quick_move<'a>( - &'a mut self, - player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a>; + fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack; /// Handles a button click event (e.g., enchantment selection, beacon effects). - fn on_button_click<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - _button_id: i32, - ) -> ScreenHandlerFuture<'a, bool> { - Box::pin(async { false }) + fn on_button_click(&mut self, _player: &dyn InventoryPlayer, _button_id: i32) -> bool { + false } /// Inserts an item into a range of slots. /// /// First tries to stack with existing items, then fills empty slots. - fn insert_item<'a>( - &'a mut self, - stack: &'a mut ItemStack, + fn insert_item( + &mut self, + stack: &mut ItemStack, start_index: i32, end_index: i32, from_last: bool, - ) -> ScreenHandlerFuture<'a, bool> { - Box::pin(async move { - let mut success = false; - let mut current_index = if from_last { - end_index - 1 - } else { - start_index - }; + ) -> bool { + let mut success = false; + let mut current_index = if from_last { + end_index - 1 + } else { + start_index + }; - if stack.is_stackable() { - while !stack.is_empty() - && (if from_last { - current_index >= start_index - } else { - current_index < end_index - }) - { - let slot = self.get_behaviour().slots[current_index as usize].clone(); - let mut slot_stack = slot.get_stack().await; - - if !slot_stack.is_empty() && slot_stack.are_items_and_components_equal(stack) { - let combined_count = slot_stack.item_count + stack.item_count; - let max_slot_count = slot.get_max_item_count_for_stack(&slot_stack).await; - if combined_count <= max_slot_count { - stack.set_count(0); - slot_stack.set_count(combined_count); - slot.set_stack(slot_stack).await; - success = true; - } else if slot_stack.item_count < max_slot_count { - stack.decrement(max_slot_count - slot_stack.item_count); - slot_stack.set_count(max_slot_count); - slot.set_stack(slot_stack).await; - success = true; - } - } - - if from_last { - current_index -= 1; - } else { - current_index += 1; - } - } - } - - if !stack.is_empty() { - if from_last { - current_index = end_index - 1; - } else { - current_index = start_index; - } - - while if from_last { + if stack.is_stackable() { + while !stack.is_empty() + && (if from_last { current_index >= start_index } else { current_index < end_index - } { - let slot = self.get_behaviour().slots[current_index as usize].clone(); - let slot_stack = slot.get_stack().await; + }) + { + let slot = self.get_behaviour().slots[current_index as usize].clone(); + let mut slot_stack = slot.get_stack(); - if slot_stack.is_empty() && slot.can_insert(stack).await { - let max_count = slot.get_max_item_count_for_stack(stack).await; - slot.set_stack(stack.split(max_count.min(stack.item_count))) - .await; - slot.mark_dirty().await; + if !slot_stack.is_empty() && slot_stack.are_items_and_components_equal(stack) { + let combined_count = slot_stack.item_count + stack.item_count; + let max_slot_count = slot.get_max_item_count_for_stack(&slot_stack); + if combined_count <= max_slot_count { + stack.set_count(0); + slot_stack.set_count(combined_count); + slot.set_stack(slot_stack); + success = true; + } else if slot_stack.item_count < max_slot_count { + stack.decrement(max_slot_count - slot_stack.item_count); + slot_stack.set_count(max_slot_count); + slot.set_stack(slot_stack); success = true; - break; - } - - if from_last { - current_index -= 1; - } else { - current_index += 1; } } + + if from_last { + current_index -= 1; + } else { + current_index += 1; + } + } + } + + if !stack.is_empty() { + if from_last { + current_index = end_index - 1; + } else { + current_index = start_index; } - success - }) + while if from_last { + current_index >= start_index + } else { + current_index < end_index + } { + let slot = self.get_behaviour().slots[current_index as usize].clone(); + let slot_stack = slot.get_stack(); + + if slot_stack.is_empty() && slot.can_insert(stack) { + let max_count = slot.get_max_item_count_for_stack(stack); + slot.set_stack(stack.split(max_count.min(stack.item_count))); + slot.mark_dirty(); + success = true; + break; + } + + if from_last { + current_index -= 1; + } else { + current_index += 1; + } + } + } + + success } /// Handles a slot click event. /// /// Override for custom click handling. Return true to prevent default handling. - fn handle_slot_click<'a>( - &'a self, - _player: &'a dyn InventoryPlayer, + fn handle_slot_click( + &self, + _player: &dyn InventoryPlayer, _click_type: MouseClick, _slot: Arc, _slot_stack: ItemStack, _cursor_stack: ItemStack, - ) -> ScreenHandlerFuture<'a, bool> { - Box::pin(async { - // TODO: required for bundle in the future - false - }) + ) -> bool { + // TODO: required for bundle in the future + false } /// Cancels any client-side changes and resynchronizes the state. - fn cancel(&mut self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - self.sync_state().await; - }) + fn cancel(&mut self) { + self.sync_state(); } /// Public entry point for slot click handling. - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); } /// Internal slot click handling implementation. /// /// Handles all click types: pickup, quick move, swap, throw, drag, clone. #[expect(clippy::too_many_lines)] - fn internal_on_slot_click<'a>( - &'a mut self, + fn internal_on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - if action_type == SlotActionType::PickupAll && button == 0 { - let behavior = self.get_behaviour_mut(); - let mut cursor_stack = behavior.cursor_stack.lock().await; - let mut to_pick_up = cursor_stack.get_max_stack_size() - cursor_stack.item_count; + player: &dyn InventoryPlayer, + ) { + if action_type == SlotActionType::PickupAll && button == 0 { + let behavior = self.get_behaviour_mut(); + let mut cursor_stack = behavior + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut to_pick_up = cursor_stack.get_max_stack_size() - cursor_stack.item_count; - for slot in &behavior.slots { - if to_pick_up == 0 { - break; - } - - let item_stack = slot.get_cloned_stack().await; - if !item_stack.are_items_and_components_equal(&cursor_stack) { - continue; - } - - if !slot.allow_modification(player).await { - continue; - } - - let taken_stack = slot - .safe_take( - item_stack.item_count.min(to_pick_up), - cursor_stack.get_max_stack_size() - cursor_stack.item_count, - player, - ) - .await; - to_pick_up -= taken_stack.item_count; - cursor_stack.increment(taken_stack.item_count); + for slot in &behavior.slots { + if to_pick_up == 0 { + break; } - } else if action_type == SlotActionType::QuickCraft { - let drag_type = button & 3; - let drag_button = (button >> 2) & 3; - let behaviour = self.get_behaviour_mut(); - if drag_type == 0 { - behaviour.drag_slots.clear(); - } else if drag_type == 1 { - if slot_index < 0 { - warn!("Invalid slot index for drag action: {slot_index}. Must be >= 0"); - return; - } - let cursor_stack = behaviour.cursor_stack.lock().await; - let slot = &behaviour.slots[slot_index as usize]; - let stack = slot.get_stack().await; - if !cursor_stack.is_empty() - && slot.can_insert(&cursor_stack).await - && (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) - && slot.get_max_item_count_for_stack(&stack).await > stack.item_count - { - behaviour.drag_slots.push(slot_index as u32); - } - } else if drag_type == 2 && !behaviour.drag_slots.is_empty() { - // process drag end - if behaviour.drag_slots.len() == 1 { - let slot = behaviour.drag_slots[0] as i32; - behaviour.drag_slots.clear(); - let _ = behaviour; - self.internal_on_slot_click( - slot, - drag_button, - SlotActionType::Pickup, - player, - ) - .await; - - return; - } - if drag_button == 2 && !player.has_infinite_materials() { - return; // Only creative - } - - let mut cursor_stack = behaviour.cursor_stack.lock().await; - let initial_count = cursor_stack.item_count; - let slots_count = behaviour.drag_slots.len(); - for slot_index in &behaviour.drag_slots { - let Some(slot) = behaviour.slots.get(*slot_index as usize).cloned() else { - continue; - }; - let stack = slot.get_stack().await; - - if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) - && slot.can_insert(&cursor_stack).await - { - let mut inserting_count = match drag_button { - 0 => (initial_count as usize) - .checked_div(slots_count) - .map_or(0, |c| c as u8), - 1 => 1, - 2 => { - cursor_stack.item_count = cursor_stack.get_max_stack_size(); - cursor_stack.item_count - } - _ => 0, - }; - inserting_count = inserting_count - .min(max( - 0, - slot.get_max_item_count_for_stack(&stack).await - - stack.item_count, - )) - .min(cursor_stack.item_count); - if inserting_count > 0 { - let mut new_stack = stack.clone(); - if new_stack.is_empty() { - new_stack = cursor_stack.copy_with_count(0); - } - new_stack.increment(inserting_count); - slot.set_stack(new_stack).await; - if drag_button != 2 { - cursor_stack.decrement(inserting_count); - } - if cursor_stack.is_empty() { - *cursor_stack = ItemStack::EMPTY.clone(); - break; - } - } - } - } - - if drag_button == 2 { - *cursor_stack = ItemStack::EMPTY.clone(); - } - behaviour.drag_slots.clear(); + let item_stack = slot.get_cloned_stack(); + if !item_stack.are_items_and_components_equal(&cursor_stack) { + continue; } - } else if action_type == SlotActionType::Throw { - if slot_index >= 0 && self.get_behaviour().cursor_stack.lock().await.is_empty() { - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - let prev_stack = slot.get_cloned_stack().await; - if !prev_stack.is_empty() { - if button == 1 { - // Throw all - while slot - .get_cloned_stack() - .await - .are_items_and_components_equal(&prev_stack) - { - let drop_stack = - slot.safe_take(prev_stack.item_count, u8::MAX, player).await; - player.drop_item(drop_stack, true).await; - // player.handleCreativeModeItemDrop(itemStack); - } - } else { - let drop_stack = slot.safe_take(1, u8::MAX, player).await; - if !drop_stack.is_empty() { - slot.on_take_item(player, &drop_stack).await; - player.drop_item(drop_stack, true).await; - } - } - } + + if !slot.allow_modification(player) { + continue; } - } else if action_type == SlotActionType::Clone { - if player.has_infinite_materials() && slot_index >= 0 { - let behaviour = self.get_behaviour_mut(); - let mut cursor_stack = behaviour.cursor_stack.lock().await; - if !cursor_stack.is_empty() { - return; - } - let slot = behaviour.slots[slot_index as usize].clone(); - let stack = slot.get_stack().await; - *cursor_stack = stack.copy_with_count(stack.get_max_stack_size()); - } - } else if (action_type == SlotActionType::Pickup - || action_type == SlotActionType::QuickMove) - && (button == 0 || button == 1) - { - let click_type = if button == 0 { - MouseClick::Left - } else { - MouseClick::Right - }; - // Drop item if outside inventory - if slot_index == SLOT_INDEX_OUTSIDE { - let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; - if !cursor_stack.is_empty() { - if click_type == MouseClick::Left { - player.drop_item(cursor_stack.clone(), true).await; - *cursor_stack = ItemStack::EMPTY.clone(); - } else { - player.drop_item(cursor_stack.split(1), true).await; - } - } - } else if action_type == SlotActionType::QuickMove { - if slot_index < 0 { - return; - } - - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if !slot.can_take_items(player).await { - return; - } - - let mut moved_stack = self.quick_move(player, slot_index).await; - - while !moved_stack.is_empty() - && ItemStack::are_items_and_components_equal( - &slot.get_cloned_stack().await, - &moved_stack, - ) - { - moved_stack = self.quick_move(player, slot_index).await; - } - } else { - // Pickup - if slot_index < 0 { - return; - } - - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if click_type == MouseClick::Left { - slot.on_click(player).await; - } - - let slot_stack = slot.get_cloned_stack().await; - let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; - - if click_type == MouseClick::Right { - let mut intercepted = false; - - if !cursor_stack.is_empty() { - let mut inner_slot_stack = slot.get_stack().await; - if let Some(bundle) = inner_slot_stack.get_data_component_mut::() - && bundle.try_insert(&mut cursor_stack) { - slot.set_stack(inner_slot_stack).await; - intercepted = true; - } - } - - if !intercepted && !slot_stack.is_empty() - && let Some(bundle) = cursor_stack.get_data_component_mut::() { - let mut inner_slot_stack = slot.get_stack().await; - if bundle.try_insert(&mut inner_slot_stack) { - if inner_slot_stack.item_count == 0 { - inner_slot_stack = ItemStack::EMPTY.clone(); - } - slot.set_stack(inner_slot_stack).await; - intercepted = true; - } - } - - if !intercepted && cursor_stack.is_empty() { - let mut inner_slot_stack = slot.get_stack().await; - if let Some(bundle) = inner_slot_stack.get_data_component_mut::() - && let Some(extracted) = bundle.try_extract() { - *cursor_stack = extracted; - slot.set_stack(inner_slot_stack).await; - intercepted = true; - } - } - - if !intercepted && slot_stack.is_empty() - && let Some(bundle) = cursor_stack.get_data_component_mut::() - && let Some(extracted) = bundle.try_extract() { - slot.set_stack(extracted).await; - intercepted = true; - } - - if intercepted { - if cursor_stack.item_count == 0 { - *cursor_stack = ItemStack::EMPTY.clone(); - } - slot.mark_dirty().await; - return; - } - } - - let equipment_slot = cursor_stack - .get_data_component::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); - - if self - .handle_slot_click( - player, - click_type.clone(), - slot.clone(), - slot_stack.clone(), - cursor_stack.clone(), - ) - .await - { - return; - } - - if slot_stack.is_empty() { - if !cursor_stack.is_empty() { - if equipment_slot.slot_type() == EquipmentType::HumanoidArmor - && (5..9).contains(&slot_index) - { - player - .enqueue_equipment_change(equipment_slot, &cursor_stack) - .await; - } - - let transfer_count = if click_type == MouseClick::Left { - cursor_stack.item_count - } else { - 1 - }; - *cursor_stack = slot - .insert_stack_count(cursor_stack.clone(), transfer_count) - .await; - } - } else if slot.can_take_items(player).await { - if cursor_stack.is_empty() { - let take_count = if click_type == MouseClick::Left { - slot_stack.item_count - } else { - slot_stack.item_count.div_ceil(2) - }; - let taken = - slot.try_take_stack_range(take_count, u8::MAX, player).await; - if let Some(taken) = taken { - // Reverse order of operations, shouldn't affect anything - *cursor_stack = taken.clone(); - slot.on_take_item(player, &taken).await; - - if (5..9).contains(&slot_index) { - let equipment_slot = cursor_stack - .get_data_component::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| { - equippable.slot - }); - player - .enqueue_equipment_change(equipment_slot, ItemStack::EMPTY) - .await; - } - } - } else if slot.can_insert(&cursor_stack).await { - if equipment_slot.slot_type() == EquipmentType::HumanoidArmor - && (5..9).contains(&slot_index) - { - player - .enqueue_equipment_change(equipment_slot, &cursor_stack) - .await; - } - - if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) - { - let insert_count = if click_type == MouseClick::Left { - cursor_stack.item_count - } else { - 1 - }; - *cursor_stack = slot - .insert_stack_count(cursor_stack.clone(), insert_count) - .await; - } else if cursor_stack.item_count - <= slot.get_max_item_count_for_stack(&cursor_stack).await - { - let old_cursor_stack = cursor_stack.clone(); - *cursor_stack = slot_stack.clone(); - slot.set_stack(old_cursor_stack).await; - } - } else if ItemStack::are_items_and_components_equal( - &slot_stack, - &cursor_stack, - ) { - let taken = slot - .try_take_stack_range( - slot_stack.item_count, - cursor_stack - .get_max_stack_size() - .saturating_sub(cursor_stack.item_count), - player, - ) - .await; - - if let Some(taken) = taken { - cursor_stack.increment(taken.item_count); - slot.on_take_item(player, &taken).await; - } - } - } - - slot.mark_dirty().await; - } - } else if action_type == SlotActionType::Swap && (0..9).contains(&button) - || button == 40 - { + let taken_stack = slot.safe_take( + item_stack.item_count.min(to_pick_up), + cursor_stack.get_max_stack_size() - cursor_stack.item_count, + player, + ); + to_pick_up -= taken_stack.item_count; + cursor_stack.increment(taken_stack.item_count); + } + } else if action_type == SlotActionType::QuickCraft { + let drag_type = button & 3; + let drag_button = (button >> 2) & 3; + let behaviour = self.get_behaviour_mut(); + if drag_type == 0 { + behaviour.drag_slots.clear(); + } else if drag_type == 1 { if slot_index < 0 { + warn!("Invalid slot index for drag action: {slot_index}. Must be >= 0"); return; } - let mut button_stack = player.get_inventory().get_stack(button as usize).await; - let source_slot = self.get_behaviour().slots[slot_index as usize].clone(); - let source_stack = source_slot.get_cloned_stack().await; + let cursor_stack = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - if !button_stack.is_empty() || !source_stack.is_empty() { - if button_stack.is_empty() { - if source_slot.can_take_items(player).await { - player - .get_inventory() - .set_stack(button as usize, source_stack.clone()) - .await; - source_slot.set_stack(ItemStack::EMPTY.clone()).await; - source_slot.on_take_item(player, &source_stack).await; - } - } else if source_stack.is_empty() && source_slot.can_insert(&button_stack).await + let slot = &behaviour.slots[slot_index as usize]; + let stack = slot.get_stack(); + if !cursor_stack.is_empty() + && slot.can_insert(&cursor_stack) + && (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.get_max_item_count_for_stack(&stack) > stack.item_count + { + behaviour.drag_slots.push(slot_index as u32); + } + } else if drag_type == 2 && !behaviour.drag_slots.is_empty() { + // process drag end + if behaviour.drag_slots.len() == 1 { + let slot = behaviour.drag_slots[0] as i32; + behaviour.drag_slots.clear(); + self.internal_on_slot_click(slot, drag_button, SlotActionType::Pickup, player); + + return; + } + if drag_button == 2 && !player.has_infinite_materials() { + return; // Only creative + } + + let mut cursor_stack = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let initial_count = cursor_stack.item_count; + let slots_count = behaviour.drag_slots.len(); + for slot_index in &behaviour.drag_slots { + let Some(slot) = behaviour.slots.get(*slot_index as usize).cloned() else { + continue; + }; + let stack = slot.get_stack(); + + if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.can_insert(&cursor_stack) { - let max_count = source_slot - .get_max_item_count_for_stack(&button_stack) - .await; - if button_stack.item_count > max_count { - // button_stack might need to be a ref instead of a clone - source_slot.set_stack(button_stack.split(max_count)).await; - } else { - player - .get_inventory() - .set_stack(button as usize, ItemStack::EMPTY.clone()) - .await; - source_slot.set_stack(button_stack).await; + let mut inserting_count = match drag_button { + 0 => (initial_count as usize) + .checked_div(slots_count) + .map_or(0, |c| c as u8), + 1 => 1, + 2 => { + cursor_stack.item_count = cursor_stack.get_max_stack_size(); + cursor_stack.item_count + } + _ => 0, + }; + inserting_count = inserting_count + .min(max( + 0, + slot.get_max_item_count_for_stack(&stack) - stack.item_count, + )) + .min(cursor_stack.item_count); + if inserting_count > 0 { + let mut new_stack = stack.clone(); + if new_stack.is_empty() { + new_stack = cursor_stack.copy_with_count(0); + } + new_stack.increment(inserting_count); + slot.set_stack(new_stack); + if drag_button != 2 { + cursor_stack.decrement(inserting_count); + } + if cursor_stack.is_empty() { + *cursor_stack = ItemStack::EMPTY.clone(); + break; + } + } + } + } + + if drag_button == 2 { + *cursor_stack = ItemStack::EMPTY.clone(); + } + behaviour.drag_slots.clear(); + } + } else if action_type == SlotActionType::Throw { + if slot_index >= 0 + && self + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() + { + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let prev_stack = slot.get_cloned_stack(); + if !prev_stack.is_empty() { + if button == 1 { + // Throw all + while slot + .get_cloned_stack() + .are_items_and_components_equal(&prev_stack) + { + let drop_stack = slot.safe_take(prev_stack.item_count, u8::MAX, player); + player.drop_item(drop_stack, true); + // player.handleCreativeModeItemDrop(itemStack); + } + } else { + let drop_stack = slot.safe_take(1, u8::MAX, player); + if !drop_stack.is_empty() { + slot.on_take_item(player, &drop_stack); + player.drop_item(drop_stack, true); } } } } - }) + } else if action_type == SlotActionType::Clone { + if player.has_infinite_materials() && slot_index >= 0 { + let behaviour = self.get_behaviour_mut(); + let mut cursor_stack = behaviour + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !cursor_stack.is_empty() { + return; + } + let slot = behaviour.slots[slot_index as usize].clone(); + let stack = slot.get_stack(); + *cursor_stack = stack.copy_with_count(stack.get_max_stack_size()); + } + } else if (action_type == SlotActionType::Pickup + || action_type == SlotActionType::QuickMove) + && (button == 0 || button == 1) + { + let click_type = if button == 0 { + MouseClick::Left + } else { + MouseClick::Right + }; + + // Drop item if outside inventory + if slot_index == SLOT_INDEX_OUTSIDE { + let mut cursor_stack = self + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !cursor_stack.is_empty() { + if click_type == MouseClick::Left { + player.drop_item(cursor_stack.clone(), true); + *cursor_stack = ItemStack::EMPTY.clone(); + } else { + player.drop_item(cursor_stack.split(1), true); + } + } + } else if action_type == SlotActionType::QuickMove { + if slot_index < 0 { + return; + } + + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if !slot.can_take_items(player) { + return; + } + + let mut moved_stack = self.quick_move(player, slot_index); + + while !moved_stack.is_empty() + && ItemStack::are_items_and_components_equal( + &slot.get_cloned_stack(), + &moved_stack, + ) + { + moved_stack = self.quick_move(player, slot_index); + } + } else { + // Pickup + if slot_index < 0 { + return; + } + + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if click_type == MouseClick::Left { + slot.on_click(player); + } + + let slot_stack = slot.get_cloned_stack(); + let mut cursor_stack = self + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + if click_type == MouseClick::Right { + let mut intercepted = false; + + if !cursor_stack.is_empty() { + let mut inner_slot_stack = slot.get_stack(); + if let Some(bundle) = inner_slot_stack.get_data_component_mut::() + && bundle.try_insert(&mut cursor_stack) { + slot.set_stack(inner_slot_stack); + intercepted = true; + } + } + + if !intercepted && !slot_stack.is_empty() + && let Some(bundle) = cursor_stack.get_data_component_mut::() { + let mut inner_slot_stack = slot.get_stack(); + if bundle.try_insert(&mut inner_slot_stack) { + if inner_slot_stack.item_count == 0 { + inner_slot_stack = ItemStack::EMPTY.clone(); + } + slot.set_stack(inner_slot_stack); + intercepted = true; + } + } + + if !intercepted && cursor_stack.is_empty() { + let mut inner_slot_stack = slot.get_stack(); + if let Some(bundle) = inner_slot_stack.get_data_component_mut::() + && let Some(extracted) = bundle.try_extract() { + *cursor_stack = extracted; + slot.set_stack(inner_slot_stack); + intercepted = true; + } + } + + if !intercepted && slot_stack.is_empty() + && let Some(bundle) = cursor_stack.get_data_component_mut::() + && let Some(extracted) = bundle.try_extract() { + slot.set_stack(extracted); + intercepted = true; + } + + if intercepted { + if cursor_stack.item_count == 0 { + *cursor_stack = ItemStack::EMPTY.clone(); + } + slot.mark_dirty(); + return; + } + } + + let equipment_slot = cursor_stack + .get_data_component::() + .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); + + if self.handle_slot_click( + player, + click_type.clone(), + slot.clone(), + slot_stack.clone(), + cursor_stack.clone(), + ) { + return; + } + + if slot_stack.is_empty() { + if !cursor_stack.is_empty() { + if equipment_slot.slot_type() == EquipmentType::HumanoidArmor + && (5..9).contains(&slot_index) + { + player.enqueue_equipment_change(equipment_slot, &cursor_stack); + } + + let transfer_count = if click_type == MouseClick::Left { + cursor_stack.item_count + } else { + 1 + }; + *cursor_stack = + slot.insert_stack_count(cursor_stack.clone(), transfer_count); + } + } else if slot.can_take_items(player) { + if cursor_stack.is_empty() { + let take_count = if click_type == MouseClick::Left { + slot_stack.item_count + } else { + slot_stack.item_count.div_ceil(2) + }; + let taken = slot.try_take_stack_range(take_count, u8::MAX, player); + if let Some(taken) = taken { + // Reverse order of operations, shouldn't affect anything + *cursor_stack = taken.clone(); + slot.on_take_item(player, &taken); + + if (5..9).contains(&slot_index) { + let equipment_slot = cursor_stack + .get_data_component::() + .map_or(&EquipmentSlot::MAIN_HAND, |equippable| { + equippable.slot + }); + player.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY); + } + } + } else if slot.can_insert(&cursor_stack) { + if equipment_slot.slot_type() == EquipmentType::HumanoidArmor + && (5..9).contains(&slot_index) + { + player.enqueue_equipment_change(equipment_slot, &cursor_stack); + } + + if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) { + let insert_count = if click_type == MouseClick::Left { + cursor_stack.item_count + } else { + 1 + }; + *cursor_stack = + slot.insert_stack_count(cursor_stack.clone(), insert_count); + } else if cursor_stack.item_count + <= slot.get_max_item_count_for_stack(&cursor_stack) + { + let old_cursor_stack = cursor_stack.clone(); + *cursor_stack = slot_stack; + slot.set_stack(old_cursor_stack); + } + } else if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) + { + let taken = slot.try_take_stack_range( + slot_stack.item_count, + cursor_stack + .get_max_stack_size() + .saturating_sub(cursor_stack.item_count), + player, + ); + + if let Some(taken) = taken { + cursor_stack.increment(taken.item_count); + slot.on_take_item(player, &taken); + } + } + } + + slot.mark_dirty(); + } + } else if action_type == SlotActionType::Swap && (0..9).contains(&button) || button == 40 { + if slot_index < 0 { + return; + } + let mut button_stack = player.get_inventory().get_stack(button as usize); + let source_slot = self.get_behaviour().slots[slot_index as usize].clone(); + let source_stack = source_slot.get_cloned_stack(); + + if !button_stack.is_empty() || !source_stack.is_empty() { + if button_stack.is_empty() { + if source_slot.can_take_items(player) { + player + .get_inventory() + .set_stack(button as usize, source_stack.clone()); + source_slot.set_stack(ItemStack::EMPTY.clone()); + source_slot.on_take_item(player, &source_stack); + } + } else if source_stack.is_empty() && source_slot.can_insert(&button_stack) { + let max_count = source_slot.get_max_item_count_for_stack(&button_stack); + if button_stack.item_count > max_count { + // button_stack might need to be a ref instead of a clone + source_slot.set_stack(button_stack.split(max_count)); + } else { + player + .get_inventory() + .set_stack(button as usize, ItemStack::EMPTY.clone()); + source_slot.set_stack(button_stack); + } + } + } + } } } pub trait ScreenHandlerListener: Send + Sync { - fn on_slot_update<'a>( - &'a self, - _screen_handler: &'a ScreenHandlerBehaviour, + fn on_slot_update( + &self, + _screen_handler: &ScreenHandlerBehaviour, _slot: u8, _stack: ItemStack, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) + ) { } - fn on_property_update<'a>( - &'a self, - _screen_handler: &'a ScreenHandlerBehaviour, + fn on_property_update( + &self, + _screen_handler: &ScreenHandlerBehaviour, _property: u8, _value: i32, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) + ) { } } -pub type BoxFuture<'a, T> = Pin + Send + 'a>>; - pub type SharedScreenHandler = Arc>; pub trait ScreenHandlerFactory: Send + Sync { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option>; + player_inventory: &Arc, + player: &dyn InventoryPlayer, + ) -> Option; fn get_display_name(&self) -> TextComponent; } diff --git a/crates/pumpkin-inventory/src/slot.rs b/crates/pumpkin-inventory/src/slot.rs index 54998d892..e13556b6a 100644 --- a/crates/pumpkin-inventory/src/slot.rs +++ b/crates/pumpkin-inventory/src/slot.rs @@ -18,12 +18,9 @@ //! - Marking the slot as changed (dirty) //! - Callbacks for slot interaction events -use std::{ - pin::Pin, - sync::{ - Arc, - atomic::{AtomicU8, Ordering}, - }, +use std::sync::{ + Arc, + atomic::{AtomicU8, Ordering}, }; use crate::screen_handler::InventoryPlayer; @@ -33,9 +30,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_world::inventory::Inventory; -/// Type alias for async slot operations. -pub type BoxFuture<'a, T> = Pin + Send + 'a>>; - /// A slot in an inventory. /// /// The slot trait defines how individual inventory positions behave. @@ -62,140 +56,98 @@ pub trait Slot: Send + Sync { /// any locks to avoid deadlocks. /// /// Also see: [`ScreenHandler::quick_move`](crate::screen_handler::ScreenHandler::quick_move) - fn on_quick_move_crafted( - &self, - _stack: ItemStack, - _stack_prev: ItemStack, - ) -> BoxFuture<'_, ()> { - Box::pin(async {}) // Default implementation - } + fn on_quick_move_crafted(&self, _stack: ItemStack, _stack_prev: ItemStack) {} /// Callback for when an item is taken from this slot. /// /// Also see: [`safe_take`] - fn on_take_item<'a>( - &'a self, - _player: &'a dyn InventoryPlayer, - _stack: &'a ItemStack, - ) -> BoxFuture<'a, ()> { - // Default implementation logic: - Box::pin(async move { - self.mark_dirty().await; - }) + fn on_take_item(&self, _player: &dyn InventoryPlayer, _stack: &ItemStack) { + self.mark_dirty(); } /// Plugin callback for slot clicks. /// /// Called when a player clicks on this slot. Can be used by /// plugins to intercept or modify click behavior. - fn on_click(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, ()> { - Box::pin(async {}) // Default implementation - } + fn on_click(&self, _player: &dyn InventoryPlayer) {} /// Checks if the given stack can be inserted into this slot. - fn can_insert<'a>(&'a self, _stack: &'a ItemStack) -> BoxFuture<'a, bool> { - // Default implementation logic: - Box::pin(async move { true }) + fn can_insert(&self, _stack: &ItemStack) -> bool { + true } /// Gets the stack in this slot. - fn get_stack(&self) -> BoxFuture<'_, ItemStack> { - // Default implementation logic: - Box::pin(async move { self.get_inventory().get_stack(self.get_index()).await }) + fn get_stack(&self) -> ItemStack { + self.get_inventory().get_stack(self.get_index()) } /// Gets a copy of the stack in this slot. - fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> { - // Default implementation logic: - Box::pin(async move { self.get_stack().await }) + fn get_cloned_stack(&self) -> ItemStack { + self.get_stack() } /// Checks if this slot has a non-empty stack. - fn has_stack(&self) -> BoxFuture<'_, bool> { - // Default implementation logic: - Box::pin(async move { !self.get_stack().await.is_empty() }) + fn has_stack(&self) -> bool { + !self.get_stack().is_empty() } /// Sets the stack in this slot. /// /// # Note /// Make sure to drop any locks to the slot stack before calling this. - fn set_stack(&self, stack: ItemStack) -> BoxFuture<'_, ()> { - // Default implementation logic: - Box::pin(async move { - self.set_stack_no_callbacks(stack).await; - }) + fn set_stack(&self, stack: ItemStack) { + self.set_stack_no_callbacks(stack); } /// Sets the stack with previous stack reference. /// /// Some slots (like armor) need to know the previous stack for callbacks. - fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> { - // Default implementation logic: - Box::pin(async move { - self.set_stack_no_callbacks(stack).await; - }) + fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) { + self.set_stack_no_callbacks(stack); } /// Sets the stack without calling callbacks. - fn set_stack_no_callbacks(&self, stack: ItemStack) -> BoxFuture<'_, ()> { - // Default implementation logic: - Box::pin(async move { - let inv = self.get_inventory(); - inv.set_stack(self.get_index(), stack).await; - self.mark_dirty().await; - }) + fn set_stack_no_callbacks(&self, stack: ItemStack) { + let inv = self.get_inventory(); + inv.set_stack(self.get_index(), stack); + self.mark_dirty(); } /// Marks this slot as changed. /// /// Must be implemented by concrete types. - fn mark_dirty(&self) -> BoxFuture<'_, ()>; + fn mark_dirty(&self); /// Gets the maximum item count for this slot. - fn get_max_item_count(&self) -> BoxFuture<'_, u8> { - // Default implementation logic: - Box::pin(async move { self.get_inventory().get_max_count_per_stack() }) + fn get_max_item_count(&self) -> u8 { + self.get_inventory().get_max_count_per_stack() } /// Gets the maximum item count for the given stack in this slot. - fn get_max_item_count_for_stack<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, u8> { - // Default implementation logic: - Box::pin(async move { - self.get_max_item_count() - .await - .min(stack.get_max_stack_size()) - }) + fn get_max_item_count_for_stack(&self, stack: &ItemStack) -> u8 { + self.get_max_item_count().min(stack.get_max_stack_size()) } /// Removes a specific amount of items from this slot. /// /// Mojang name: `remove` - fn take_stack(&self, amount: u8) -> BoxFuture<'_, ItemStack> { - // Default implementation logic: - Box::pin(async move { - let inv = self.get_inventory(); - inv.remove_stack_specific(self.get_index(), amount).await - }) + fn take_stack(&self, amount: u8) -> ItemStack { + let inv = self.get_inventory(); + inv.remove_stack_specific(self.get_index(), amount) } /// Checks if the player can take items from this slot. /// /// Mojang name: `mayPickup` - fn can_take_items(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, bool> { - // Default implementation logic: - Box::pin(async move { true }) + fn can_take_items(&self, _player: &dyn InventoryPlayer) -> bool { + true } /// Checks if this slot can be modified by the player. /// /// Mojang name: `allowModification` - fn allow_modification<'a>(&'a self, player: &'a dyn InventoryPlayer) -> BoxFuture<'a, bool> { - // Default implementation logic: - Box::pin(async move { - self.can_insert(&self.get_cloned_stack().await).await - && self.can_take_items(player).await - }) + fn allow_modification(&self, player: &dyn InventoryPlayer) -> bool { + self.can_insert(&self.get_cloned_stack()) && self.can_take_items(player) } /// Tries to take a stack in the given range. @@ -204,37 +156,31 @@ pub trait Slot: Send + Sync { /// For result slots, cannot take partial stacks. /// /// Mojang name: `tryRemove` - fn try_take_stack_range<'a>( - &'a self, + fn try_take_stack_range( + &self, min: u8, max: u8, - player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - // Default implementation logic: - Box::pin(async move { - if !self.can_take_items(player).await { - return None; - } - if !self.allow_modification(player).await - && self.get_cloned_stack().await.item_count > max - { - // If the slot is not allowed to be modified, we cannot take a partial stack from it. - return None; - } - let min = min.min(max); - let stack = self.take_stack(min).await; + player: &dyn InventoryPlayer, + ) -> Option { + if !self.can_take_items(player) { + return None; + } + if !self.allow_modification(player) && self.get_cloned_stack().item_count > max { + // If the slot is not allowed to be modified, we cannot take a partial stack from it. + return None; + } + let min = min.min(max); + let stack = self.take_stack(min); - if stack.is_empty() { - None - } else { - if self.get_cloned_stack().await.is_empty() { - self.set_stack_prev(ItemStack::EMPTY.clone(), stack.clone()) - .await; - } - - Some(stack) + if stack.is_empty() { + None + } else { + if self.get_cloned_stack().is_empty() { + self.set_stack_prev(ItemStack::EMPTY.clone(), stack.clone()); } - }) + + Some(stack) + } } /// Safely tries to take a stack of items from the slot. @@ -242,62 +188,49 @@ pub trait Slot: Send + Sync { /// Returns an empty stack if can't take. Triggers callbacks. /// /// Mojang name: `safeTake` - fn safe_take<'a>( - &'a self, - min: u8, - max: u8, - player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, ItemStack> { - Box::pin(async move { - let stack = self.try_take_stack_range(min, max, player).await; + fn safe_take(&self, min: u8, max: u8, player: &dyn InventoryPlayer) -> ItemStack { + let stack = self.try_take_stack_range(min, max, player); - if let Some(stack) = &stack { - self.on_take_item(player, stack).await; - } + if let Some(stack) = &stack { + self.on_take_item(player, stack); + } - stack.unwrap_or_else(|| ItemStack::EMPTY.clone()) - }) + stack.unwrap_or_else(|| ItemStack::EMPTY.clone()) } /// Inserts a stack into this slot. /// /// Returns any leftover items that couldn't fit. - fn insert_stack(&self, stack: ItemStack) -> BoxFuture<'_, ItemStack> { - // Default implementation logic: - Box::pin(async move { - let stack_item_count = stack.item_count; - self.insert_stack_count(stack, stack_item_count).await - }) + fn insert_stack(&self, stack: ItemStack) -> ItemStack { + let stack_item_count = stack.item_count; + self.insert_stack_count(stack, stack_item_count) } /// Inserts a specific count from a stack. /// /// Returns any leftover items. - fn insert_stack_count(&self, mut stack: ItemStack, count: u8) -> BoxFuture<'_, ItemStack> { - // Default implementation logic: - Box::pin(async move { - if !stack.is_empty() && self.can_insert(&stack).await { - let mut stack_self = self.get_stack().await; - let min_count = count - .min(stack.item_count) - .min(self.get_max_item_count_for_stack(&stack).await - stack_self.item_count); + fn insert_stack_count(&self, mut stack: ItemStack, count: u8) -> ItemStack { + if !stack.is_empty() && self.can_insert(&stack) { + let mut stack_self = self.get_stack(); + let min_count = count + .min(stack.item_count) + .min(self.get_max_item_count_for_stack(&stack) - stack_self.item_count); - if min_count != 0 { - if stack_self.is_empty() { - self.set_stack(stack.split(min_count)).await; - } else if stack.are_items_and_components_equal(&stack_self) { - stack.decrement(min_count); - stack_self.increment(min_count); - self.set_stack(stack_self).await; - } + if min_count != 0 { + if stack_self.is_empty() { + self.set_stack(stack.split(min_count)); + } else if stack.are_items_and_components_equal(&stack_self) { + stack.decrement(min_count); + stack_self.increment(min_count); + self.set_stack(stack_self); } } - if stack.is_empty() { - ItemStack::EMPTY.clone() - } else { - stack - } - }) + } + if stack.is_empty() { + ItemStack::EMPTY.clone() + } else { + stack + } } } @@ -342,10 +275,8 @@ impl Slot for NormalSlot { self.id.store(id as u8, Ordering::Relaxed); } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } } @@ -399,43 +330,33 @@ impl Slot for ArmorSlot { } /// Restricts inserts to appropriate armor types. - fn can_insert<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, bool> { - Box::pin(async move { - match self.equipment_slot { - EquipmentSlot::Head(_) => { - stack.is_helmet() || stack.is_skull() || stack.item == &Item::CARVED_PUMPKIN - } - EquipmentSlot::Chest(_) => stack.is_chestplate() || stack.item == &Item::ELYTRA, - EquipmentSlot::Legs(_) => stack.is_leggings(), - EquipmentSlot::Feet(_) => stack.is_boots(), - _ => true, + fn can_insert(&self, stack: &ItemStack) -> bool { + match self.equipment_slot { + EquipmentSlot::Head(_) => { + stack.is_helmet() || stack.is_skull() || stack.item == &Item::CARVED_PUMPKIN } - }) + EquipmentSlot::Chest(_) => stack.is_chestplate() || stack.item == &Item::ELYTRA, + EquipmentSlot::Legs(_) => stack.is_leggings(), + EquipmentSlot::Feet(_) => stack.is_boots(), + _ => true, + } } - fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> { - Box::pin(async move { - //TODO: this.entity.onEquipStack(this.equipmentSlot, previousStack, stack); - self.set_stack_no_callbacks(stack).await; - }) + fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) { + self.set_stack_no_callbacks(stack); } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } /// Armor slots can only hold one item. - fn get_max_item_count(&self) -> BoxFuture<'_, u8> { - Box::pin(async move { 1 }) + fn get_max_item_count(&self) -> u8 { + 1 } /// TODO: Check for curse of binding enchantment. - fn can_take_items(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, bool> { - Box::pin(async move { - // TODO: Check enchantments - true - }) + fn can_take_items(&self, _player: &dyn InventoryPlayer) -> bool { + true } } diff --git a/crates/pumpkin-inventory/src/smithing_table_screen_handler.rs b/crates/pumpkin-inventory/src/smithing_table_screen_handler.rs index f83c55f90..dce7724c2 100644 --- a/crates/pumpkin-inventory/src/smithing_table_screen_handler.rs +++ b/crates/pumpkin-inventory/src/smithing_table_screen_handler.rs @@ -2,9 +2,7 @@ use std::any::Any; use std::sync::Arc; use crate::player::player_inventory::PlayerInventory; -use crate::screen_handler::{ - InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; +use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use crate::slot::NormalSlot; use pumpkin_data::item_stack::ItemStack; @@ -66,48 +64,39 @@ impl ScreenHandler for SmithingTableScreenHandler { self } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ScreenHandlerFuture<'a, ItemStack> { - Box::pin(async move { - let mut stack = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); - if let Some(slot) = slot { - let mut slot_stack = slot.get_cloned_stack().await; - if !slot_stack.is_empty() { - stack = slot_stack.clone(); - if slot_index < 4 { - if !self.insert_item(&mut slot_stack, 4, 40, true).await { - return ItemStack::EMPTY.clone(); - } - } else if !self.insert_item(&mut slot_stack, 0, 3, false).await { + if let Some(slot) = slot { + let mut slot_stack = slot.get_cloned_stack(); + if !slot_stack.is_empty() { + stack = slot_stack.clone(); + if slot_index < 4 { + if !self.insert_item(&mut slot_stack, 4, 40, true) { return ItemStack::EMPTY.clone(); } + } else if !self.insert_item(&mut slot_stack, 0, 3, false) { + return ItemStack::EMPTY.clone(); + } - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; - } + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); } } - stack - }) + } + stack } } diff --git a/crates/pumpkin-inventory/src/stonecutter_screen_handler.rs b/crates/pumpkin-inventory/src/stonecutter_screen_handler.rs index 7f9c9e6e8..b33cd65a3 100644 --- a/crates/pumpkin-inventory/src/stonecutter_screen_handler.rs +++ b/crates/pumpkin-inventory/src/stonecutter_screen_handler.rs @@ -3,10 +3,8 @@ use std::sync::Arc; use std::sync::atomic::{AtomicU8, Ordering}; use crate::player::player_inventory::PlayerInventory; -use crate::screen_handler::{ - InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; -use crate::slot::{BoxFuture, NormalSlot, Slot}; +use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; +use crate::slot::{NormalSlot, Slot}; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; @@ -54,13 +52,11 @@ impl StonecutterScreenHandler { handler } - async fn update_output(&self) { - let input_lock = self.input_inventory.get_stack(0).await; + fn update_output(&self) { + let input_lock = self.input_inventory.get_stack(0); if input_lock.is_empty() { - self.output_inventory - .set_stack(0, ItemStack::EMPTY.clone()) - .await; + self.output_inventory.set_stack(0, ItemStack::EMPTY.clone()); self.selected_recipe.store(u8::MAX, Ordering::Relaxed); return; } @@ -72,11 +68,9 @@ impl StonecutterScreenHandler { let recipe = available_recipes[recipe_index as usize]; let item = Item::from_registry_key(recipe.result.id).unwrap_or(&Item::AIR); let result = ItemStack::new(recipe.result.count, item); - self.output_inventory.set_stack(0, result).await; + self.output_inventory.set_stack(0, result); } else { - self.output_inventory - .set_stack(0, ItemStack::EMPTY.clone()) - .await; + self.output_inventory.set_stack(0, ItemStack::EMPTY.clone()); } } @@ -106,59 +100,49 @@ impl ScreenHandler for StonecutterScreenHandler { self } - fn on_slot_click<'a>( - &'a mut self, + fn on_slot_click( + &mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &'a dyn InventoryPlayer, - ) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; - if slot_index == 0 { - self.update_output().await; - } - }) + player: &dyn InventoryPlayer, + ) { + self.internal_on_slot_click(slot_index, button, action_type, player); + if slot_index == 0 { + self.update_output(); + } } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ScreenHandlerFuture<'a, ItemStack> { - Box::pin(async move { - let mut stack = ItemStack::EMPTY.clone(); - let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); + fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { + let mut stack = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots.get(slot_index as usize).cloned(); - if let Some(slot) = slot { - let mut slot_stack = slot.get_cloned_stack().await; - if !slot_stack.is_empty() { - stack = slot_stack.clone(); - if slot_index < 2 { - // From Stonecutter to Player - if !self.insert_item(&mut slot_stack, 2, 38, true).await { - return ItemStack::EMPTY.clone(); - } - slot.on_quick_move_crafted(slot_stack.clone(), stack.clone()) - .await; - } else { - // From Player to Stonecutter - // Try input slot (0) - if !self.insert_item(&mut slot_stack, 0, 1, false).await { - return ItemStack::EMPTY.clone(); - } + if let Some(slot) = slot { + let mut slot_stack = slot.get_cloned_stack(); + if !slot_stack.is_empty() { + stack = slot_stack.clone(); + if slot_index < 2 { + // From Stonecutter to Player + if !self.insert_item(&mut slot_stack, 2, 38, true) { + return ItemStack::EMPTY.clone(); } - - if slot_stack.is_empty() { - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - slot.set_stack(slot_stack).await; + slot.on_quick_move_crafted(slot_stack.clone(), stack.clone()); + } else { + // From Player to Stonecutter + // Try input slot (0) + if !self.insert_item(&mut slot_stack, 0, 1, false) { + return ItemStack::EMPTY.clone(); } } + + if slot_stack.is_empty() { + slot.set_stack(ItemStack::EMPTY.clone()); + } else { + slot.set_stack(slot_stack); + } } - stack - }) + } + stack } } @@ -197,55 +181,41 @@ impl Slot for StonecutterOutputSlot { self.id.store(id as u8, Ordering::Relaxed); } - fn on_take_item<'a>( - &'a self, - player: &'a dyn InventoryPlayer, - stack: &'a ItemStack, - ) -> BoxFuture<'a, ()> { - Box::pin(async move { - player - .increment_stat( - StatisticCategory::Crafted, - stack.item.id as i32, - stack.item_count as i32, - ) - .await; - self.input_inventory.remove_stack_specific(0, 1).await; - self.mark_dirty().await; - }) + fn on_take_item(&self, player: &dyn InventoryPlayer, stack: &ItemStack) { + player.increment_stat( + StatisticCategory::Crafted, + stack.item.id as i32, + stack.item_count as i32, + ); + self.input_inventory.remove_stack_specific(0, 1); + self.mark_dirty(); } - fn can_insert(&self, _stack: &ItemStack) -> BoxFuture<'_, bool> { - Box::pin(async move { false }) + fn can_insert(&self, _stack: &ItemStack) -> bool { + false } - fn get_stack(&self) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { self.inventory.get_stack(self.index).await }) + fn get_stack(&self) -> ItemStack { + self.inventory.get_stack(self.index) } - fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> { - Box::pin(async move { self.inventory.get_stack(self.index).await }) + fn get_cloned_stack(&self) -> ItemStack { + self.inventory.get_stack(self.index) } - fn has_stack(&self) -> BoxFuture<'_, bool> { - Box::pin(async move { !self.inventory.get_stack(self.index).await.is_empty() }) + fn has_stack(&self) -> bool { + !self.inventory.get_stack(self.index).is_empty() } - fn set_stack(&self, stack: ItemStack) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.set_stack(self.index, stack).await; - }) + fn set_stack(&self, stack: ItemStack) { + self.inventory.set_stack(self.index, stack); } - fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> { - Box::pin(async move { - // Do nothing - }) + fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) { + // Do nothing } - fn mark_dirty(&self) -> BoxFuture<'_, ()> { - Box::pin(async move { - self.inventory.mark_dirty(); - }) + fn mark_dirty(&self) { + self.inventory.mark_dirty(); } } diff --git a/crates/pumpkin-inventory/src/sync_handler.rs b/crates/pumpkin-inventory/src/sync_handler.rs index 57596adb1..0a33c80cf 100644 --- a/crates/pumpkin-inventory/src/sync_handler.rs +++ b/crates/pumpkin-inventory/src/sync_handler.rs @@ -18,7 +18,7 @@ //! and server stay in sync. If the client detects a desync, it can request a full //! resynchronization. -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use pumpkin_data::item_stack::ItemStack; use pumpkin_protocol::{ @@ -30,7 +30,6 @@ use pumpkin_protocol::{ CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, }, }; -use tokio::sync::Mutex; use crate::screen_handler::{InventoryPlayer, ScreenHandlerBehaviour}; @@ -67,8 +66,11 @@ impl SyncHandler { /// Stores the player to synchronize with. /// /// Must be called before any sync operations. - pub async fn store_player(&self, player: Arc) { - self.player.lock().await.replace(player); + pub fn store_player(&self, player: Arc) { + self.player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .replace(player); } /// Sends a full container content update. @@ -82,39 +84,40 @@ impl SyncHandler { /// - `cursor_stack` - The item held by the cursor /// - `properties` - Container property values /// - `next_revision` - The new revision number - pub async fn update_state( + pub fn update_state( &self, screen_handler: &ScreenHandlerBehaviour, stacks: &[ItemStack], cursor_stack: &ItemStack, - properties: Vec, + properties: &[i32], next_revision: u32, ) { - if let Some(player) = self.player.lock().await.as_ref() { - player - .enqueue_inventory_packet( - &CSetContainerContent::new( - VarInt(screen_handler.sync_id.into()), - VarInt(next_revision as i32), - stacks - .iter() - .map(|stack| ItemStackSerializer::from(stack.clone())) - .collect::>() - .as_slice(), - &ItemStackSerializer::from(cursor_stack.clone()), - ), - screen_handler.window_type, - ) - .await; + if let Some(player) = self + .player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + player.enqueue_inventory_packet( + &CSetContainerContent::new( + VarInt(screen_handler.sync_id.into()), + VarInt(next_revision as i32), + stacks + .iter() + .map(|stack| ItemStackSerializer::from(stack.clone())) + .collect::>() + .as_slice(), + &ItemStackSerializer::from(cursor_stack.clone()), + ), + screen_handler.window_type, + ); for (i, property) in properties.iter().enumerate() { - player - .enqueue_property_packet(&CSetContainerProperty::new( - VarInt(screen_handler.sync_id.into()), - i as i16, - *property as i16, - )) - .await; + player.enqueue_property_packet(&CSetContainerProperty::new( + VarInt(screen_handler.sync_id.into()), + i as i16, + *property as i16, + )); } } } @@ -128,26 +131,29 @@ impl SyncHandler { /// - `slot` - The slot index that changed /// - `stack` - The new stack in that slot /// - `next_revision` - The new revision number - pub async fn update_slot( + pub fn update_slot( &self, screen_handler: &ScreenHandlerBehaviour, slot: usize, stack: &ItemStack, next_revision: u32, ) { - if let Some(player) = self.player.lock().await.as_ref() { - player - .enqueue_slot_packet( - &CSetContainerSlot::new( - screen_handler.sync_id as i8, - next_revision as i32, - slot as i16, - &ItemStackSerializer::from(stack.clone()), - ), - screen_handler.window_type, - screen_handler.slots.len(), - ) - .await; + if let Some(player) = self + .player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + player.enqueue_slot_packet( + &CSetContainerSlot::new( + screen_handler.sync_id as i8, + next_revision as i32, + slot as i16, + &ItemStackSerializer::from(stack.clone()), + ), + screen_handler.window_type, + screen_handler.slots.len(), + ); } } @@ -158,17 +164,16 @@ impl SyncHandler { /// # Arguments /// - `screen_handler` - The screen handler /// - `stack` - The new cursor item - pub async fn update_cursor_stack( - &self, - _screen_handler: &ScreenHandlerBehaviour, - stack: &ItemStack, - ) { - if let Some(player) = self.player.lock().await.as_ref() { - player - .enqueue_cursor_packet(&CSetCursorItem::new(&ItemStackSerializer::from( - stack.clone(), - ))) - .await; + pub fn update_cursor_stack(&self, _screen_handler: &ScreenHandlerBehaviour, stack: &ItemStack) { + if let Some(player) = self + .player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + player.enqueue_cursor_packet(&CSetCursorItem::new(&ItemStackSerializer::from( + stack.clone(), + ))); } } @@ -180,20 +185,23 @@ impl SyncHandler { /// - `screen_handler` - The screen handler /// - `property` - The property index /// - `value` - The new property value - pub async fn update_property( + pub fn update_property( &self, screen_handler: &ScreenHandlerBehaviour, property: i32, value: i32, ) { - if let Some(player) = self.player.lock().await.as_ref() { - player - .enqueue_property_packet(&CSetContainerProperty::new( - VarInt(screen_handler.sync_id.into()), - property as i16, - value as i16, - )) - .await; + if let Some(player) = self + .player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .as_ref() + { + player.enqueue_property_packet(&CSetContainerProperty::new( + VarInt(screen_handler.sync_id.into()), + property as i16, + value as i16, + )); } } } diff --git a/crates/pumpkin-world/src/inventory/inventory.rs b/crates/pumpkin-world/src/inventory/inventory.rs index 25ffb102d..a107cb4c3 100644 --- a/crates/pumpkin-world/src/inventory/inventory.rs +++ b/crates/pumpkin-world/src/inventory/inventory.rs @@ -2,98 +2,73 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use std::any::Any; -use std::pin::Pin; use std::{ hash::{Hash, Hasher}, sync::Arc, }; -pub type InventoryFuture<'a, T> = Pin + Send + 'a>>; - pub trait Inventory: Send + Sync + Clearable { fn size(&self) -> usize; - fn is_empty(&self) -> InventoryFuture<'_, bool>; + fn is_empty(&self) -> bool; - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack>; + fn get_stack(&self, slot: usize) -> ItemStack; - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack>; + fn remove_stack(&self, slot: usize) -> ItemStack; - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack>; + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack; - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()>; + fn set_stack(&self, slot: usize, stack: ItemStack); - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async {}) - } - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async {}) - } + fn on_open(&self) {} + fn on_close(&self) {} - fn count<'a>(&'a self, item: &'a Item) -> InventoryFuture<'a, u8> { - Box::pin(async move { - let mut count = 0; + fn count(&self, item: &Item) -> u8 { + let mut count = 0; - for i in 0..self.size() { - let stack = self.get_stack(i).await; - if stack.get_item().id == item.id { - count += stack.item_count; - } + for i in 0..self.size() { + let stack = self.get_stack(i); + if stack.get_item().id == item.id { + count += stack.item_count; } + } - count - }) + count } - fn contains_any_predicate<'a>( - &'a self, - predicate: &'a (dyn Fn(&ItemStack) -> bool + Sync), - ) -> InventoryFuture<'a, bool> { - Box::pin(async move { - for i in 0..self.size() { - let stack = self.get_stack(i).await; - if predicate(&stack) { - return true; - } + fn contains_any_predicate(&self, predicate: &(dyn Fn(&ItemStack) -> bool + Sync)) -> bool { + for i in 0..self.size() { + let stack = self.get_stack(i); + if predicate(&stack) { + return true; } + } - false - }) + false } - fn contains_any<'a>(&'a self, items: &'a [Item]) -> InventoryFuture<'a, bool> { - Box::pin(async move { - self.contains_any_predicate(&|stack| { - !stack.is_empty() && items.contains(stack.get_item()) - }) - .await - }) + fn contains_any(&self, items: &[Item]) -> bool { + self.contains_any_predicate(&|stack| !stack.is_empty() && items.contains(stack.get_item())) } - fn write_inventory_nbt<'a>( - &'a self, - nbt: &'a mut NbtCompound, - include_empty: bool, - ) -> InventoryFuture<'a, ()> { - Box::pin(async move { - let mut slots = Vec::new(); - let size = self.size(); + fn write_inventory_nbt(&self, nbt: &mut NbtCompound, include_empty: bool) { + let mut slots = Vec::new(); + let size = self.size(); - for i in 0..size { - let stack = self.get_stack(i).await; + for i in 0..size { + let stack = self.get_stack(i); - 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); - slots.push(NbtTag::Compound(item_compound)); - } + 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); + slots.push(NbtTag::Compound(item_compound)); } + } - if include_empty || !slots.is_empty() { - nbt.put("Items", NbtTag::List(slots)); - } - }) + if include_empty || !slots.is_empty() { + nbt.put("Items", NbtTag::List(slots)); + } } fn get_max_count_per_stack(&self) -> u8 { @@ -123,7 +98,7 @@ pub trait Inventory: Send + Sync + Clearable { } pub trait Clearable { - fn clear(&self) -> Pin + Send + '_>>; + fn clear(&self); } pub fn sync_read_items_from_nbt(nbt: &NbtCompound, stacks: &mut [ItemStack]) { diff --git a/crates/pumpkin-world/src/inventory/simple_inventory.rs b/crates/pumpkin-world/src/inventory/simple_inventory.rs index 6e9ba539f..bc519aa63 100644 --- a/crates/pumpkin-world/src/inventory/simple_inventory.rs +++ b/crates/pumpkin-world/src/inventory/simple_inventory.rs @@ -1,7 +1,7 @@ -use crate::inventory::{Clearable, Inventory, InventoryFuture}; +use crate::inventory::{Clearable, Inventory}; use pumpkin_data::item_stack::ItemStack; use std::any::Any; -use tokio::sync::RwLock; +use std::sync::RwLock; pub struct SimpleInventory { pub stacks: RwLock>, @@ -19,11 +19,12 @@ impl SimpleInventory { } impl Clearable for SimpleInventory { - fn clear(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut stacks = self.stacks.write().await; - stacks.fill_with(|| ItemStack::EMPTY.clone()); - }) + fn clear(&self) { + let mut stacks = self + .stacks + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stacks.fill_with(|| ItemStack::EMPTY.clone()); } } @@ -32,52 +33,57 @@ impl Inventory for SimpleInventory { self.size } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let stacks = self.stacks.read().await; - stacks.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + let stacks = self + .stacks + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stacks.iter().all(ItemStack::is_empty) } - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let stacks = self.stacks.read().await; - stacks - .get(slot) - .cloned() - .unwrap_or_else(|| ItemStack::EMPTY.clone()) - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let stacks = self + .stacks + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + stacks + .get(slot) + .cloned() + .unwrap_or_else(|| ItemStack::EMPTY.clone()) } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut stacks = self.stacks.write().await; - if slot < stacks.len() { - std::mem::replace(&mut stacks[slot], ItemStack::EMPTY.clone()) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut stacks = self + .stacks + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < stacks.len() { + std::mem::replace(&mut stacks[slot], ItemStack::EMPTY.clone()) + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut stacks = self.stacks.write().await; - if slot < stacks.len() && !stacks[slot].is_empty() && amount > 0 { - stacks[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut stacks = self + .stacks + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < stacks.len() && !stacks[slot].is_empty() && amount > 0 { + stacks[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut stacks = self.stacks.write().await; - if slot < stacks.len() { - stacks[slot] = stack; - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut stacks = self + .stacks + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < stacks.len() { + stacks[slot] = stack; + } } fn as_any(&self) -> &dyn Any { diff --git a/crates/pumpkin/src/block/blocks/anvil.rs b/crates/pumpkin/src/block/blocks/anvil.rs index c75df379b..c2a3faf4f 100644 --- a/crates/pumpkin/src/block/blocks/anvil.rs +++ b/crates/pumpkin/src/block/blocks/anvil.rs @@ -11,13 +11,13 @@ use pumpkin_data::translation; use pumpkin_inventory::anvil::AnvilScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::SimpleInventory; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; #[pumpkin_block_from_tag("minecraft:anvil")] pub struct AnvilBlock; @@ -29,13 +29,8 @@ impl BlockBehaviour for AnvilBlock { pumpkin_data::statistic::CustomStatistic::InteractWithAnvil as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&AnvilScreenFactory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&AnvilScreenFactory, Some(*args.position)); BlockActionResult::Success } @@ -73,19 +68,17 @@ impl BlockBehaviour for AnvilBlock { struct AnvilScreenFactory; impl ScreenHandlerFactory for AnvilScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let inventory = Arc::new(SimpleInventory::new(3)); - let handler = AnvilScreenHandler::new(sync_id, player_inventory, inventory); - let concrete_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let inventory = Arc::new(SimpleInventory::new(3)); + let handler = AnvilScreenHandler::new(sync_id, player_inventory, inventory); + let concrete_arc = Arc::new(Mutex::new(handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/barrel.rs b/crates/pumpkin/src/block/blocks/barrel.rs index c28a9adb7..64d1bae31 100644 --- a/crates/pumpkin/src/block/blocks/barrel.rs +++ b/crates/pumpkin/src/block/blocks/barrel.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{ @@ -14,28 +15,25 @@ use pumpkin_data::translation; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; struct BarrelScreenFactory(Arc); impl ScreenHandlerFactory for BarrelScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await; - let concrete_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()); + let concrete_arc = Arc::new(Mutex::new(handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -65,13 +63,8 @@ impl BlockBehaviour for BarrelBlock { pumpkin_data::statistic::CustomStatistic::OpenBarrel as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&BarrelScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&BarrelScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success diff --git a/crates/pumpkin/src/block/blocks/beacon.rs b/crates/pumpkin/src/block/blocks/beacon.rs index 9975d431b..97e80c786 100644 --- a/crates/pumpkin/src/block/blocks/beacon.rs +++ b/crates/pumpkin/src/block/blocks/beacon.rs @@ -1,11 +1,12 @@ use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; use crate::block::entities::BlockEntity; use pumpkin_data::translation; +use pumpkin_inventory::beacon_screen_handler::create_beacon_handler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; @@ -18,22 +19,16 @@ use crate::block::{BlockBehaviour, NormalUseArgs}; struct BeaconScreenFactory(Arc); impl ScreenHandlerFactory for BeaconScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - // Assumes create_beacon_handler exists in your generic_container_screen_handler equivalent - use pumpkin_inventory::beacon_screen_handler::create_beacon_handler; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = create_beacon_handler(sync_id, player_inventory, self.0.clone()); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - let concrete_handler = - create_beacon_handler(sync_id, player_inventory, self.0.clone()).await; - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -63,13 +58,8 @@ impl BlockBehaviour for BeaconBlock { ); // Open the screen using the factory - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&BeaconScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&BeaconScreenFactory(inventory), Some(*args.position)); BlockActionResult::Success } diff --git a/crates/pumpkin/src/block/blocks/blast_furnace.rs b/crates/pumpkin/src/block/blocks/blast_furnace.rs index 2f5a54e5e..437c0e710 100644 --- a/crates/pumpkin/src/block/blocks/blast_furnace.rs +++ b/crates/pumpkin/src/block/blocks/blast_furnace.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::entities::{ PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity, @@ -13,11 +14,10 @@ use pumpkin_data::{ use pumpkin_inventory::{ furnace_like::furnace_like_screen_handler::FurnaceLikeScreenHandler, player::player_inventory::PlayerInventory, - screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, + screen_handler::{InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, }; use pumpkin_macros::pumpkin_block; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; use crate::{ block::{ @@ -48,27 +48,24 @@ impl BlastingFurnaceScreenFactory { } impl ScreenHandlerFactory for BlastingFurnaceScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let concrete_handler = FurnaceLikeScreenHandler::new( - sync_id, - player_inventory, - self.inventory.clone(), - self.property_delegate.clone(), - self.experience_container.clone(), - WindowType::BlastFurnace, - ) - .await; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = FurnaceLikeScreenHandler::new( + sync_id, + player_inventory, + self.inventory.clone(), + &self.property_delegate, + self.experience_container.clone(), + WindowType::BlastFurnace, + ); - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> pumpkin_util::text::TextComponent { @@ -99,13 +96,8 @@ impl BlockBehaviour for BlastFurnaceBlock { property_delegate, experience_container, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&blasting_furnace_screen_factory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&blasting_furnace_screen_factory, Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } diff --git a/crates/pumpkin/src/block/blocks/brewing_stand.rs b/crates/pumpkin/src/block/blocks/brewing_stand.rs index 73c347d02..182f6f947 100644 --- a/crates/pumpkin/src/block/blocks/brewing_stand.rs +++ b/crates/pumpkin/src/block/blocks/brewing_stand.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::{GetComparatorOutputArgs, PlacedArgs}; use crate::block::{ @@ -9,7 +10,9 @@ use crate::block::{ use crate::block::entities::brewing_stand::BrewingStandBlockEntity; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; -use pumpkin_inventory::screen_handler::{BoxFuture, ScreenHandlerFactory, SharedScreenHandler}; +use pumpkin_inventory::screen_handler::{ + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, +}; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; @@ -20,21 +23,15 @@ struct BrewingScreenFactory( ); impl ScreenHandlerFactory for BrewingScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn pumpkin_inventory::screen_handler::InventoryPlayer, - ) -> BoxFuture<'a, Option> { + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { let inventory = self.0.clone(); - let property_delegate = self.1.clone(); - let pi = player_inventory.clone(); - Box::pin(async move { - // Delegate to pumpkin-inventory brewing creator - pumpkin_inventory::brewing::create_brewing(sync_id, pi, inventory, property_delegate) - .await - .map(|handler| Arc::new(tokio::sync::Mutex::new(handler)) as SharedScreenHandler) - }) + pumpkin_inventory::brewing::create_brewing(sync_id, player_inventory, inventory, &self.1) + .map(|handler| Arc::new(Mutex::new(handler)) as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -59,13 +56,8 @@ impl BlockBehaviour for BrewingStandBlock { pumpkin_data::statistic::CustomStatistic::InteractWithBrewingstand as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&BrewingScreenFactory(inventory, pd), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&BrewingScreenFactory(inventory, pd), Some(*args.position)); } BlockActionResult::Success @@ -83,7 +75,7 @@ impl BlockBehaviour for BrewingStandBlock { let mut bottles = 0u8; // Bottle slots are 0, 1, 2 in brewing stands for slot in 0..3 { - let stack = futures::executor::block_on(inventory.get_stack(slot)); + let stack = inventory.get_stack(slot); if !stack.is_empty() { bottles += 1; } diff --git a/crates/pumpkin/src/block/blocks/cartography_table.rs b/crates/pumpkin/src/block/blocks/cartography_table.rs index 16397d7b4..8e016f517 100644 --- a/crates/pumpkin/src/block/blocks/cartography_table.rs +++ b/crates/pumpkin/src/block/blocks/cartography_table.rs @@ -5,12 +5,12 @@ use pumpkin_data::translation; use pumpkin_inventory::cartography_table_screen_handler::CartographyTableScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; #[pumpkin_block("minecraft:cartography_table")] pub struct CartographyTableBlock; @@ -22,13 +22,8 @@ impl BlockBehaviour for CartographyTableBlock { pumpkin_data::statistic::CustomStatistic::InteractWithCartographyTable as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&CartographyTableScreenFactory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&CartographyTableScreenFactory, Some(*args.position)); BlockActionResult::Success } @@ -37,18 +32,16 @@ impl BlockBehaviour for CartographyTableBlock { struct CartographyTableScreenFactory; impl ScreenHandlerFactory for CartographyTableScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler: SharedScreenHandler = Arc::new(Mutex::new( - CartographyTableScreenHandler::new(sync_id, player_inventory), - )); - Some(handler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler: SharedScreenHandler = Arc::new(Mutex::new( + CartographyTableScreenHandler::new(sync_id, player_inventory), + )); + Some(handler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/chests.rs b/crates/pumpkin/src/block/blocks/chests.rs index 60bf81313..06db7ef4d 100644 --- a/crates/pumpkin/src/block/blocks/chests.rs +++ b/crates/pumpkin/src/block/blocks/chests.rs @@ -13,7 +13,7 @@ use pumpkin_inventory::double::DoubleInventory; use pumpkin_inventory::generic_container_screen_handler::{create_generic_9x3, create_generic_9x6}; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag}; use pumpkin_util::GameMode; @@ -21,7 +21,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; use pumpkin_world::world::BlockFlags; -use tokio::sync::Mutex; +use std::sync::Mutex; use crate::block::{ BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs, @@ -39,23 +39,21 @@ use crate::{ struct ChestScreenFactory(Arc); impl ScreenHandlerFactory for ChestScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let concrete_handler = if self.0.size() > 27 { - create_generic_9x6(sync_id, player_inventory, self.0.clone()).await - } else { - create_generic_9x3(sync_id, player_inventory, self.0.clone()).await - }; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = if self.0.size() > 27 { + create_generic_9x6(sync_id, player_inventory, self.0.clone()) + } else { + create_generic_9x3(sync_id, player_inventory, self.0.clone()) + }; - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -196,10 +194,8 @@ fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { && let Some(table) = get_chest_loot_table(&loot_key) && let Some(inv) = entity.clone().get_inventory() { - tokio::spawn(async move { - fill_chest_inventory(&inv, table, seed).await; - inv.mark_dirty(); - }); + fill_chest_inventory(&inv, table, seed); + inv.mark_dirty(); } let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else { @@ -240,13 +236,8 @@ fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { first_inventory }; - let player = args.player.clone(); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&ChestScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&ChestScreenFactory(inventory), Some(*args.position)); BlockActionResult::Success } diff --git a/crates/pumpkin/src/block/blocks/crafting_table.rs b/crates/pumpkin/src/block/blocks/crafting_table.rs index 6a67fd6cd..ffd4b9efa 100644 --- a/crates/pumpkin/src/block/blocks/crafting_table.rs +++ b/crates/pumpkin/src/block/blocks/crafting_table.rs @@ -5,12 +5,12 @@ use pumpkin_data::translation; use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; @@ -22,14 +22,11 @@ impl BlockBehaviour for CraftingTableBlock { pumpkin_data::statistic::CustomStatistic::InteractWithCraftingTable as i32, 1, ); - let player = Arc::clone(args.player); let recipe_manager = args.server.recipe_manager.clone(); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&CraftingTableScreenFactory(recipe_manager), Some(pos)) - .await; - }); + args.player.open_handled_screen( + &CraftingTableScreenFactory(recipe_manager), + Some(*args.position), + ); BlockActionResult::Success } @@ -38,20 +35,17 @@ impl BlockBehaviour for CraftingTableBlock { struct CraftingTableScreenFactory(Arc); impl ScreenHandlerFactory for CraftingTableScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = - CraftingTableScreenHandler::new(sync_id, player_inventory, Some(self.0.clone())) - .await; - let concrete_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = + CraftingTableScreenHandler::new(sync_id, player_inventory, Some(self.0.clone())); + let concrete_arc = Arc::new(Mutex::new(handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/enchanting_table.rs b/crates/pumpkin/src/block/blocks/enchanting_table.rs index dea630648..f4480cfaa 100644 --- a/crates/pumpkin/src/block/blocks/enchanting_table.rs +++ b/crates/pumpkin/src/block/blocks/enchanting_table.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::entities::enchanting_table::EnchantingTableBlockEntity; use crate::block::registry::BlockActionResult; @@ -7,13 +8,12 @@ use pumpkin_data::{Block, BlockStateId, translation}; use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::{Inventory, SimpleInventory}; -use tokio::sync::Mutex; #[pumpkin_block("minecraft:enchanting_table")] pub struct EnchantingTableBlock; @@ -69,20 +69,14 @@ impl BlockBehaviour for EnchantingTableBlock { } let bookshelf_count = bookshelf_count.min(15); - let player = Arc::clone(args.player); - let pos = *args.position; let seed = args.player.enchantment_seed(); - tokio::spawn(async move { - player - .open_handled_screen( - &EnchantingTableScreenFactory { - bookshelf_count, - seed, - }, - Some(pos), - ) - .await; - }); + args.player.open_handled_screen( + &EnchantingTableScreenFactory { + bookshelf_count, + seed, + }, + Some(*args.position), + ); BlockActionResult::Success } } @@ -101,24 +95,22 @@ struct EnchantingTableScreenFactory { } impl ScreenHandlerFactory for EnchantingTableScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let inventory: Arc = Arc::new(SimpleInventory::new(2)); - let handler = EnchantingTableScreenHandler::new( - sync_id, - player_inventory, - &inventory, - self.seed, - self.bookshelf_count, - ); - let screen_handler_arc = Arc::new(Mutex::new(handler)); - Some(screen_handler_arc as SharedScreenHandler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let inventory: Arc = Arc::new(SimpleInventory::new(2)); + let handler = EnchantingTableScreenHandler::new( + sync_id, + player_inventory, + &inventory, + self.seed, + self.bookshelf_count, + ); + let screen_handler_arc = Arc::new(Mutex::new(handler)); + Some(screen_handler_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/ender_chest.rs b/crates/pumpkin/src/block/blocks/ender_chest.rs index 6a1fc77f6..1f370b097 100644 --- a/crates/pumpkin/src/block/blocks/ender_chest.rs +++ b/crates/pumpkin/src/block/blocks/ender_chest.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::entities::ender_chest::EnderChestBlockEntity; use crate::block::{ @@ -13,13 +14,12 @@ use pumpkin_inventory::{ generic_container_screen_handler::create_generic_9x3, player::ender_chest_inventory::EnderChestInventory, player::player_inventory::PlayerInventory, - screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, + screen_handler::{InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::block::viewer::ViewerCountTracker; -use tokio::sync::Mutex; pub struct EnderChestScreenFactory { pub inventory: Arc, @@ -27,22 +27,19 @@ pub struct EnderChestScreenFactory { } impl ScreenHandlerFactory for EnderChestScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - if let Some(tracker) = &self.tracker { - self.inventory.set_tracker(tracker.clone()); - } - let handler = - create_generic_9x3(sync_id, player_inventory, self.inventory.clone()).await; - let concrete_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + if let Some(tracker) = &self.tracker { + self.inventory.set_tracker(tracker.clone()); + } + let handler = create_generic_9x3(sync_id, player_inventory, self.inventory.clone()); + let concrete_arc = Arc::new(Mutex::new(handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -96,21 +93,15 @@ impl BlockBehaviour for EnderChestBlock { pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; let tracker = block_entity.get_tracker(); - tokio::spawn(async move { - let inventory = player.ender_chest_inventory(); - player - .open_handled_screen( - &EnderChestScreenFactory { - inventory: inventory.clone(), - tracker: Some(tracker), - }, - Some(pos), - ) - .await; - }); + let inventory = args.player.ender_chest_inventory(); + args.player.open_handled_screen( + &EnderChestScreenFactory { + inventory: inventory.clone(), + tracker: Some(tracker), + }, + Some(*args.position), + ); // TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true); } diff --git a/crates/pumpkin/src/block/blocks/furnace.rs b/crates/pumpkin/src/block/blocks/furnace.rs index a484a46d0..ad9e69c0d 100644 --- a/crates/pumpkin/src/block/blocks/furnace.rs +++ b/crates/pumpkin/src/block/blocks/furnace.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::entities::{ PropertyDelegate, furnace::FurnaceBlockEntity, furnace_like_block_entity::ExperienceContainer, @@ -12,11 +13,10 @@ use pumpkin_data::{ use pumpkin_inventory::{ furnace_like::furnace_like_screen_handler::FurnaceLikeScreenHandler, player::player_inventory::PlayerInventory, - screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, + screen_handler::{InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, }; use pumpkin_macros::pumpkin_block; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; use crate::{ block::{ @@ -47,27 +47,24 @@ impl FurnaceScreenFactory { } impl ScreenHandlerFactory for FurnaceScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let concrete_handler = FurnaceLikeScreenHandler::new( - sync_id, - player_inventory, - self.inventory.clone(), - self.property_delegate.clone(), - self.experience_container.clone(), - WindowType::Furnace, - ) - .await; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = FurnaceLikeScreenHandler::new( + sync_id, + player_inventory, + self.inventory.clone(), + &self.property_delegate, + self.experience_container.clone(), + WindowType::Furnace, + ); - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> pumpkin_util::text::TextComponent { @@ -95,13 +92,8 @@ impl BlockBehaviour for FurnaceBlock { ); let furnace_screen_factory = FurnaceScreenFactory::new(inventory, property_delegate, experience_container); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&furnace_screen_factory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&furnace_screen_factory, Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } diff --git a/crates/pumpkin/src/block/blocks/hopper.rs b/crates/pumpkin/src/block/blocks/hopper.rs index 4d95d5614..e5a4774ea 100644 --- a/crates/pumpkin/src/block/blocks/hopper.rs +++ b/crates/pumpkin/src/block/blocks/hopper.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::{GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs}; @@ -15,31 +16,27 @@ use pumpkin_data::{Block, BlockDirection, translation}; use pumpkin_inventory::generic_container_screen_handler::create_hopper; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; use pumpkin_world::world::BlockFlags; -use tokio::sync::Mutex; struct HopperBlockScreenFactory(Arc); impl ScreenHandlerFactory for HopperBlockScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone()).await; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone()); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -65,13 +62,8 @@ impl BlockBehaviour for HopperBlock { pumpkin_data::statistic::CustomStatistic::InspectHopper as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&HopperBlockScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&HopperBlockScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success diff --git a/crates/pumpkin/src/block/blocks/lectern.rs b/crates/pumpkin/src/block/blocks/lectern.rs index 219ea693d..a27bef28c 100644 --- a/crates/pumpkin/src/block/blocks/lectern.rs +++ b/crates/pumpkin/src/block/blocks/lectern.rs @@ -20,7 +20,7 @@ use pumpkin_data::{Block, BlockDirection, BlockStateId, tag, translation}; use pumpkin_inventory::lectern_screen_handler::{LecternController, LecternScreenHandler}; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, ScreenHandlerFuture, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; @@ -29,7 +29,7 @@ use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; -use tokio::sync::Mutex; +use std::sync::Mutex; /// Bridges the screen handler back into the world: page changes emit the /// vanilla redstone pulse and taking the book clears `has_book`. @@ -51,29 +51,25 @@ impl LecternController for LecternPageController { .map_or(0, |entity| entity.page.load(Ordering::Relaxed) as i32) } - fn set_page(&self, page: i32) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - let Some(entity) = self.entity() else { - return; - }; - let page_count = entity.page_count().await; - let page = page.clamp(0, (page_count - 1).max(0)); - if page == entity.page.load(Ordering::Relaxed) as i32 { - return; - } - entity.page.store(page as usize, Ordering::Relaxed); - entity.mark_dirty(); - LecternBlock::pulse(&self.world, &self.position); - }) + fn set_page(&self, page: i32) { + let Some(entity) = self.entity() else { + return; + }; + let page_count = entity.page_count(); + let page = page.clamp(0, (page_count - 1).max(0)); + if page == entity.page.load(Ordering::Relaxed) as i32 { + return; + } + entity.page.store(page as usize, Ordering::Relaxed); + entity.mark_dirty(); + LecternBlock::pulse(&self.world, &self.position); } - fn on_book_taken(&self) -> ScreenHandlerFuture<'_, ()> { - Box::pin(async move { - if let Some(entity) = self.entity() { - entity.page.store(0, Ordering::Relaxed); - } - LecternBlock::set_has_book(&self.world, &self.position, false); - }) + fn on_book_taken(&self) { + if let Some(entity) = self.entity() { + entity.page.store(0, Ordering::Relaxed); + } + LecternBlock::set_has_book(&self.world, &self.position, false); } } @@ -83,17 +79,15 @@ struct LecternScreenFactory { } impl ScreenHandlerFactory for LecternScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - _player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = - LecternScreenHandler::new(sync_id, self.inventory.clone(), self.controller.clone()); - Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) - }) + _player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = + LecternScreenHandler::new(sync_id, self.inventory.clone(), self.controller.clone()); + Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -194,19 +188,13 @@ impl BlockBehaviour for LecternBlock { position: *args.position, inventory: inventory.clone(), }); - let player = args.player.clone(); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen( - &LecternScreenFactory { - inventory, - controller, - }, - Some(pos), - ) - .await; - }); + args.player.open_handled_screen( + &LecternScreenFactory { + inventory, + controller, + }, + Some(*args.position), + ); BlockActionResult::Success } @@ -234,7 +222,7 @@ impl BlockBehaviour for LecternBlock { }; let book = item_stack.split_unless_creative(args.player.gamemode.load(), 1); - futures::executor::block_on(lectern.set_stack(0, book)); + lectern.set_stack(0, book); Self::set_has_book(args.world, args.position, true); args.world @@ -287,7 +275,7 @@ impl BlockBehaviour for LecternBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(lectern_entity) = block_entity.as_any().downcast_ref::() { - let book = futures::executor::block_on(lectern_entity.remove_stack(0)); + let book = lectern_entity.remove_stack(0); if !book.is_empty() { // Drop the book item let entity = Entity::new( @@ -309,9 +297,7 @@ impl BlockBehaviour for LecternBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(lectern_entity) = block_entity.as_any().downcast_ref::() { - Some(futures::executor::block_on( - lectern_entity.comparator_output(), - )) + Some(lectern_entity.comparator_output()) } else { Some(0) } diff --git a/crates/pumpkin/src/block/blocks/loom.rs b/crates/pumpkin/src/block/blocks/loom.rs index 931344582..df4624d9a 100644 --- a/crates/pumpkin/src/block/blocks/loom.rs +++ b/crates/pumpkin/src/block/blocks/loom.rs @@ -8,12 +8,12 @@ use pumpkin_data::{BlockStateId, FacingExt}; use pumpkin_inventory::loom_screen_handler::LoomScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; #[pumpkin_block("minecraft:loom")] pub struct LoomBlock; @@ -39,13 +39,8 @@ impl BlockBehaviour for LoomBlock { pumpkin_data::statistic::CustomStatistic::InteractWithLoom as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&LoomScreenFactory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&LoomScreenFactory, Some(*args.position)); BlockActionResult::Success } @@ -54,19 +49,17 @@ impl BlockBehaviour for LoomBlock { struct LoomScreenFactory; impl ScreenHandlerFactory for LoomScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler: SharedScreenHandler = Arc::new(Mutex::new(LoomScreenHandler::new( - sync_id, - player_inventory, - ))); - Some(handler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler: SharedScreenHandler = Arc::new(Mutex::new(LoomScreenHandler::new( + sync_id, + player_inventory, + ))); + Some(handler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/redstone/crafter.rs b/crates/pumpkin/src/block/blocks/redstone/crafter.rs index b486e0a4e..cf62b1152 100644 --- a/crates/pumpkin/src/block/blocks/redstone/crafter.rs +++ b/crates/pumpkin/src/block/blocks/redstone/crafter.rs @@ -1,5 +1,5 @@ use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::entities::crafter::CrafterBlockEntity; @@ -17,7 +17,7 @@ use pumpkin_data::{BlockDirection, BlockStateId}; use pumpkin_inventory::generic_container_screen_handler::create_crafter_3x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; @@ -28,18 +28,16 @@ use pumpkin_world::world::BlockFlags; struct CrafterScreenFactory(Arc); impl ScreenHandlerFactory for CrafterScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = create_crafter_3x3(sync_id, player_inventory, self.0.clone()).await; - let screen_handler_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = create_crafter_3x3(sync_id, player_inventory, self.0.clone()); + let screen_handler_arc = Arc::new(Mutex::new(handler)); - Some(screen_handler_arc as SharedScreenHandler) - }) + Some(screen_handler_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -58,13 +56,8 @@ impl BlockBehaviour for CrafterBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(inventory) = block_entity.get_inventory() { - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&CrafterScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&CrafterScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success } @@ -171,7 +164,10 @@ impl BlockBehaviour for CrafterBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) { let crafter = block_entity.as_any().downcast_ref::()?; - let items = crafter.items.blocking_read(); + let items = crafter + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); let occupied = items.iter().filter(|s| !s.is_empty()).count() as u8; Some(occupied) } else { diff --git a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs index 411ccec77..27ceff365 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -1,7 +1,7 @@ use rand::{Rng, RngExt, rng}; use std::sync::Arc; +use std::sync::Mutex; use std::sync::atomic::AtomicBool; -use tokio::sync::Mutex; use uuid::Uuid; use crate::block::blocks::redstone::block_receives_redstone_power; @@ -50,7 +50,7 @@ use pumpkin_data::{Block, BlockStateId, FacingExt}; use pumpkin_inventory::generic_container_screen_handler::create_generic_3x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions}; @@ -65,18 +65,16 @@ use pumpkin_world::world::BlockFlags; struct DispenserScreenFactory(Arc); impl ScreenHandlerFactory for DispenserScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()).await; - let screen_handler_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()); + let screen_handler_arc = Arc::new(Mutex::new(handler)); - Some(screen_handler_arc as SharedScreenHandler) - }) + Some(screen_handler_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -129,13 +127,8 @@ impl BlockBehaviour for DispenserBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(inventory) = block_entity.get_inventory() { - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&DispenserScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&DispenserScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success } @@ -191,7 +184,7 @@ impl BlockBehaviour for DispenserBlock { return; }; - if let Some((slot_index, mut item)) = dispenser.get_random_slot().await { + if let Some((slot_index, mut item)) = dispenser.get_random_slot() { let props = DispenserLikeProperties::from_state_id(state.id, block); let ctx = DispenseContext { world: &world, @@ -199,7 +192,7 @@ impl BlockBehaviour for DispenserBlock { facing: props.facing, }; Self::dispense(&ctx, dispenser, &mut item).await; - dispenser.set_stack(slot_index, item).await; + dispenser.set_stack(slot_index, item); } else { world.sync_world_event(WorldEvent::SoundDispenserFail, position, 0); } @@ -291,7 +284,7 @@ impl DispenserBlock { Self::dispense_firework_rocket(ctx, item); } else if item.item.id == Item::BUCKET.id { // Empty buckets pick up the fluid in front of the dispenser - Self::dispense_empty_bucket(ctx, dispenser, item).await; + Self::dispense_empty_bucket(ctx, dispenser, item); } else if FilledBucketItem::ids().contains(&item.item.id) { // Filled buckets place their fluid in front of the dispenser Self::dispense_filled_bucket(ctx, item); @@ -663,7 +656,7 @@ impl DispenserBlock { Self::finish_projectile_launch(ctx, Arc::new(rocket), WorldEvent::SoundFireworkShoot); } - async fn dispense_empty_bucket( + fn dispense_empty_bucket( ctx: &DispenseContext<'_>, dispenser: &DispenserBlockEntity, item: &mut ItemStack, @@ -678,7 +671,7 @@ impl DispenserBlock { let filled_stack = ItemStack::new(1, filled); if item.is_empty() { *item = filled_stack; - } else if let Some(rest) = Self::add_to_first_free_slot(dispenser, filled_stack).await { + } else if let Some(rest) = Self::add_to_first_free_slot(dispenser, filled_stack) { Self::eject_item(ctx, rest); } @@ -688,11 +681,14 @@ impl DispenserBlock { /// Places `stack` into the first empty slot, returning it back if every slot is occupied. /// The slot currently being dispensed from still holds its pre-dispense stack, so it is /// never considered free. - async fn add_to_first_free_slot( + fn add_to_first_free_slot( dispenser: &DispenserBlockEntity, stack: ItemStack, ) -> Option { - let mut items = dispenser.items.write().await; + let mut items = dispenser + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); for slot in items.iter_mut() { if slot.is_empty() { *slot = stack; diff --git a/crates/pumpkin/src/block/blocks/redstone/dropper.rs b/crates/pumpkin/src/block/blocks/redstone/dropper.rs index 74dc8ce3b..31d824f1a 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dropper.rs @@ -1,6 +1,6 @@ use rand::{Rng, RngExt, rng}; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; @@ -21,7 +21,7 @@ use pumpkin_data::{FacingExt, translation}; use pumpkin_inventory::generic_container_screen_handler::create_generic_3x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::vector3::Vector3; @@ -33,18 +33,16 @@ use pumpkin_world::world::BlockFlags; struct DropperScreenFactory(Arc); impl ScreenHandlerFactory for DropperScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()).await; - let screen_handler_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()); + let screen_handler_arc = Arc::new(Mutex::new(handler)); - Some(screen_handler_arc as SharedScreenHandler) - }) + Some(screen_handler_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -91,13 +89,8 @@ impl BlockBehaviour for DropperBlock { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(inventory) = block_entity.get_inventory() { - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&DropperScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&DropperScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success } @@ -152,7 +145,7 @@ impl BlockBehaviour for DropperBlock { return; }; - if let Some((slot_index, mut item)) = dropper.get_random_slot().await { + if let Some((slot_index, mut item)) = dropper.get_random_slot() { let props = DispenserLikeProperties::from_state_id(state.id, block); let target_pos = position.offset(props.facing.to_block_direction().to_offset()); @@ -163,20 +156,18 @@ impl BlockBehaviour for DropperBlock { let backup = item.clone(); let one_item = item.split(1); - if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item) - .await - { - dropper.set_stack(slot_index, item).await; + if HopperBlockEntity::add_one_item(dropper, container.as_ref(), &one_item) { + dropper.set_stack(slot_index, item); return; } - dropper.set_stack(slot_index, backup).await; + dropper.set_stack(slot_index, backup); return; } // No container found, dispense item into the world let drop_item = item.split(1); - dropper.set_stack(slot_index, item).await; + dropper.set_stack(slot_index, item); let facing = to_normal(props.facing); let mut pos = position.to_centered_f64().add(&(facing * 0.7)); diff --git a/crates/pumpkin/src/block/blocks/shulker_box.rs b/crates/pumpkin/src/block/blocks/shulker_box.rs index de931ab0e..91cd95526 100644 --- a/crates/pumpkin/src/block/blocks/shulker_box.rs +++ b/crates/pumpkin/src/block/blocks/shulker_box.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs}; use crate::block::{ @@ -13,28 +14,25 @@ use pumpkin_data::translation; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; struct ShulkerBoxScreenFactory(Arc); impl ScreenHandlerFactory for ShulkerBoxScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await; - let screen_handler_arc = Arc::new(Mutex::new(handler)); + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()); + let screen_handler_arc = Arc::new(Mutex::new(handler)); - Some(screen_handler_arc as SharedScreenHandler) - }) + Some(screen_handler_arc as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -79,13 +77,8 @@ impl BlockBehaviour for ShulkerBoxBlock { pumpkin_data::statistic::CustomStatistic::OpenShulkerBox as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&ShulkerBoxScreenFactory(inventory), Some(pos)) - .await; - }); + args.player + .open_handled_screen(&ShulkerBoxScreenFactory(inventory), Some(*args.position)); } BlockActionResult::Success diff --git a/crates/pumpkin/src/block/blocks/smithing_table.rs b/crates/pumpkin/src/block/blocks/smithing_table.rs index 66153e777..01fb069d8 100644 --- a/crates/pumpkin/src/block/blocks/smithing_table.rs +++ b/crates/pumpkin/src/block/blocks/smithing_table.rs @@ -4,13 +4,13 @@ use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_inventory::smithing_table_screen_handler::SmithingTableScreenHandler; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; #[pumpkin_block("minecraft:smithing_table")] pub struct SmithingTableBlock; @@ -22,13 +22,8 @@ impl BlockBehaviour for SmithingTableBlock { pumpkin_data::statistic::CustomStatistic::InteractWithSmithingTable as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&SmithingTableScreenFactory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&SmithingTableScreenFactory, Some(*args.position)); BlockActionResult::Success } @@ -37,18 +32,17 @@ impl BlockBehaviour for SmithingTableBlock { struct SmithingTableScreenFactory; impl ScreenHandlerFactory for SmithingTableScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler: SharedScreenHandler = Arc::new(Mutex::new( - SmithingTableScreenHandler::new(sync_id, player_inventory), - )); - Some(handler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler: SharedScreenHandler = Arc::new(Mutex::new(SmithingTableScreenHandler::new( + sync_id, + player_inventory, + ))); + Some(handler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/blocks/smoker.rs b/crates/pumpkin/src/block/blocks/smoker.rs index 3e5b35926..c9c8c851a 100644 --- a/crates/pumpkin/src/block/blocks/smoker.rs +++ b/crates/pumpkin/src/block/blocks/smoker.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::sync::Mutex; use crate::block::entities::{ PropertyDelegate, furnace_like_block_entity::ExperienceContainer, smoker::SmokerBlockEntity, @@ -12,11 +13,10 @@ use pumpkin_data::{ use pumpkin_inventory::{ furnace_like::furnace_like_screen_handler::FurnaceLikeScreenHandler, player::player_inventory::PlayerInventory, - screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, + screen_handler::{InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, }; use pumpkin_macros::pumpkin_block; use pumpkin_world::inventory::Inventory; -use tokio::sync::Mutex; use crate::{ block::{ @@ -47,27 +47,24 @@ impl SmokerScreenFactory { } impl ScreenHandlerFactory for SmokerScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let concrete_handler = FurnaceLikeScreenHandler::new( - sync_id, - player_inventory, - self.inventory.clone(), - self.property_delegate.clone(), - self.experience_container.clone(), - WindowType::Smoker, - ) - .await; + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let concrete_handler = FurnaceLikeScreenHandler::new( + sync_id, + player_inventory, + self.inventory.clone(), + &self.property_delegate, + self.experience_container.clone(), + WindowType::Smoker, + ); - let concrete_arc = Arc::new(Mutex::new(concrete_handler)); + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); - Some(concrete_arc as SharedScreenHandler) - }) + Some(concrete_arc as SharedScreenHandler) } fn get_display_name(&self) -> pumpkin_util::text::TextComponent { @@ -94,13 +91,8 @@ impl BlockBehaviour for SmokerBlock { ); let smoker_screen_factory = SmokerScreenFactory::new(inventory, property_delegate, experience_container); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&smoker_screen_factory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&smoker_screen_factory, Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } diff --git a/crates/pumpkin/src/block/blocks/stonecutter.rs b/crates/pumpkin/src/block/blocks/stonecutter.rs index 01f6ba246..29e39dd6d 100644 --- a/crates/pumpkin/src/block/blocks/stonecutter.rs +++ b/crates/pumpkin/src/block/blocks/stonecutter.rs @@ -4,12 +4,12 @@ use crate::block::{BlockBehaviour, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::Mutex; use pumpkin_inventory::stonecutter_screen_handler::StonecutterScreenHandler; @@ -23,13 +23,8 @@ impl BlockBehaviour for StonecutterBlock { pumpkin_data::statistic::CustomStatistic::InteractWithStonecutter as i32, 1, ); - let player = Arc::clone(args.player); - let pos = *args.position; - tokio::spawn(async move { - player - .open_handled_screen(&StonecutterScreenFactory, Some(pos)) - .await; - }); + args.player + .open_handled_screen(&StonecutterScreenFactory, Some(*args.position)); BlockActionResult::Success } @@ -38,19 +33,17 @@ impl BlockBehaviour for StonecutterBlock { struct StonecutterScreenFactory; impl ScreenHandlerFactory for StonecutterScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let handler: SharedScreenHandler = Arc::new(Mutex::new(StonecutterScreenHandler::new( - sync_id, - player_inventory, - ))); - Some(handler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let handler: SharedScreenHandler = Arc::new(Mutex::new(StonecutterScreenHandler::new( + sync_id, + player_inventory, + ))); + Some(handler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/block/entities/barrel.rs b/crates/pumpkin/src/block/entities/barrel.rs index 818dc669a..02b474f7d 100644 --- a/crates/pumpkin/src/block/entities/barrel.rs +++ b/crates/pumpkin/src/block/entities/barrel.rs @@ -8,6 +8,7 @@ use pumpkin_util::random::xoroshiro128::Xoroshiro; use pumpkin_util::random::{RandomImpl, get_seed}; use std::any::Any; use std::pin::Pin; +use std::sync::RwLock; use std::{ array::from_fn, sync::{ @@ -18,14 +19,13 @@ use std::{ use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt}; use crate::world::{BlockFlags, World}; -use pumpkin_world::inventory::InventoryFuture; use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use super::BlockEntity; pub struct BarrelBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, // Viewer @@ -47,12 +47,18 @@ impl BlockEntity for BarrelBlockEntity { { let mut barrel = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), viewers: ViewerCountTracker::new(), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, barrel.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + barrel + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); barrel } @@ -61,7 +67,9 @@ impl BlockEntity for BarrelBlockEntity { &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { - self.write_inventory_nbt(nbt, true) + Box::pin(async move { + self.write_inventory_nbt(nbt, true); + }) } fn tick(&self, world: &Arc) { @@ -114,7 +122,7 @@ impl BarrelBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), viewers: ViewerCountTracker::new(), } @@ -159,60 +167,61 @@ impl Inventory for BarrelBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.open_container(); - }) + fn on_open(&self) { + self.viewers.open_container(); } - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.close_container(); - }) + fn on_close(&self) { + self.viewers.close_container(); } fn mark_dirty(&self) { @@ -225,11 +234,12 @@ impl Inventory for BarrelBlockEntity { } impl Clearable for BarrelBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/beacon.rs b/crates/pumpkin/src/block/entities/beacon.rs index ad44e6173..1caa7bcc1 100644 --- a/crates/pumpkin/src/block/entities/beacon.rs +++ b/crates/pumpkin/src/block/entities/beacon.rs @@ -3,19 +3,18 @@ use pumpkin_data::data_component_impl::IDSetContent; use pumpkin_data::tag::Taggable; use std::any::Any; use std::pin::Pin; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; +use std::sync::{Arc, Mutex}; use pumpkin_data::effect::StatusEffect; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; use crate::block::entities::BlockEntity; use crate::world::World; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; pub struct BeaconBlockEntity { pub position: BlockPos, @@ -100,7 +99,7 @@ impl BeaconBlockEntity { if levels < 4 { return false; } - // Regeneration (10) or tier II upgrade matching primary + // Regeneration (10) or identical to primary secondary_id == 10 || secondary_id == primary_id } @@ -120,48 +119,50 @@ impl BeaconBlockEntity { true } - /// Replicates Java's `updateBase` logic - fn update_base(&self, world: &Arc) -> i32 { - let mut levels = 0; + pub fn update_base(&self, world: &Arc) -> i32 { let x = self.position.0.x; let y = self.position.0.y; let z = self.position.0.z; - for step in 1..=4 { - let ly = y - step; - if ly < world.dimension.min_y { + let mut current_level = 0; + + for level in 1..=4 { + let layer_y = y - level; + if layer_y < world.dimension.min_y { break; } - let mut is_ok = true; - for lx in (x - step)..=(x + step) { - for lz in (z - step)..=(z + step) { - let pos = BlockPos::new(lx, ly, lz); - let block = world.get_block(&pos); + let mut layer_valid = true; + for dx in -level..=level { + for dz in -level..=level { + let block_pos = BlockPos::new(x + dx, layer_y, z + dz); + let state = world.get_block_state(&block_pos); + let block = world.get_block(&block_pos); + if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEACON_BASE_BLOCKS) { - is_ok = false; + layer_valid = false; break; } + + // Optional: Stricter block type validations can happen here + let _ = state; } - if !is_ok { + if !layer_valid { break; } } - if !is_ok { + if layer_valid { + current_level = level; + } else { break; } - levels = step; } - levels + + current_level } - /// Replicates Java's `applyEffects` bounding box mapping and duration mapping - fn apply_effects(&self, world: &Arc, levels: i32) { - if levels <= 0 { - return; - } - + pub fn apply_effects(&self, world: &Arc, levels: i32) { let primary_id = self.primary_effect.load(Ordering::Relaxed); let secondary_id = self.secondary_effect.load(Ordering::Relaxed); @@ -170,29 +171,32 @@ impl BeaconBlockEntity { } let primary_effect = StatusEffect::from_id(primary_id as u16); - let secondary_effect = if secondary_id > 0 { - StatusEffect::from_id(secondary_id as u16) - } else { - None - }; - - // Vanilla: expandTowards(0.0, level.getHeight(), 0.0) -> Reaches across the entire Y axis - let range = (levels * 10 + 10) as f64; - let pos = self.position.0.to_f64(); - - // Use the dimension height for vanilla parity (usually 384.0 in modern versions) - let world_height = world.dimension.height as f64; - - let bounding_box = BoundingBox::new(pos, pos.add_raw(1.0, 1.0, 1.0)) - .expand(range, range, range) - .expand_towards(0.0, world_height, 0.0); - - let players = world.get_players_at_box(&bounding_box); + let secondary_effect = StatusEffect::from_id(secondary_id as u16); + // Vanilla duration: (9 + levels * 2) * 20 ticks let duration_ticks = (9 + levels * 2) * 20; + + // Base amplifier: primary gets amp 1 (Level II) if secondary matches primary let base_amp = i32::from(levels >= 4 && primary_id == secondary_id); - for player in players { + // Vanilla Range is level * 10 + 10 blocks in each horizontal direction + let range = f64::from(levels * 10 + 10); + let pos = self.position.0.to_f64(); + let box_min = [pos.x - range, pos.y - range, pos.z - range]; + let box_max = [ + pos.x + range + 1.0, + pos.y + range + 1.0 + 384.0, + pos.z + range + 1.0, + ]; + let bounds = BoundingBox::new_array(box_min, box_max); + + // Apply effect to all players in range + let players = world.players.load(); + for player in players.iter() { + if !bounds.intersects(&player.living_entity.entity.bounding_box.load()) { + continue; + } + if let Some(effect) = primary_effect { player.add_effect(pumpkin_data::potion::Effect { effect_type: effect, @@ -273,10 +277,18 @@ impl BlockEntity for BeaconBlockEntity { ); nbt.put_int("Levels", self.levels.load(Ordering::Relaxed)); - if let Some(name) = &*self.custom_name.lock().await { + if let Some(name) = &*self + .custom_name + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + { nbt.put_string("CustomName", name.clone()); } - if let Some(lock) = &*self.lock_key.lock().await { + if let Some(lock) = &*self + .lock_key + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + { nbt.put_string("Lock", lock.clone()); } }) @@ -288,9 +300,6 @@ impl BlockEntity for BeaconBlockEntity { let levels = self.update_base(world); self.levels.store(levels, Ordering::Relaxed); - // TODO: Beam Section validation (scanning upward to heightmap to check for sky visibility) - // is typically checked here before applying effects in Vanilla. - if levels > 0 { self.apply_effects(world, levels); } @@ -324,10 +333,6 @@ impl BlockEntity for BeaconBlockEntity { fn as_any(&self) -> &dyn std::any::Any { self } - - fn get_inventory(self: Arc) -> Option> { - Some(self as Arc) - } } impl Inventory for BeaconBlockEntity { @@ -335,57 +340,64 @@ impl Inventory for BeaconBlockEntity { 1 } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { self.payment.lock().await.is_empty() }) + fn is_empty(&self) -> bool { + self.payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() } - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot == 0 { - self.payment.lock().await.clone() - } else { - ItemStack::EMPTY.clone() - } - }) + fn get_stack(&self, slot: usize) -> ItemStack { + if slot == 0 { + self.payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot == 0 { - let mut removed = ItemStack::EMPTY.clone(); - let mut guard = self.payment.lock().await; - std::mem::swap(&mut removed, &mut *guard); - self.mark_dirty(); - removed - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + if slot == 0 { + let mut removed = ItemStack::EMPTY.clone(); + let mut guard = self + .payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::swap(&mut removed, &mut *guard); + self.mark_dirty(); + removed + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - if slot == 0 { - let mut stack = self.payment.lock().await; - if stack.is_empty() { - return ItemStack::EMPTY.clone(); - } - let res = stack.split(amount); - self.mark_dirty(); - res - } else { - ItemStack::EMPTY.clone() + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + if slot == 0 { + let mut stack = self + .payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if stack.is_empty() { + return ItemStack::EMPTY.clone(); } - }) + let res = stack.split(amount); + self.mark_dirty(); + res + } else { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - if slot == 0 { - *self.payment.lock().await = stack; - self.mark_dirty(); - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + if slot == 0 { + *self + .payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; + self.mark_dirty(); + } } fn mark_dirty(&self) { @@ -398,11 +410,9 @@ impl Inventory for BeaconBlockEntity { } impl Clearable for BeaconBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - if let Ok(mut payment) = self.payment.try_lock() { - *payment = ItemStack::EMPTY.clone(); - } - }) + fn clear(&self) { + if let Ok(mut payment) = self.payment.try_lock() { + *payment = ItemStack::EMPTY.clone(); + } } } diff --git a/crates/pumpkin/src/block/entities/blasting_furnace.rs b/crates/pumpkin/src/block/entities/blasting_furnace.rs index 0571eeed9..41c8a2157 100644 --- a/crates/pumpkin/src/block/entities/blasting_furnace.rs +++ b/crates/pumpkin/src/block/entities/blasting_furnace.rs @@ -5,7 +5,7 @@ use std::{ array::from_fn, collections::HashMap, sync::{ - Arc, Mutex as StdMutex, + Arc, Mutex as StdMutex, RwLock, atomic::{AtomicBool, AtomicU16, Ordering}, }, }; @@ -29,7 +29,7 @@ pub struct BlastingFurnaceBlockEntity { pub lit_time_remaining: AtomicU16, pub lit_total_time: AtomicU16, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, /// Tracks recipes used for XP calculation (vanilla `RecipesUsed` NBT format) /// Maps result item ID -> craft count @@ -45,7 +45,7 @@ impl BlastingFurnaceBlockEntity { Self { position, dirty: AtomicBool::new(false), - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), cooking_total_time: AtomicU16::new(0), cooking_time_spent: AtomicU16::new(0), lit_total_time: AtomicU16::new(0), diff --git a/crates/pumpkin/src/block/entities/brewing_stand.rs b/crates/pumpkin/src/block/entities/brewing_stand.rs index 17af78ae8..cef0fe9dd 100644 --- a/crates/pumpkin/src/block/entities/brewing_stand.rs +++ b/crates/pumpkin/src/block/entities/brewing_stand.rs @@ -2,14 +2,13 @@ use std::any::Any; use std::future::Future; use std::pin::Pin; use std::sync::{ - Arc, Mutex as StdMutex, + Arc, Mutex as StdMutex, RwLock, atomic::AtomicI32, atomic::{AtomicBool, Ordering}, }; use crate::block::entities::PropertyDelegate; use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::data_component_impl::DataComponentImpl; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::potion_brewing::{ITEM_RECIPES, POTION_RECIPES}; @@ -19,7 +18,6 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::inventory::{Inventory, sync_read_items_from_nbt, sync_write_items_to_nbt}; -use tokio::sync::RwLock; pub struct BrewingStandBlockEntity { pub position: BlockPos, @@ -66,7 +64,7 @@ impl BrewingStandBlockEntity { let ingredient_id = ingredient.get_item().id; // Check potion recipes (water bottle -> potions, potion upgrades, etc.) - let Ok(items) = self.items.try_read() else { + let Ok(items) = self.items.read() else { return false; }; for slot_idx in 0..3usize { @@ -102,100 +100,123 @@ impl BrewingStandBlockEntity { false } - /// Perform brewing on all valid potion slots + /// Perform the brewing process on all valid potion slots + #[expect(clippy::too_many_lines)] fn do_brew(&self, world: &Arc, ingredient: &ItemStack) { + if ingredient.is_empty() { + return; + } + let ingredient_id = ingredient.get_item().id; - // Apply recipes to each slot - for slot_idx in 0..3usize { - let Ok(items) = self.items.try_read() else { - continue; + // Brew potion slots + let mut ingredient_used = false; + { + let Ok(mut items) = self.items.write() else { + return; }; - let slot = &items[slot_idx]; - if slot.is_empty() { - continue; - } - let mut new_stack_opt: Option = None; - - // Try item recipes first (potion -> splash/lingering) - for recipe in &ITEM_RECIPES { - if slot.get_item().id == recipe.from().id - && recipe.ingredient().iter().any(|i| i.id == ingredient_id) - { - let new_item = recipe.to(); - let potion_comp = slot.get_data_component::().cloned(); - let new_stack = potion_comp.map_or_else( - || ItemStack::new(slot.item_count, new_item), - |pc| { - ItemStack::new_with_component( - slot.item_count, - new_item, - vec![( - pumpkin_data::data_component::DataComponent::PotionContents, - Some(pc.to_dyn()), - )], - ) - }, - ); - new_stack_opt = Some(new_stack); - break; + for slot_idx in 0..3usize { + let slot = &mut items[slot_idx]; + if slot.is_empty() { + continue; } - } - // Try potion recipes (modify potion type) if item recipe didn't apply - if new_stack_opt.is_none() - && let Some(pc) = slot - .get_data_component::() - && let Some(potion_id) = pc.potion_id - { - for recipe in &POTION_RECIPES { - if recipe.from().id as i32 == potion_id + // 1. Try item recipes (e.g. gunpowder -> splash potion, dragon breath -> lingering) + let mut item_brewed = false; + for recipe in &ITEM_RECIPES { + if slot.get_item().id == recipe.from().id && recipe.ingredient().iter().any(|i| i.id == ingredient_id) { - let new_pc = pumpkin_data::data_component_impl::PotionContentsImpl { - potion_id: Some(recipe.to().id as i32), - custom_color: pc.custom_color, - custom_effects: pc.custom_effects.clone(), - custom_name: pc.custom_name.clone(), - }; - let new_stack = ItemStack::new_with_component( - slot.item_count, - slot.get_item(), - vec![( - pumpkin_data::data_component::DataComponent::PotionContents, - Some(new_pc.to_dyn()), - )], - ); - new_stack_opt = Some(new_stack); + // Preserve potion contents component when converting potion type + let pc = slot.get_data_component::().cloned(); + *slot = ItemStack::new(1, recipe.to()); + if let Some(pc) = pc { + slot.set_data_component(pc); + } + item_brewed = true; + ingredient_used = true; break; } } + + if item_brewed { + continue; + } + + // 2. Try potion recipes (e.g. water bottle -> awkward potion, awkward -> strength) + if let Some(pc) = slot + .get_data_component::() + && let Some(potion_id) = pc.potion_id + { + for recipe in &POTION_RECIPES { + if recipe.from().id as i32 == potion_id + && recipe.ingredient().iter().any(|i| i.id == ingredient_id) + { + let new_potion_id = recipe.to().id as i32; + let mut new_pc = pc.clone(); + new_pc.potion_id = Some(new_potion_id); + slot.set_data_component(new_pc); + ingredient_used = true; + break; + } + } + } } - drop(items); - - // Update the slot if a recipe was applied - if let Some(new_stack) = new_stack_opt - && let Ok(mut items) = self.items.try_write() - { - items[slot_idx] = new_stack; - self.mark_dirty(); + // Consume one ingredient + if ingredient_used { + items[3].decrement(1); } } - if let Some(server) = world.server.upgrade() { - let mut event = crate::plugin::api::events::inventory::brew::BrewEvent::new( - self.position, - self.fuel.load(std::sync::atomic::Ordering::Relaxed) as u8, - ); - server.plugin_manager.fire_blocking(&server, &mut event); + if !ingredient_used { + return; } - // Consume ingredient - if let Ok(mut items) = self.items.try_write() { - items[3].decrement(1); - self.mark_dirty(); + // Fire BrewEvent + if let Some(server) = world.server.upgrade() { + let mut brew_event = crate::plugin::api::events::inventory::brew::BrewEvent::new( + self.position, + self.fuel.load(Ordering::Relaxed) as u8, + ); + server + .plugin_manager + .fire_blocking(&server, &mut brew_event); + } + + // Check if remaining ingredient matches or clear it + if let Ok(items) = self.items.read() { + let remaining = &items[3]; + if remaining.is_empty() + || !self + .ingredient_item + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some_and(|stored| remaining.get_item().id == stored.id) + { + *self + .ingredient_item + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } + } + + // Check if we can immediately start the next brew + if let Ok(items) = self.items.read() { + let ingredient = items[3].clone(); + drop(items); + if self.fuel.load(Ordering::Relaxed) > 0 && self.is_brewable(&ingredient) { + self.fuel.fetch_sub(1, Ordering::Relaxed); + self.brew_time.store(400, Ordering::Relaxed); + *self + .ingredient_item + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + Some(ingredient.get_item()); + } else { + self.brew_time.store(0, Ordering::Relaxed); + } } // Play sound at the center of the block @@ -208,9 +229,6 @@ impl BrewingStandBlockEntity { // Mark dirty to trigger update self.mark_dirty(); - - // Handle crafting remainder (like glass bottles from honey bottles) - // TODO: Implement remainder handling when item lookup by ID is available } } @@ -219,76 +237,66 @@ impl pumpkin_world::inventory::Inventory for BrewingStandBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - for slot in items.iter() { - if !slot.is_empty() { - return false; - } + fn is_empty(&self) -> bool { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for slot in items.iter() { + if !slot.is_empty() { + return false; } - true - }) + } + true } - fn get_stack(&self, slot: usize) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let items = self.items.read().await; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack( - &self, - slot: usize, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific( - &self, - slot: usize, - amount: u8, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let taken = if items[slot].item_count <= amount { - std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) - } else { - let mut taken = items[slot].clone(); - taken.item_count = amount; - items[slot].item_count -= amount; - taken - }; - self.mark_dirty(); + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let taken = if items[slot].item_count <= amount { + std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) + } else { + let mut taken = items[slot].clone(); + taken.item_count = amount; + items[slot].item_count -= amount; taken - }) + }; + self.mark_dirty(); + taken } - fn set_stack( - &self, - slot: usize, - stack: ItemStack, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } - fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move {}) - } + fn on_open(&self) {} - fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move {}) - } + fn on_close(&self) {} fn mark_dirty(&self) { self.dirty.store(true, Ordering::Relaxed); @@ -325,12 +333,13 @@ impl pumpkin_world::inventory::Inventory for BrewingStandBlockEntity { } impl pumpkin_world::inventory::Clearable for BrewingStandBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } @@ -358,7 +367,10 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { } // Load inventory items from NBT - let items = entity.items.get_mut(); + let items = entity + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner); sync_read_items_from_nbt(nbt, items); // If there's an ingredient in slot 3, remember its base item for matching @@ -399,7 +411,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed)); // Save inventory contents to NBT - self.write_inventory_nbt(nbt, true).await; + self.write_inventory_nbt(nbt, true); }) } diff --git a/crates/pumpkin/src/block/entities/chest.rs b/crates/pumpkin/src/block/entities/chest.rs index ab23e0c14..3fdae3618 100644 --- a/crates/pumpkin/src/block/entities/chest.rs +++ b/crates/pumpkin/src/block/entities/chest.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex as StdMutex, atomic::AtomicBool}; +use std::sync::{Arc, Mutex as StdMutex, RwLock, atomic::AtomicBool}; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::item_stack::ItemStack; @@ -11,7 +11,7 @@ use crate::{ pub struct ChestBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, // Viewer diff --git a/crates/pumpkin/src/block/entities/chest_like_block_entity.rs b/crates/pumpkin/src/block/entities/chest_like_block_entity.rs index 42885ff87..536a63686 100644 --- a/crates/pumpkin/src/block/entities/chest_like_block_entity.rs +++ b/crates/pumpkin/src/block/entities/chest_like_block_entity.rs @@ -24,7 +24,7 @@ macro_rules! impl_block_entity_for_chest { let mut chest = Self { position, - items: tokio::sync::RwLock::new(std::array::from_fn(|_| { + items: std::sync::RwLock::new(std::array::from_fn(|_| { ItemStack::EMPTY.clone() })), dirty: std::sync::atomic::AtomicBool::new(false), @@ -40,7 +40,13 @@ macro_rules! impl_block_entity_for_chest { .unwrap_or_else(std::sync::PoisonError::into_inner) .is_some(); if !has_loot_table { - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, chest.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + chest + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); } chest @@ -70,7 +76,7 @@ macro_rules! impl_block_entity_for_chest { } } else { // Loot has already been generated, so persist the actual items. - self.write_inventory_nbt(nbt, true).await; + self.write_inventory_nbt(nbt, true); } }) } @@ -145,74 +151,61 @@ macro_rules! impl_inventory_for_chest { Self::INVENTORY_SIZE } - fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(|s| s.is_empty()) - }) + fn is_empty(&self) -> bool { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.iter().all(|s| s.is_empty()) } - fn get_stack( - &self, - slot: usize, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let items = self.items.read().await; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack( - &self, - slot: usize, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific( - &self, - slot: usize, - amount: u8, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack( - &self, - slot: usize, - stack: ItemStack, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } - fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.open_container(); - }) + fn on_open(&self) { + self.viewers.open_container(); } - fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.close_container(); - }) + fn on_close(&self) { + self.viewers.close_container(); } fn mark_dirty(&self) { @@ -231,14 +224,13 @@ macro_rules! impl_inventory_for_chest { macro_rules! impl_clearable_for_chest { ($struct_name:ty) => { impl pumpkin_world::inventory::Clearable for $struct_name { - fn clear( - &self, - ) -> std::pin::Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - <$struct_name as pumpkin_world::inventory::Inventory>::mark_dirty(self); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + <$struct_name as pumpkin_world::inventory::Inventory>::mark_dirty(self); } } }; @@ -311,11 +303,12 @@ macro_rules! impl_chest_helper_methods { pub fn new(position: pumpkin_util::math::position::BlockPos) -> Self { use std::array::from_fn; use std::sync::Mutex as StdMutex; + use std::sync::RwLock; use std::sync::atomic::AtomicBool; Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), viewers: $crate::block::viewer::ViewerCountTracker::new(), loot_table: StdMutex::new(None), diff --git a/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs b/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs index a822b1eee..d804b449c 100644 --- a/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs +++ b/crates/pumpkin/src/block/entities/chiseled_bookshelf.rs @@ -6,6 +6,7 @@ use pumpkin_util::math::position::BlockPos; use std::any::Any; use std::future::Future; use std::pin::Pin; +use std::sync::RwLock; use std::{ array::from_fn, sync::{ @@ -19,12 +20,11 @@ use crate::{ block::entities::BlockEntity, world::{BlockFlags, World}, }; -use pumpkin_world::inventory::InventoryFuture; use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; pub struct ChiseledBookshelfBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub last_interacted_slot: AtomicI8, pub dirty: AtomicBool, } @@ -46,11 +46,17 @@ impl BlockEntity for ChiseledBookshelfBlockEntity { { let mut bookshelf = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), last_interacted_slot: AtomicI8::new(-1), dirty: AtomicBool::new(false), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, bookshelf.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + bookshelf + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); if let Some(slot) = nbt.get_int(LAST_INTERACTED_SLOT) { bookshelf .last_interacted_slot @@ -65,7 +71,10 @@ impl BlockEntity for ChiseledBookshelfBlockEntity { nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { - let items = self.items.read().await; + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); sync_write_items_to_nbt(items.as_slice(), nbt); nbt.put_int( LAST_INTERACTED_SLOT, @@ -99,7 +108,7 @@ impl ChiseledBookshelfBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), last_interacted_slot: AtomicI8::new(-1), dirty: AtomicBool::new(false), } @@ -116,7 +125,11 @@ impl ChiseledBookshelfBlockEntity { .store(slot as i8, Ordering::Relaxed); self.mark_dirty(); - let occupied = !self.items.blocking_read()[slot].is_empty(); + let occupied = !self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner)[slot] + .is_empty(); match slot { 0 => properties.slot_0_occupied = occupied, 1 => properties.slot_1_occupied = occupied, @@ -141,13 +154,19 @@ impl ChiseledBookshelfBlockEntity { } pub fn set_book(&self, slot: usize, stack: ItemStack) { - let mut items = self.items.blocking_write(); + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); items[slot] = stack; self.mark_dirty(); } pub fn remove_book(&self, slot: usize, amount: u8) -> ItemStack { - let mut items = self.items.blocking_write(); + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); let res = if !items[slot].is_empty() && amount > 0 { items[slot].split(amount) } else { @@ -163,48 +182,53 @@ impl Inventory for ChiseledBookshelfBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -217,11 +241,12 @@ impl Inventory for ChiseledBookshelfBlockEntity { } impl Clearable for ChiseledBookshelfBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/crafter.rs b/crates/pumpkin/src/block/entities/crafter.rs index fcb1b28c0..68538c748 100644 --- a/crates/pumpkin/src/block/entities/crafter.rs +++ b/crates/pumpkin/src/block/entities/crafter.rs @@ -2,16 +2,17 @@ use crate::block::entities::BlockEntity; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; pub struct CrafterBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub crafting_ticks_remaining: AtomicI32, pub triggered: AtomicBool, pub dirty: AtomicBool, @@ -23,7 +24,10 @@ impl BlockEntity for CrafterBlockEntity { nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { - let items = self.items.read().await; + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); sync_write_items_to_nbt(items.as_slice(), nbt); nbt.put_int( "crafting_ticks_remaining", @@ -39,7 +43,7 @@ impl BlockEntity for CrafterBlockEntity { { let crafter = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), crafting_ticks_remaining: AtomicI32::new( nbt.get_int("crafting_ticks_remaining").unwrap_or(0), ), @@ -97,7 +101,7 @@ impl CrafterBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), crafting_ticks_remaining: AtomicI32::new(0), triggered: AtomicBool::new(false), dirty: AtomicBool::new(false), @@ -110,48 +114,53 @@ impl Inventory for CrafterBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -164,11 +173,12 @@ impl Inventory for CrafterBlockEntity { } impl Clearable for CrafterBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/dispenser.rs b/crates/pumpkin/src/block/entities/dispenser.rs index 7bcf9f1fa..89109e9b2 100644 --- a/crates/pumpkin/src/block/entities/dispenser.rs +++ b/crates/pumpkin/src/block/entities/dispenser.rs @@ -2,17 +2,18 @@ use crate::block::entities::BlockEntity; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use rand::{RngExt, rng}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; pub struct DispenserBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, } @@ -21,7 +22,9 @@ impl BlockEntity for DispenserBlockEntity { &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { - self.write_inventory_nbt(nbt, true) + Box::pin(async move { + self.write_inventory_nbt(nbt, true); + }) } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self @@ -30,11 +33,17 @@ impl BlockEntity for DispenserBlockEntity { { let mut dispenser = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, dispenser.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + dispenser + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); dispenser } @@ -80,13 +89,16 @@ impl DispenserBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), } } - pub async fn get_random_slot(&self) -> Option<(usize, ItemStack)> { - let items = self.items.read().await; + pub fn get_random_slot(&self) -> Option<(usize, ItemStack)> { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut non_empty = Vec::new(); for (i, stack) in items.iter().enumerate() { if !stack.is_empty() { @@ -107,48 +119,53 @@ impl Inventory for DispenserBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -161,11 +178,12 @@ impl Inventory for DispenserBlockEntity { } impl Clearable for DispenserBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/dropper.rs b/crates/pumpkin/src/block/entities/dropper.rs index d978dfc7d..caf816b58 100644 --- a/crates/pumpkin/src/block/entities/dropper.rs +++ b/crates/pumpkin/src/block/entities/dropper.rs @@ -2,17 +2,18 @@ use crate::block::entities::BlockEntity; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use rand::{RngExt, rng}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; pub struct DropperBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, } @@ -21,7 +22,9 @@ impl BlockEntity for DropperBlockEntity { &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { - self.write_inventory_nbt(nbt, true) + Box::pin(async move { + self.write_inventory_nbt(nbt, true); + }) } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self @@ -30,11 +33,17 @@ impl BlockEntity for DropperBlockEntity { { let mut dropper = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, dropper.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + dropper + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); dropper } @@ -80,13 +89,16 @@ impl DropperBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), } } - pub async fn get_random_slot(&self) -> Option<(usize, ItemStack)> { - let items = self.items.read().await; + pub fn get_random_slot(&self) -> Option<(usize, ItemStack)> { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut non_empty = Vec::new(); for (i, stack) in items.iter().enumerate() { if !stack.is_empty() { @@ -107,48 +119,53 @@ impl Inventory for DropperBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -161,11 +178,12 @@ impl Inventory for DropperBlockEntity { } impl Clearable for DropperBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/furnace.rs b/crates/pumpkin/src/block/entities/furnace.rs index 436211416..9eaa6ecad 100644 --- a/crates/pumpkin/src/block/entities/furnace.rs +++ b/crates/pumpkin/src/block/entities/furnace.rs @@ -5,7 +5,7 @@ use std::{ array::from_fn, collections::HashMap, sync::{ - Arc, Mutex as StdMutex, + Arc, Mutex as StdMutex, RwLock, atomic::{AtomicBool, AtomicU16, Ordering}, }, }; @@ -29,7 +29,7 @@ pub struct FurnaceBlockEntity { pub lit_time_remaining: AtomicU16, pub lit_total_time: AtomicU16, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, /// Tracks recipes used for XP calculation (vanilla `RecipesUsed` NBT format) /// Maps result item ID -> craft count @@ -45,7 +45,7 @@ impl FurnaceBlockEntity { Self { position, dirty: AtomicBool::new(false), - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), cooking_total_time: AtomicU16::new(0), cooking_time_spent: AtomicU16::new(0), lit_total_time: AtomicU16::new(0), diff --git a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs index b6632fbc4..680c19528 100644 --- a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs +++ b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs @@ -20,9 +20,9 @@ pub trait CookingBlockEntityBase: /// Calculates XP from tracked recipes and clears the `recipes_used` map fn extract_experience_from_recipes(&self) -> i32; - fn get_input_item(&self) -> impl std::future::Future; - fn get_fuel_item(&self) -> impl std::future::Future; - fn get_output_item(&self) -> impl std::future::Future; + fn get_input_item(&self) -> ItemStack; + fn get_fuel_item(&self) -> ItemStack; + fn get_output_item(&self) -> ItemStack; fn set_cooking_time_spent(&self, spent_time: u16); fn set_cooking_total_time(&self, total_time: u16); @@ -54,25 +54,28 @@ macro_rules! impl_cooking_block_entity_base { self.lit_total_time.load(Ordering::Relaxed) } - fn get_input_item(&self) -> impl std::future::Future { - async move { - let items = self.items.read().await; - items[0].clone() - } + fn get_input_item(&self) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[0].clone() } - fn get_fuel_item(&self) -> impl std::future::Future { - async move { - let items = self.items.read().await; - items[1].clone() - } + fn get_fuel_item(&self) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[1].clone() } - fn get_output_item(&self) -> impl std::future::Future { - async move { - let items = self.items.read().await; - items[2].clone() - } + fn get_output_item(&self) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[2].clone() } fn set_cooking_time_spent(&self, spent_time: u16) { @@ -237,12 +240,13 @@ macro_rules! impl_property_delegate_for_cooking { macro_rules! impl_clearable_for_cooking { ($struct_name:ty) => { impl pumpkin_world::inventory::Clearable for $struct_name { - fn clear(&self) -> std::pin::Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } }; @@ -270,81 +274,70 @@ macro_rules! impl_inventory_for_cooking { Self::INVENTORY_SIZE } - fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(|s| s.is_empty()) - }) + fn is_empty(&self) -> bool { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.iter().all(|s| s.is_empty()) } - fn get_stack( - &self, - slot: usize, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let items = self.items.read().await; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack( - &self, - slot: usize, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific( - &self, - slot: usize, - amount: u8, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res + } + + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let is_same_item = !stack.is_empty() + && ItemStack::are_items_and_components_equal(&items[slot], &stack); + + items[slot] = stack.clone(); + + if slot == 0 && !is_same_item { + if let Some(recipe) = pumpkin_data::recipes::get_cooking_recipe_with_ingredient( + stack.item, + CookingRecipeKind::Smelting, + ) { + self.set_cooking_total_time(recipe.cookingtime as u16); } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) - } - - fn set_stack( - &self, - slot: usize, - stack: ItemStack, - ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - let is_same_item = !stack.is_empty() - && ItemStack::are_items_and_components_equal(&items[slot], &stack); - - items[slot] = stack.clone(); - - if slot == 0 && !is_same_item { - if let Some(recipe) = - pumpkin_data::recipes::get_cooking_recipe_with_ingredient( - stack.item, - CookingRecipeKind::Smelting, - ) - { - self.set_cooking_total_time(recipe.cookingtime as u16); - } else { - self.set_cooking_total_time(0); - } - self.set_cooking_time_spent(0); + self.set_cooking_total_time(0); } + self.set_cooking_time_spent(0); + } - // Always consider the inventory changed when setting a stack - self.mark_dirty(); - }) + // Always consider the inventory changed when setting a stack + self.mark_dirty(); } fn mark_dirty(&self) { @@ -485,7 +478,6 @@ macro_rules! impl_block_entity_for_cooking { if is_burning != self.is_burning() { is_dirty = true; - let (furnace_block, furnace_block_state) = world.get_block_and_state(&self.position); let mut props = @@ -548,14 +540,14 @@ macro_rules! impl_block_entity_for_cooking { let mut furnace = Self { position, dirty: AtomicBool::new(false), - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: std::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), cooking_total_time, cooking_time_spent, lit_total_time, lit_time_remaining, recipes_used: std::sync::Mutex::new(recipes_used_map), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, furnace.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt(nbt, furnace.items.get_mut().unwrap_or_else(std::sync::PoisonError::into_inner)); furnace } @@ -571,7 +563,6 @@ macro_rules! impl_block_entity_for_cooking { nbt.put_short("lit_time_remaining", self.get_lit_time_remaining() as i16); // Save RecipesUsed in vanilla format (map of recipe ID -> craft count) - // Scope the mutex guard so it's dropped before the await { let recipes = self .recipes_used @@ -592,7 +583,7 @@ macro_rules! impl_block_entity_for_cooking { } } - self.write_inventory_nbt(nbt, true).await; + self.write_inventory_nbt(nbt, true); }) } diff --git a/crates/pumpkin/src/block/entities/hopper.rs b/crates/pumpkin/src/block/entities/hopper.rs index dd19426f6..0ea37cd6b 100644 --- a/crates/pumpkin/src/block/entities/hopper.rs +++ b/crates/pumpkin/src/block/entities/hopper.rs @@ -10,17 +10,18 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::Ordering; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64}; pub struct HopperBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, pub facing: FacingHopper, pub cooldown_time: AtomicI32, @@ -49,7 +50,7 @@ impl BlockEntity for HopperBlockEntity { "TransferCooldown", NbtTag::Int(self.cooldown_time.load(Ordering::Relaxed)), ); - self.write_inventory_nbt(nbt, true).await; + self.write_inventory_nbt(nbt, true); }) } @@ -59,14 +60,20 @@ impl BlockEntity for HopperBlockEntity { { let mut hopper = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), facing: FacingHopper::Down, cooldown_time: AtomicI32::from(nbt.get_int("TransferCooldown").unwrap_or(-1)), ticked_game_time: AtomicI64::new(0), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, hopper.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + hopper + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); hopper } @@ -143,7 +150,7 @@ impl HopperBlockEntity { pub fn new(position: BlockPos, facing: FacingHopper) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), facing, cooldown_time: AtomicI32::new(-1), @@ -152,12 +159,12 @@ impl HopperBlockEntity { } async fn try_move_items(&self, state: &HopperLikeProperties, world: &Arc) { if self.cooldown_time.load(Ordering::Relaxed) <= 0 && state.enabled { - let mut success = if self.is_empty().await { + let mut success = if self.is_empty() { false } else { self.eject_items(world).await }; - if !self.inventory_full().await { + if !self.inventory_full() { success |= self.suck_in_items(world).await; } if success { @@ -167,8 +174,11 @@ impl HopperBlockEntity { } } - async fn inventory_full(&self) -> bool { - let items = self.items.read().await; + fn inventory_full(&self) -> bool { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); for item in items.iter() { if item.is_empty() || item.item_count != item.get_max_stack_size() { return false; @@ -196,13 +206,13 @@ impl HopperBlockEntity { { // TODO check WorldlyContainer for i in 0..container.size() { - let mut item = container.get_stack(i).await; + let mut item = container.get_stack(i); if !item.is_empty() && container.can_transfer_to(self, i, &item) { //TODO WorldlyContainer let _backup = item.clone(); let one_item = item.split(1); - if Self::add_one_item(container.as_ref(), self, one_item).await { - container.set_stack(i, item).await; + if Self::add_one_item(container.as_ref(), self, &one_item) { + container.set_stack(i, item); // If extracting from furnace output slot (index 2), drop XP as orbs let furnace_output_slot: usize = 2; if i == furnace_output_slot @@ -264,7 +274,7 @@ impl HopperBlockEntity { let is_empty = stack.is_empty(); (backup, one_item, is_empty) }; - if Self::add_one_item(self, self, one_item).await { + if Self::add_one_item(self, self, &one_item) { if is_empty { item_entity.get_entity().remove(); } @@ -291,7 +301,7 @@ impl HopperBlockEntity { // TODO check WorldlyContainer let mut is_full = true; for i in 0..container.size() { - let item = container.get_stack(i).await; + let item = container.get_stack(i); if item.item_count < item.get_max_stack_size() { is_full = false; break; @@ -301,8 +311,12 @@ impl HopperBlockEntity { return false; } let target_pos = self.position.offset(to_offset(&self.facing)); - let items = self.items.read().await; - for item in items.iter() { + let items: [ItemStack; 5] = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + for item in &items { if !item.is_empty() { let mut move_event = crate::plugin::api::events::inventory::inventory_move_item::InventoryMoveItemEvent::new( self.position, @@ -318,7 +332,7 @@ impl HopperBlockEntity { } let mut item_clone = item.clone(); let one_item = item_clone.split(1); - if Self::add_one_item(self, container.as_ref(), one_item).await { + if Self::add_one_item(self, container.as_ref(), &one_item) { return true; } } @@ -326,20 +340,20 @@ impl HopperBlockEntity { } false } - pub async fn add_one_item(from: &dyn Inventory, to: &dyn Inventory, item: ItemStack) -> bool { + pub fn add_one_item(from: &dyn Inventory, to: &dyn Inventory, item: &ItemStack) -> bool { let mut success = false; - let to_empty = to.is_empty().await; + let to_empty = to.is_empty(); for j in 0..to.size() { - if to.is_valid_slot_for(j, &item) { - let mut dst = to.get_stack(j).await; + if to.is_valid_slot_for(j, item) { + let mut dst = to.get_stack(j); if dst.is_empty() { dst = item.clone(); - to.set_stack(j, dst).await; + to.set_stack(j, dst); success = true; } else if dst.item_count < dst.get_max_stack_size() && dst.item == item.item { // TODO check Components equal dst.item_count += 1; - to.set_stack(j, dst).await; + to.set_stack(j, dst); success = true; } if success { @@ -373,48 +387,53 @@ impl Inventory for HopperBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -427,11 +446,12 @@ impl Inventory for HopperBlockEntity { } impl Clearable for HopperBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/jukebox.rs b/crates/pumpkin/src/block/entities/jukebox.rs index 93430b9a5..881b63826 100644 --- a/crates/pumpkin/src/block/entities/jukebox.rs +++ b/crates/pumpkin/src/block/entities/jukebox.rs @@ -1,18 +1,17 @@ use std::any::Any; use std::future::Future; use std::pin::Pin; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; use crate::block::entities::BlockEntity; use crate::world::World; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; /// Matches vanilla's `JukeboxBlockEntity` pub struct JukeboxBlockEntity { @@ -64,7 +63,10 @@ impl BlockEntity for JukeboxBlockEntity { nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { - let record = self.record_stack.lock().await; + let record = self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if !record.is_empty() { let mut record_nbt = NbtCompound::new(); record.write_item_stack(&mut record_nbt); @@ -139,20 +141,29 @@ impl JukeboxBlockEntity { /// Get the current record stack pub fn get_record(&self) -> ItemStack { - self.record_stack.blocking_lock().clone() + self.record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } /// Set the record stack - matches vanilla's `setStack()` /// Note: The caller is responsible for updating block state and playing music pub fn set_record(&self, stack: ItemStack) { - *self.record_stack.blocking_lock() = stack; + *self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; self.mark_dirty(); } /// Clear the stack and return what was there - used for dropping pub fn clear_record(&self) -> ItemStack { self.stop_playing(); - let mut record = self.record_stack.blocking_lock(); + let mut record = self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let taken = record.clone(); *record = ItemStack::EMPTY.clone(); self.mark_dirty(); @@ -195,35 +206,43 @@ impl Inventory for JukeboxBlockEntity { 1 } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { self.record_stack.lock().await.is_empty() }) + fn is_empty(&self) -> bool { + self.record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() } - fn get_stack(&self, _slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { self.record_stack.lock().await.clone() }) + fn get_stack(&self, _slot: usize) -> ItemStack { + self.record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - fn remove_stack(&self, _slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - self.stop_playing(); - let mut record = self.record_stack.lock().await; - let taken = record.clone(); - *record = ItemStack::EMPTY.clone(); - self.mark_dirty(); - taken - }) + fn remove_stack(&self, _slot: usize) -> ItemStack { + self.stop_playing(); + let mut record = self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let taken = record.clone(); + *record = ItemStack::EMPTY.clone(); + self.mark_dirty(); + taken } - fn remove_stack_specific(&self, _slot: usize, _amount: u8) -> InventoryFuture<'_, ItemStack> { + fn remove_stack_specific(&self, _slot: usize, _amount: u8) -> ItemStack { // Jukebox only holds one item, so remove the whole stack self.remove_stack(0) } - fn set_stack(&self, _slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - *self.record_stack.lock().await = stack; - self.mark_dirty(); - }) + fn set_stack(&self, _slot: usize, stack: ItemStack) { + *self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -236,11 +255,12 @@ impl Inventory for JukeboxBlockEntity { } impl Clearable for JukeboxBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - self.stop_playing(); - *self.record_stack.lock().await = ItemStack::EMPTY.clone(); - self.mark_dirty(); - }) + fn clear(&self) { + self.stop_playing(); + *self + .record_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = ItemStack::EMPTY.clone(); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/lectern.rs b/crates/pumpkin/src/block/entities/lectern.rs index fd9f12a24..83fb204ac 100644 --- a/crates/pumpkin/src/block/entities/lectern.rs +++ b/crates/pumpkin/src/block/entities/lectern.rs @@ -8,14 +8,13 @@ use std::{ future::Future, pin::Pin, sync::{ - Arc, + Arc, Mutex, atomic::{AtomicBool, AtomicUsize, Ordering}, }, }; -use tokio::sync::Mutex; use crate::block::entities::BlockEntity; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; pub struct LecternBlockEntity { pub position: BlockPos, @@ -62,7 +61,10 @@ impl BlockEntity for LecternBlockEntity { nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { - let book = self.book.lock().await; + let book = self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if !book.is_empty() { let mut book_nbt = NbtCompound::default(); book.write_item_stack(&mut book_nbt); @@ -129,15 +131,23 @@ impl LecternBlockEntity { .map_or(0, |pages| pages as i32) } - pub async fn page_count(&self) -> i32 { - Self::page_count_of(&*self.book.lock().await) + pub fn page_count(&self) -> i32 { + Self::page_count_of( + &self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ) } /// Vanilla comparator output: `floor(page / (page_count - 1) * 14) + 1`, /// or `0` without a book. Single-page books emit `1` (`0 / 0` is `NaN`, /// which vanilla's `MathHelper.floor` turns into `0`). - pub async fn comparator_output(&self) -> u8 { - let book = self.book.lock().await; + pub fn comparator_output(&self) -> u8 { + let book = self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if book.is_empty() { return 0; } @@ -155,43 +165,52 @@ impl Inventory for LecternBlockEntity { 1 } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { self.book.lock().await.is_empty() }) + fn is_empty(&self) -> bool { + self.book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() } - fn get_stack(&self, _slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { self.book.lock().await.clone() }) + fn get_stack(&self, _slot: usize) -> ItemStack { + self.book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } - fn remove_stack(&self, _slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut removed = ItemStack::EMPTY.clone(); - let mut guard = self.book.lock().await; - std::mem::swap(&mut removed, &mut *guard); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, _slot: usize) -> ItemStack { + let mut removed = ItemStack::EMPTY.clone(); + let mut guard = self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::swap(&mut removed, &mut *guard); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, _slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut stack = self.book.lock().await; - if stack.is_empty() { - return ItemStack::EMPTY.clone(); - } - let res = stack.split(amount); - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, _slot: usize, amount: u8) -> ItemStack { + let mut stack = self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if stack.is_empty() { + return ItemStack::EMPTY.clone(); + } + let res = stack.split(amount); + self.mark_dirty(); + res } - fn set_stack(&self, _slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - *self.book.lock().await = stack; - // A freshly placed book always opens on its first page. - self.page.store(0, Ordering::Relaxed); - self.mark_dirty(); - }) + fn set_stack(&self, _slot: usize, stack: ItemStack) { + *self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = stack; + // A freshly placed book always opens on its first page. + self.page.store(0, Ordering::Relaxed); + self.mark_dirty(); } fn mark_dirty(&self) { @@ -204,10 +223,11 @@ impl Inventory for LecternBlockEntity { } impl Clearable for LecternBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - *self.book.lock().await = ItemStack::EMPTY.clone(); - self.mark_dirty(); - }) + fn clear(&self) { + *self + .book + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = ItemStack::EMPTY.clone(); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/mod.rs b/crates/pumpkin/src/block/entities/mod.rs index 00e2409aa..ef0832913 100644 --- a/crates/pumpkin/src/block/entities/mod.rs +++ b/crates/pumpkin/src/block/entities/mod.rs @@ -144,8 +144,7 @@ pub trait BlockEntity: Any + Send + Sync { { Box::pin(async move { if let Some(inventory) = self.get_inventory() { - // Assuming scatter_inventory is an async method on World - world.scatter_inventory(&position, &inventory).await; + world.scatter_inventory(&position, &inventory); } }) } @@ -460,9 +459,7 @@ mod test { async fn furnace_contents_survive_a_chunk_round_trip() { let position = BlockPos::new(0, 100, 0); let furnace = Arc::new(FurnaceBlockEntity::new(position)); - furnace - .set_stack(0, ItemStack::new(5, &Item::DIAMOND)) - .await; + furnace.set_stack(0, ItemStack::new(5, &Item::DIAMOND)); let mut nbt = NbtCompound::new(); furnace.write_internal(&mut nbt).await; @@ -474,7 +471,7 @@ mod test { ); if let Some(inventory) = inventory { - let stack = inventory.get_stack(0).await; + let stack = inventory.get_stack(0); assert_eq!(stack.get_item().id, Item::DIAMOND.id); assert_eq!(stack.item_count, 5); } diff --git a/crates/pumpkin/src/block/entities/shelf.rs b/crates/pumpkin/src/block/entities/shelf.rs index d8c26fb81..8e296d092 100644 --- a/crates/pumpkin/src/block/entities/shelf.rs +++ b/crates/pumpkin/src/block/entities/shelf.rs @@ -2,16 +2,17 @@ use crate::block::entities::BlockEntity; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; pub struct ShelfBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, } @@ -20,7 +21,9 @@ impl BlockEntity for ShelfBlockEntity { &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { - self.write_inventory_nbt(nbt, true) + Box::pin(async move { + self.write_inventory_nbt(nbt, true); + }) } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self @@ -29,11 +32,17 @@ impl BlockEntity for ShelfBlockEntity { { let mut shelf = Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, shelf.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + shelf + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); shelf } @@ -79,7 +88,7 @@ impl ShelfBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), } } @@ -90,48 +99,53 @@ impl Inventory for ShelfBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } fn mark_dirty(&self) { @@ -144,11 +158,12 @@ impl Inventory for ShelfBlockEntity { } impl Clearable for ShelfBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/shulker_box.rs b/crates/pumpkin/src/block/entities/shulker_box.rs index de5ef8306..48ff590c4 100644 --- a/crates/pumpkin/src/block/entities/shulker_box.rs +++ b/crates/pumpkin/src/block/entities/shulker_box.rs @@ -5,14 +5,14 @@ use pumpkin_util::math::position::BlockPos; use std::any::Any; use std::future::Future; use std::pin::Pin; +use std::sync::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; use std::{array::from_fn, sync::Arc}; -use tokio::sync::RwLock; use crate::block::entities::BlockEntity; use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt}; use crate::world::World; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, sync_write_items_to_nbt}; +use pumpkin_world::inventory::{Clearable, Inventory, sync_write_items_to_nbt}; pub struct ShulkerBoxBlockEntity { pub position: BlockPos, @@ -43,7 +43,13 @@ impl BlockEntity for ShulkerBoxBlockEntity { viewers: ViewerCountTracker::new(), }; - pumpkin_world::inventory::sync_read_items_from_nbt(nbt, shulker_box.items.get_mut()); + pumpkin_world::inventory::sync_read_items_from_nbt( + nbt, + shulker_box + .items + .get_mut() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); shulker_box } @@ -52,7 +58,9 @@ impl BlockEntity for ShulkerBoxBlockEntity { &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { - self.write_inventory_nbt(nbt, true) + Box::pin(async move { + self.write_inventory_nbt(nbt, true); + }) } fn tick(&self, world: &Arc) { @@ -150,60 +158,61 @@ impl Inventory for ShulkerBoxBlockEntity { Self::INVENTORY_SIZE } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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; - items[slot].clone() - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let items = self + .items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot].clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); - self.mark_dirty(); - removed - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let removed = std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()); + self.mark_dirty(); + removed } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - let res = if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - }; - self.mark_dirty(); - res - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let res = if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + }; + self.mark_dirty(); + res } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut items = self.items.write().await; - items[slot] = stack; - self.mark_dirty(); - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items[slot] = stack; + self.mark_dirty(); } - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.open_container(); - }) + fn on_open(&self) { + self.viewers.open_container(); } - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.viewers.close_container(); - }) + fn on_close(&self) { + self.viewers.close_container(); } fn mark_dirty(&self) { @@ -216,11 +225,12 @@ impl Inventory for ShulkerBoxBlockEntity { } impl Clearable for ShulkerBoxBlockEntity { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - let mut items = self.items.write().await; - items.fill_with(|| ItemStack::EMPTY.clone()); - self.mark_dirty(); - }) + fn clear(&self) { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.fill_with(|| ItemStack::EMPTY.clone()); + self.mark_dirty(); } } diff --git a/crates/pumpkin/src/block/entities/smoker.rs b/crates/pumpkin/src/block/entities/smoker.rs index bf5075314..382c1e58b 100644 --- a/crates/pumpkin/src/block/entities/smoker.rs +++ b/crates/pumpkin/src/block/entities/smoker.rs @@ -5,7 +5,7 @@ use std::{ array::from_fn, collections::HashMap, sync::{ - Arc, Mutex as StdMutex, + Arc, Mutex as StdMutex, RwLock, atomic::{AtomicBool, AtomicU16, Ordering}, }, }; @@ -29,7 +29,7 @@ pub struct SmokerBlockEntity { pub lit_time_remaining: AtomicU16, pub lit_total_time: AtomicU16, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, /// Tracks recipes used for XP calculation (vanilla `RecipesUsed` NBT format) /// Maps result item ID -> craft count @@ -45,7 +45,7 @@ impl SmokerBlockEntity { Self { position, dirty: AtomicBool::new(false), - items: tokio::sync::RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), + items: RwLock::new(from_fn(|_| ItemStack::EMPTY.clone())), cooking_total_time: AtomicU16::new(0), cooking_time_spent: AtomicU16::new(0), lit_total_time: AtomicU16::new(0), diff --git a/crates/pumpkin/src/block/entities/trapped_chest.rs b/crates/pumpkin/src/block/entities/trapped_chest.rs index 6a8ff3b5d..e584e7c63 100644 --- a/crates/pumpkin/src/block/entities/trapped_chest.rs +++ b/crates/pumpkin/src/block/entities/trapped_chest.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex as StdMutex, atomic::AtomicBool}; +use std::sync::{Arc, Mutex as StdMutex, RwLock, atomic::AtomicBool}; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::item_stack::ItemStack; @@ -11,7 +11,7 @@ use crate::{ pub struct TrappedChestBlockEntity { pub position: BlockPos, - pub items: tokio::sync::RwLock<[ItemStack; Self::INVENTORY_SIZE]>, + pub items: RwLock<[ItemStack; Self::INVENTORY_SIZE]>, pub dirty: AtomicBool, // Viewer diff --git a/crates/pumpkin/src/block/mod.rs b/crates/pumpkin/src/block/mod.rs index b291e837e..ce42c8ff0 100644 --- a/crates/pumpkin/src/block/mod.rs +++ b/crates/pumpkin/src/block/mod.rs @@ -493,7 +493,7 @@ pub fn calculate_comparator_output(inventory: &dyn pumpkin_world::inventory::Inv let mut fill_sum = 0.0; let mut non_empty_count = 0; for i in 0..size { - let stack = futures::executor::block_on(inventory.get_stack(i)); + let stack = inventory.get_stack(i); if !stack.is_empty() { let max_stack = stack.get_max_stack_size() as f32; let count = stack.item_count as f32; diff --git a/crates/pumpkin/src/command/commands/item.rs b/crates/pumpkin/src/command/commands/item.rs index 492b6dba5..ebd212244 100644 --- a/crates/pumpkin/src/command/commands/item.rs +++ b/crates/pumpkin/src/command/commands/item.rs @@ -98,7 +98,7 @@ impl CommandExecutor for BlockReplaceExecutor { let mut item_stack = parsed_stack.clone(); item_stack.item_count = count as u8; - futures::executor::block_on(inventory.set_stack(slot, item_stack)); + inventory.set_stack(slot, item_stack); let msg = TextComponent::translate_cross( translation::java::COMMANDS_ITEM_BLOCK_SET_SUCCESS, @@ -151,11 +151,9 @@ impl CommandExecutor for EntityReplaceExecutor { if (200..=226).contains(&mojang_slot) { let ender_slot = mojang_slot - 200; if ender_slot < player.ender_chest_inventory.size() { - futures::executor::block_on( - player_arc - .ender_chest_inventory - .set_stack(ender_slot, item_stack.clone()), - ); + player_arc + .ender_chest_inventory + .set_stack(ender_slot, item_stack.clone()); modified_count += 1; } } else { @@ -181,16 +179,12 @@ impl CommandExecutor for EntityReplaceExecutor { if let Some(slot) = mapped_slot && slot < inventory.size() { - futures::executor::block_on( - player_arc.inventory().set_stack(slot, item_stack.clone()), - ); + player_arc.inventory().set_stack(slot, item_stack.clone()); let stack_serializer = ItemStackSerializer::from(item_stack.clone()); let packet = CSetContainerSlot::new(0, 0, slot as i16, &stack_serializer); - futures::executor::block_on( - player_arc.enqueue_slot_packet(&packet, None, 0), - ); + player_arc.enqueue_slot_packet(&packet, None, 0); let eq_slot = if slot == 36 { Some(EquipmentSlot::FEET) diff --git a/crates/pumpkin/src/command/commands/loot.rs b/crates/pumpkin/src/command/commands/loot.rs index ac2c54f71..e0dfc3ffc 100644 --- a/crates/pumpkin/src/command/commands/loot.rs +++ b/crates/pumpkin/src/command/commands/loot.rs @@ -50,7 +50,7 @@ struct LootExecutor { source: Source, } -async fn insert_into_inventory( +fn insert_into_inventory( inventory: &dyn pumpkin_world::inventory::Inventory, mut stack: pumpkin_data::item_stack::ItemStack, ) -> pumpkin_data::item_stack::ItemStack { @@ -58,7 +58,7 @@ async fn insert_into_inventory( if stack.is_empty() { break; } - let mut slot_stack = inventory.get_stack(i).await; + let mut slot_stack = inventory.get_stack(i); if !slot_stack.is_empty() && slot_stack.get_item().id == stack.get_item().id { let max_stack_size = 64; let space = max_stack_size - slot_stack.item_count; @@ -66,7 +66,7 @@ async fn insert_into_inventory( let to_add = stack.item_count.min(space); slot_stack.item_count += to_add; stack.item_count -= to_add; - inventory.set_stack(i, slot_stack).await; + inventory.set_stack(i, slot_stack); } } } @@ -75,9 +75,9 @@ async fn insert_into_inventory( if stack.is_empty() { break; } - let slot_stack = inventory.get_stack(i).await; + let slot_stack = inventory.get_stack(i); if slot_stack.is_empty() { - inventory.set_stack(i, stack.clone()).await; + inventory.set_stack(i, stack.clone()); stack.item_count = 0; break; } @@ -194,10 +194,7 @@ impl CommandExecutor for LootExecutor { }; for stack in stacks { - let remaining = futures::executor::block_on(insert_into_inventory( - inventory.as_ref(), - stack, - )); + let remaining = insert_into_inventory(inventory.as_ref(), stack); if !remaining.is_empty() { world.drop_stack(&pos, remaining); } diff --git a/crates/pumpkin/src/data/player_server.rs b/crates/pumpkin/src/data/player_server.rs index 20cf5dadf..70aea5837 100644 --- a/crates/pumpkin/src/data/player_server.rs +++ b/crates/pumpkin/src/data/player_server.rs @@ -48,10 +48,9 @@ impl ServerPlayerData { player .player_screen_handler .lock() - .await - .on_closed(player.as_ref()) - .await; - player.on_handled_screen_closed().await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .on_closed(player.as_ref()); + player.on_handled_screen_closed(); let mut nbt = NbtCompound::new(); player.write_nbt(&mut nbt); diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index 9a8fcc624..a7df8b122 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -567,17 +567,13 @@ impl EntityBase for ItemEntity { .living_entity .pickup(&self.entity, amount_picked_up.into()); - let player_clone = player.clone(); - tokio::spawn(async move { - player_clone - .current_screen_handler - .lock() - .await - .lock() - .await - .send_content_updates() - .await; - }); + player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); if is_empty { self.entity.remove(); diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index 476ff3e0b..e0b5f8e33 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -1906,7 +1906,7 @@ impl LivingEntity { } #[allow(dead_code)] - async fn damage_armor_items(&self, caller: &dyn EntityBase, damage_amount: f32) { + fn damage_armor_items(&self, caller: &dyn EntityBase, damage_amount: f32) { // Formula: armor loses floor(incoming_damage / 4) durability, minimum 1. let armor_damage = (damage_amount / 4.0).floor().max(1.0) as i32; let mut equipment_updates = Vec::new(); @@ -1954,12 +1954,10 @@ impl LivingEntity { } equipment_updates.push((slot.clone(), stack.clone())); if let Some(player) = caller.get_player() { - player - .enqueue_slot_set_packet(&CSetPlayerInventory::new( - (slot_index as i32).into(), - &ItemStackSerializer::from(stack), - )) - .await; + player.enqueue_slot_set_packet(&CSetPlayerInventory::new( + (slot_index as i32).into(), + &ItemStackSerializer::from(stack), + )); } } } diff --git a/crates/pumpkin/src/entity/passive/villager/mod.rs b/crates/pumpkin/src/entity/passive/villager/mod.rs index ace71463d..df87c9853 100644 --- a/crates/pumpkin/src/entity/passive/villager/mod.rs +++ b/crates/pumpkin/src/entity/passive/villager/mod.rs @@ -18,7 +18,7 @@ use pumpkin_data::tag::{Enchantment as EnchantmentTag, Taggable}; use pumpkin_data::tracked_data; use pumpkin_inventory::merchant::merchant_screen_handler::MerchantScreenHandler; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; @@ -32,7 +32,6 @@ use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3:: use pumpkin_util::text::TextComponent; use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_world::inventory::SimpleInventory; -use tokio::sync::Mutex; use crate::entity::player::Player; use crate::entity::{ @@ -881,18 +880,30 @@ impl VillagerEntity { .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 { + let ok = { + let screen = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen = screen + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if screen.sync_id() != sync_id { + false + } else if let Some(handler) = + screen.as_any_mut().downcast_mut::() + { + handler.offers.clone_from(&offers); + handler.update_result_slot(); + true + } else { + false + } + }; + if !ok { 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; } @@ -919,18 +930,30 @@ impl VillagerEntity { .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 { + let ok = { + let screen = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen = screen + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if screen.sync_id() != sync_id { + false + } else if let Some(handler) = + screen.as_any_mut().downcast_mut::() + { + handler.offers.clone_from(&offers); + handler.update_result_slot(); + true + } else { + false + } + }; + if !ok { 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; } @@ -1277,7 +1300,7 @@ 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 { + if let Some(sync_id) = player.open_handled_screen(self, None) { let offers = self .offers .lock() @@ -1435,96 +1458,89 @@ impl VillagerEntity { } impl ScreenHandlerFactory for VillagerEntity { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let self_weak = self - .self_weak - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone()?; - let server_player = player.as_any().downcast_ref::(); - let player_uuid = - server_player.map_or_else(uuid::Uuid::nil, |p| p.get_entity().entity_uuid); - if let Some(player) = server_player { - self.update_special_prices(player); + player_inventory: &Arc, + player: &dyn InventoryPlayer, + ) -> Option { + let self_weak = self + .self_weak + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone()?; + let server_player = player.as_any().downcast_ref::(); + let player_uuid = + server_player.map_or_else(uuid::Uuid::nil, |p| p.get_entity().entity_uuid); + if let Some(player) = server_player { + self.update_special_prices(player); + } + 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, + ); + + self.is_trading.store(true, Ordering::Relaxed); + *self + .trading_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((player_uuid, sync_id)); + let validity_weak = self_weak.clone(); + handler.validity_check = Some(Box::new(move |inventory_player| { + validity_weak.upgrade().is_some_and(|villager| { + villager.can_continue_trading(inventory_player, player_uuid, sync_id) + }) + })); + let update_weak = self_weak.clone(); + handler.on_trade_updated = Some(Box::new(move |has_result| { + let Some(villager) = update_weak.upgrade() else { + return; + }; + if villager + .trade_sound_cooldown + .compare_exchange(0, 20, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + villager.get_entity().play_sound(if has_result { + pumpkin_data::sound::Sound::EntityVillagerYes + } else { + pumpkin_data::sound::Sound::EntityVillagerNo + }); } - let offers = self - .offers - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone(); - let world = self.get_entity().world.load().clone(); + })); + let close_weak = self_weak.clone(); + handler.on_close = Some(Box::new(move || { + if let Some(villager) = close_weak.upgrade() { + villager.is_trading.store(false, Ordering::Relaxed); + *villager + .trading_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + villager.reset_special_prices(); + } + })); - let mut handler = MerchantScreenHandler::new( - sync_id, - player_inventory, - self.merchant_inventory.clone(), - offers, - ) - .await; - - self.is_trading.store(true, Ordering::Relaxed); - *self - .trading_player - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((player_uuid, sync_id)); - let validity_weak = self_weak.clone(); - handler.validity_check = Some(Box::new(move |inventory_player| { - validity_weak.upgrade().is_some_and(|villager| { - villager.can_continue_trading(inventory_player, player_uuid, sync_id) - }) - })); - let update_weak = self_weak.clone(); - handler.on_trade_updated = Some(Box::new(move |has_result| { - let Some(villager) = update_weak.upgrade() else { - return; - }; - if villager - .trade_sound_cooldown - .compare_exchange(0, 20, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - villager.get_entity().play_sound(if has_result { - pumpkin_data::sound::Sound::EntityVillagerYes - } else { - pumpkin_data::sound::Sound::EntityVillagerNo - }); - } - })); - let close_weak = self_weak.clone(); - handler.on_close = Some(Box::new(move || { - let close_weak = close_weak.clone(); - Box::pin(async move { - if let Some(villager) = close_weak.upgrade() { - villager.is_trading.store(false, Ordering::Relaxed); - *villager - .trading_player - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - villager.reset_special_prices(); - } - }) - })); - - handler.on_trade = Some(Box::new(move |offer_index| { - let self_weak = self_weak.clone(); + handler.on_trade = Some(Box::new(move |offer_index| { + if let Some(villager) = self_weak.upgrade() { let world = world.clone(); - Box::pin(async move { - if let Some(villager) = self_weak.upgrade() { - villager - .complete_trade(offer_index, &world, player_uuid) - .await; - } - }) - })); + tokio::spawn(async move { + villager + .complete_trade(offer_index, &world, player_uuid) + .await; + }); + } + })); - Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) - }) + Some(Arc::new(std::sync::Mutex::new(handler)) as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/entity/passive/wandering_trader.rs b/crates/pumpkin/src/entity/passive/wandering_trader.rs index c868a8a98..2dd468fb2 100644 --- a/crates/pumpkin/src/entity/passive/wandering_trader.rs +++ b/crates/pumpkin/src/entity/passive/wandering_trader.rs @@ -17,7 +17,7 @@ use pumpkin_data::villager::{ }; use pumpkin_inventory::merchant::merchant_screen_handler::MerchantScreenHandler; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; @@ -298,7 +298,7 @@ impl WanderingTraderEntity { } pub async fn open_trading_screen(&self, player: &Arc) { - if let Some(sync_id) = player.open_handled_screen(self, None).await { + if let Some(sync_id) = player.open_handled_screen(self, None) { let offers = self .offers .lock() @@ -488,95 +488,87 @@ impl WanderingTraderEntity { } impl ScreenHandlerFactory for WanderingTraderEntity { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let self_weak = self - .self_weak - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone()?; - let server_player = player.as_any().downcast_ref::(); - let player_uuid = - server_player.map_or_else(uuid::Uuid::nil, |p| p.get_entity().entity_uuid); + player_inventory: &Arc, + player: &dyn InventoryPlayer, + ) -> Option { + let self_weak = self + .self_weak + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone()?; + let server_player = player.as_any().downcast_ref::(); + let player_uuid = + server_player.map_or_else(uuid::Uuid::nil, |p| p.get_entity().entity_uuid); - let offers = self - .offers - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone(); - let world = self.get_entity().world.load().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( - sync_id, - player_inventory, - self.merchant_inventory.clone(), - offers, - ) - .await; + let mut handler = MerchantScreenHandler::new( + sync_id, + player_inventory, + self.merchant_inventory.clone(), + offers, + ); - self.is_trading.store(true, Ordering::Relaxed); - *self - .trading_player - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((player_uuid, sync_id)); + self.is_trading.store(true, Ordering::Relaxed); + *self + .trading_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((player_uuid, sync_id)); - let validity_weak = self_weak.clone(); - handler.validity_check = Some(Box::new(move |inventory_player| { - validity_weak.upgrade().is_some_and(|trader| { - trader.can_continue_trading(inventory_player, player_uuid, sync_id) - }) - })); + let validity_weak = self_weak.clone(); + handler.validity_check = Some(Box::new(move |inventory_player| { + validity_weak.upgrade().is_some_and(|trader| { + trader.can_continue_trading(inventory_player, player_uuid, sync_id) + }) + })); - let update_weak = self_weak.clone(); - handler.on_trade_updated = Some(Box::new(move |has_result| { - let Some(trader) = update_weak.upgrade() else { - return; - }; - if trader - .trade_sound_cooldown - .compare_exchange(0, 20, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - trader.get_entity().play_sound(if has_result { - Sound::EntityWanderingTraderYes - } else { - Sound::EntityWanderingTraderNo - }); - } - })); + let update_weak = self_weak.clone(); + handler.on_trade_updated = Some(Box::new(move |has_result| { + let Some(trader) = update_weak.upgrade() else { + return; + }; + if trader + .trade_sound_cooldown + .compare_exchange(0, 20, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + trader.get_entity().play_sound(if has_result { + Sound::EntityWanderingTraderYes + } else { + Sound::EntityWanderingTraderNo + }); + } + })); - let close_weak = self_weak.clone(); - handler.on_close = Some(Box::new(move || { - let close_weak = close_weak.clone(); - Box::pin(async move { - if let Some(trader) = close_weak.upgrade() { - trader.is_trading.store(false, Ordering::Relaxed); - *trader - .trading_player - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) = None; - } - }) - })); + let close_weak = self_weak.clone(); + handler.on_close = Some(Box::new(move || { + if let Some(trader) = close_weak.upgrade() { + trader.is_trading.store(false, Ordering::Relaxed); + *trader + .trading_player + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } + })); - let trade_weak = self_weak.clone(); - handler.on_trade = Some(Box::new(move |offer_index| { - let trade_weak = trade_weak.clone(); - let world = world.clone(); - Box::pin(async move { - if let Some(trader) = trade_weak.upgrade() { - trader.complete_trade(offer_index, &world, player_uuid); - } - }) - })); + let trade_weak = self_weak; + handler.on_trade = Some(Box::new(move |offer_index| { + let trade_weak = trade_weak.clone(); + let world = world.clone(); + if let Some(trader) = trade_weak.upgrade() { + trader.complete_trade(offer_index, &world, player_uuid); + } + })); - Some(Arc::new(tokio::sync::Mutex::new(handler)) as SharedScreenHandler) - }) + Some(Arc::new(std::sync::Mutex::new(handler)) as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index f59136726..f2ca20ed4 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -244,8 +244,8 @@ use pumpkin_inventory::player::{ player_inventory::PlayerInventory, player_screen_handler::PlayerScreenHandler, }; use pumpkin_inventory::screen_handler::{ - BoxFuture, ClickType, InventoryPlayer, PlayerFuture, ScreenHandler, ScreenHandlerBehaviour, - ScreenHandlerFactory, ScreenHandlerListener, + ClickType, InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFactory, + ScreenHandlerListener, }; use pumpkin_inventory::sync_handler::SyncHandler; use pumpkin_macros::send_cancellable; @@ -803,8 +803,8 @@ pub struct Player { root_vehicle_uuid: AtomicCell>, pub chat_session: Arc>, pub signature_cache: Mutex, - pub player_screen_handler: Arc>, - pub current_screen_handler: Mutex>>, + pub player_screen_handler: Arc>, + pub current_screen_handler: std::sync::Mutex>>, pub screen_handler_sync_id: AtomicU8, pub screen_handler_listener: Arc, pub inventory_changed: Arc, @@ -931,14 +931,13 @@ impl Player { struct ScreenListener(Arc); impl ScreenHandlerListener for ScreenListener { - fn on_slot_update<'a>( - &'a self, - _screen_handler: &'a ScreenHandlerBehaviour, + fn on_slot_update( + &self, + _screen_handler: &ScreenHandlerBehaviour, _slot: u8, _stack: ItemStack, - ) -> BoxFuture<'a, ()> { + ) { self.0.store(true, Ordering::Relaxed); - Box::pin(async {}) } } @@ -977,10 +976,12 @@ impl Player { let ender_chest_inventory = Arc::new(EnderChestInventory::new()); - let player_screen_handler = Arc::new(Mutex::new( - PlayerScreenHandler::new(&inventory, None, 0, Some(server.recipe_manager.clone())) - .await, - )); + let player_screen_handler = Arc::new(std::sync::Mutex::new(PlayerScreenHandler::new( + &inventory, + None, + 0, + Some(server.recipe_manager.clone()), + ))); // Initialize abilities based on gamemode (like vanilla's GameMode.setAbilities()) let mut abilities = Abilities::default(); @@ -1098,7 +1099,7 @@ impl Player { chat_session: Arc::new(Mutex::new(ChatSession::default())), // Placeholder value until the player actually sets their session id signature_cache: Mutex::new(MessageCache::default()), player_screen_handler: player_screen_handler.clone(), - current_screen_handler: Mutex::new(player_screen_handler), + current_screen_handler: std::sync::Mutex::new(player_screen_handler), screen_handler_sync_id: AtomicU8::new(0), screen_handler_listener: Arc::new(ScreenListener(inventory_changed.clone())), inventory_changed, @@ -1277,7 +1278,7 @@ impl Player { } /// Opens the player's ender chest screen. - pub async fn open_ender_chest(self: &Arc) -> Option { + pub fn open_ender_chest(self: &Arc) -> Option { self.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, @@ -1291,7 +1292,6 @@ impl Player { }, None, ) - .await } /// Removes the [`Player`] out of the current [`World`]. @@ -1299,13 +1299,13 @@ impl Player { if !self .current_screen_handler .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .as_any() .is::() { - self.on_handled_screen_closed().await; + self.on_handled_screen_closed(); } let vehicle = self @@ -2466,18 +2466,10 @@ impl Player { if is_invalid { if let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) { - tokio::spawn(async move { - p.close_handled_screen().await; - }); + p.close_handled_screen(); } - } else { - tokio::spawn(async move { - current_screen_handler - .lock() - .await - .send_content_updates() - .await; - }); + } else if let Ok(mut screen_handler) = current_screen_handler.try_lock() { + screen_handler.send_content_updates(); } } @@ -3617,10 +3609,10 @@ impl Player { self.send_abilities_update(); self.enqueue_set_held_item_packet(&CSetSelectedSlot::new( - self.get_inventory().get_selected_slot() as i8, - )).await; + self.get_inventory().get_selected_slot() as i8, + )); - self.on_screen_handler_opened(self.player_screen_handler.clone()).await; + self.on_screen_handler_opened(&self.player_screen_handler); self.send_health(); @@ -4540,15 +4532,17 @@ impl Player { self.drop_item(dropped_stack); let inv: Arc = self.inventory.clone(); - let screen_binding = self.current_screen_handler.lock().await; - let mut screen_handler = screen_binding.lock().await; + let screen_binding = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut screen_handler = screen_binding + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let selected_slot = self.inventory.get_selected_slot(); - if let Some(slot_index) = screen_handler - .get_slot_index(&inv, selected_slot as usize) - .await - { + if let Some(slot_index) = screen_handler.get_slot_index(&inv, selected_slot as usize) { screen_handler.set_received_stack(slot_index, updated_stack); - screen_handler.send_content_updates().await; + screen_handler.send_content_updates(); } } @@ -4983,10 +4977,15 @@ impl Player { .store(current_id % 100 + 1, Ordering::Relaxed); } - pub async fn close_handled_screen(self: &Arc) { + pub fn close_handled_screen(&self) { let (sync_id, bedrock_window_type) = { - let current_handler_guard = self.current_screen_handler.lock().await; - let handler = current_handler_guard.lock().await; + let current_handler_guard = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let handler = current_handler_guard + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let sync_id = handler.sync_id(); let window_type = handler.window_type(); let bedrock_window_type = match window_type { @@ -5009,65 +5008,74 @@ impl Player { (sync_id, bedrock_window_type) }; - self.client - .enqueue_packet_editioned( - &CCloseContainer::new(sync_id.into()), - &pumpkin_protocol::bedrock::server::container_close::SContainerClose { - container_id: sync_id, - container_type: bedrock_window_type, - server_initiated_close: true, - }, - ) - .await; - self.on_handled_screen_closed().await; + self.try_enqueue_packet_editioned( + &CCloseContainer::new(sync_id.into()), + &pumpkin_protocol::bedrock::server::container_close::SContainerClose { + container_id: sync_id, + container_type: bedrock_window_type, + server_initiated_close: true, + }, + ); + self.on_handled_screen_closed(); } - pub async fn on_handled_screen_closed(self: &Arc) { - let current_screen_handler: Arc> = - self.current_screen_handler.lock().await.clone(); + pub fn on_handled_screen_closed(&self) { + let current_screen_handler: Arc> = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); let window_type = { - let mut handler = current_screen_handler.lock().await; + let mut handler = current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let wt = handler.window_type(); - handler.on_closed(self.as_ref()).await; + handler.on_closed(self); wt }; - let server = self.living_entity.entity.world.load().server.upgrade(); - if let Some(server) = server { + let world = self.living_entity.entity.world.load(); + let server = world.server.upgrade(); + if let Some(server) = server + && let Some(player_arc) = world.get_player_by_uuid(self.gameprofile.id) + { let mut event = crate::plugin::api::events::player::inventory_close::InventoryCloseEvent::new( - self, + &player_arc, window_type, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } - let player_screen_handler: Arc> = + let player_screen_handler: Arc> = self.player_screen_handler.clone(); if !Arc::ptr_eq(&player_screen_handler, ¤t_screen_handler) { player_screen_handler .lock() - .await - .copy_shared_slots(current_screen_handler) - .await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .copy_shared_slots(current_screen_handler); } - *self.current_screen_handler.lock().await = self.player_screen_handler.clone(); + *self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = + self.player_screen_handler.clone(); self.open_container_pos.store(None); } - pub async fn on_screen_handler_opened(&self, screen_handler: Arc>) { - let mut screen_handler = screen_handler.lock().await; + pub fn on_screen_handler_opened( + &self, + screen_handler: &std::sync::Mutex, + ) { + let mut screen_handler = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - screen_handler - .add_listener(self.screen_handler_listener.clone()) - .await; - - screen_handler - .update_sync_handler(self.screen_handler_sync_handler.clone()) - .await; + screen_handler.add_listener(self.screen_handler_listener.clone()); + screen_handler.update_sync_handler(self.screen_handler_sync_handler.clone()); } pub async fn on_rename_item(self: &Arc, packet: SRenameItem<'_>) { @@ -5086,43 +5094,49 @@ impl Player { .await; } - let screen_handler_arc = self.current_screen_handler.lock().await.clone(); - let mut screen_handler = screen_handler_arc.lock().await; + let screen_handler_arc = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(anvil_handler) = screen_handler .as_any_mut() .downcast_mut::() { - anvil_handler - .update_item_name(packet.item_name.to_string()) - .await; + anvil_handler.update_item_name(packet.item_name.to_string()); } } - pub async fn open_handled_screen( - self: &Arc, + pub fn open_handled_screen( + &self, screen_handler_factory: &dyn ScreenHandlerFactory, block_pos: Option, ) -> Option { if !self .current_screen_handler .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .as_any() .is::() { - self.close_handled_screen().await; + self.close_handled_screen(); } let server = self.world().server.upgrade(); - if let Some(server) = server { + if let Some(server) = server + && let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) + { let mut event = crate::plugin::api::events::inventory::inventory_open::InventoryOpenEvent::new( - self.clone(), + player_arc, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return None; } @@ -5130,15 +5144,14 @@ impl Player { self.increment_screen_handler_sync_id(); - if let Some(screen_handler) = screen_handler_factory - .create_screen_handler( - self.screen_handler_sync_id.load(Ordering::Relaxed), - &self.inventory, - self.as_ref(), - ) - .await - { - let screen_handler_temp = screen_handler.lock().await; + if let Some(screen_handler) = screen_handler_factory.create_screen_handler( + self.screen_handler_sync_id.load(Ordering::Relaxed), + &self.inventory, + self, + ) { + let screen_handler_temp = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let sync_id = screen_handler_temp.sync_id(); let window_type = screen_handler_temp.window_type()?; @@ -5172,13 +5185,14 @@ impl Player { target_entity_id: VarLong(-1), }; - self.client - .enqueue_packet_editioned(&java_packet, &bedrock_packet) - .await; + self.try_enqueue_packet_editioned(&java_packet, &bedrock_packet); drop(screen_handler_temp); - self.on_screen_handler_opened(screen_handler.clone()).await; - *self.current_screen_handler.lock().await = screen_handler; + self.on_screen_handler_opened(&screen_handler); + *self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = screen_handler; self.open_container_pos.store(block_pos); Some(self.screen_handler_sync_id.load(Ordering::Relaxed)) } else { @@ -5188,30 +5202,32 @@ impl Player { } } - pub async fn open_handled_screen_direct( - self: &Arc, - screen_handler: Arc>, - title: TextComponent, + pub fn open_handled_screen_direct( + &self, + screen_handler: Arc>, + title: &TextComponent, ) { if !self .current_screen_handler .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .as_any() .is::() { - self.close_handled_screen().await; + self.close_handled_screen(); } - let screen_handler_temp = screen_handler.lock().await; + let screen_handler_temp = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let sync_id = screen_handler_temp.sync_id(); let Some(window_type) = screen_handler_temp.window_type() else { return; }; - let java_packet = COpenScreen::new(sync_id.into(), (window_type as i32).into(), &title); + let java_packet = COpenScreen::new(sync_id.into(), (window_type as i32).into(), title); let bedrock_window_type = match window_type { WindowType::Crafting => 1, @@ -5238,29 +5254,74 @@ impl Player { target_entity_id: VarLong(-1), }; - self.client - .enqueue_packet_editioned(&java_packet, &bedrock_packet) - .await; + self.try_enqueue_packet_editioned(&java_packet, &bedrock_packet); drop(screen_handler_temp); - self.on_screen_handler_opened(screen_handler.clone()).await; - *self.current_screen_handler.lock().await = screen_handler; + self.on_screen_handler_opened(&screen_handler); + *self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = screen_handler; self.open_container_pos.store(None); } #[allow(clippy::too_many_lines)] pub async fn on_slot_click(self: &Arc, packet: SClickSlot, server: &Arc) { self.update_last_action_time(); - let screen_handler_arc = self.current_screen_handler.lock().await.clone(); - let mut screen_handler = screen_handler_arc.lock().await; - let (sync_id, container_slots, allow_grab_items, allow_put_items) = { + let ( + sync_id, + container_slots, + allow_grab_items, + allow_put_items, + can_use, + is_slot_valid, + available_slots, + clicked_item, + cursor_item, + window_type, + ) = { + let screen_handler_arc = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let screen_handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let b = screen_handler.get_behaviour(); + let sync_id = b.sync_id; + let container_slots = b.container_slots; + let allow_grab_items = b.allow_grab_items; + let allow_put_items = b.allow_put_items; + let can_use = screen_handler.can_use(self.as_ref()); + let is_slot_valid = screen_handler.is_slot_valid(i32::from(packet.slot)); + let available_slots = b.slots.len(); + + let clicked_item = (packet.slot >= 0 && (packet.slot as usize) < b.slots.len()) + .then(|| b.slots[packet.slot as usize].get_cloned_stack()); + + let cursor_item = Some( + b.cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ); + + let window_type = screen_handler.window_type(); + ( - b.sync_id, - b.container_slots, - b.allow_grab_items, - b.allow_put_items, + sync_id, + container_slots, + allow_grab_items, + allow_put_items, + can_use, + is_slot_valid, + available_slots, + clicked_item, + cursor_item, + window_type, ) }; @@ -5269,47 +5330,48 @@ impl Player { } if self.gamemode.load() == GameMode::Spectator { - screen_handler.sync_state().await; + let screen_handler_arc = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .sync_state(); return; } - if !screen_handler.can_use(self.as_ref()) { + if !can_use { warn!( "Player {} interacted with invalid menu {:?}", - self.gameprofile.name, - screen_handler.window_type() + self.gameprofile.name, window_type ); return; } let slot = packet.slot; - if !screen_handler.is_slot_valid(i32::from(slot)).await { + if !is_slot_valid { warn!( "Player {} clicked invalid slot index: {}, available slots: {}", - self.gameprofile.name, - slot, - screen_handler.get_behaviour().slots.len() + self.gameprofile.name, slot, available_slots ); return; } - // Fire InventoryClickEvent - let clicked_item = if slot >= 0 { - let slot_obj = &screen_handler.get_behaviour().slots[slot as usize]; - Some(slot_obj.get_cloned_stack().await) - } else { - None + let cancel_screen = || { + let screen_handler_arc = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .cancel(); }; - let cursor_item = Some( - screen_handler - .get_behaviour() - .cursor_stack - .lock() - .await - .clone(), - ); let raw_slot = slot; // For now raw_slot == slot, as we don't have separate view/inventory indexing yet let hotbar_button = if matches!(packet.mode, SlotActionType::Swap) { packet.button @@ -5357,7 +5419,7 @@ impl Player { server; InventoryClickEvent::new( self, - screen_handler.window_type(), + window_type, click_type, slot, raw_slot, @@ -5367,7 +5429,7 @@ impl Player { ); 'after: {} 'cancelled: { - screen_handler.cancel().await; + cancel_screen(); return; } }} @@ -5383,7 +5445,7 @@ impl Player { .await; } if interact_event.cancelled { - screen_handler.cancel().await; + cancel_screen(); return; } @@ -5406,12 +5468,12 @@ impl Player { server.plugin_manager.fire(&server, &mut prep_craft).await; } if craft_event.cancelled || prep_craft.cancelled { - screen_handler.cancel().await; + cancel_screen(); return; } } - if screen_handler.window_type() == Some(WindowType::Smithing) + if window_type == Some(WindowType::Smithing) && slot == 3 && let Some(ref stack) = clicked_item && !stack.is_empty() @@ -5431,14 +5493,14 @@ impl Player { server.plugin_manager.fire(&server, &mut prep_smith).await; } if smith_event.cancelled { - screen_handler.cancel().await; + cancel_screen(); return; } } - if (screen_handler.window_type() == Some(WindowType::Furnace) - || screen_handler.window_type() == Some(WindowType::BlastFurnace) - || screen_handler.window_type() == Some(WindowType::Smoker)) + if (window_type == Some(WindowType::Furnace) + || window_type == Some(WindowType::BlastFurnace) + || window_type == Some(WindowType::Smoker)) && slot == 2 && let Some(ref stack) = clicked_item && !stack.is_empty() @@ -5459,7 +5521,7 @@ impl Player { } } - if screen_handler.window_type() == Some(WindowType::Grindstone) + if window_type == Some(WindowType::Grindstone) && let Some(ref stack) = clicked_item { let mut prep_grindstone = @@ -5495,62 +5557,75 @@ impl Player { server.plugin_manager.fire(&server, &mut drag_event).await; } if drag_event.cancelled { - screen_handler.cancel().await; + cancel_screen(); return; } } + let screen_handler_arc = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + // Enforce flags let is_container_slot = slot >= 0 && i32::from(slot) < container_slots as i32; match packet.mode { SlotActionType::Pickup => { - let cursor_stack = screen_handler.get_behaviour().cursor_stack.lock().await; + let cursor_stack = screen_handler + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if is_container_slot { if !cursor_stack.is_empty() && !allow_put_items { drop(cursor_stack); - screen_handler.cancel().await; + screen_handler.cancel(); return; } if cursor_stack.is_empty() && !allow_grab_items { drop(cursor_stack); - screen_handler.cancel().await; + screen_handler.cancel(); return; } } } SlotActionType::QuickMove => { if is_container_slot && !allow_grab_items { - screen_handler.cancel().await; + screen_handler.cancel(); return; } if !is_container_slot && !allow_put_items { - screen_handler.cancel().await; + screen_handler.cancel(); return; } } SlotActionType::Swap => { if is_container_slot && (!allow_grab_items || !allow_put_items) { - screen_handler.cancel().await; + screen_handler.cancel(); return; } } SlotActionType::Throw => { if is_container_slot && !allow_grab_items { - screen_handler.cancel().await; + screen_handler.cancel(); return; } } SlotActionType::QuickCraft => { if !allow_put_items { // Dragging items into slots - screen_handler.cancel().await; + screen_handler.cancel(); return; } } SlotActionType::PickupAll => { if !allow_grab_items { - screen_handler.cancel().await; + screen_handler.cancel(); return; } } @@ -5564,14 +5639,12 @@ impl Player { .load(Ordering::Relaxed) as i32); screen_handler.disable_sync(); - screen_handler - .on_slot_click( - i32::from(slot), - i32::from(packet.button), - packet.mode.clone(), - self.as_ref(), - ) - .await; + screen_handler.on_slot_click( + i32::from(slot), + i32::from(packet.button), + packet.mode.clone(), + self.as_ref(), + ); for (key, value) in packet.array_of_changed_slots { screen_handler.set_received_hash(key as usize, value); @@ -5581,24 +5654,28 @@ impl Player { screen_handler.enable_sync(); if not_in_sync { - screen_handler.update_to_client().await; + screen_handler.update_to_client(); } else { - screen_handler.send_content_updates().await; + screen_handler.send_content_updates(); } } /// Handles when the player clicks a button in a container (e.g. Enchantment Table) - pub async fn on_container_button_click(self: &Arc, packet: SContainerButtonClick) { - let screen_handler = self.current_screen_handler.lock().await.clone(); - let mut screen_handler = screen_handler.lock().await; + pub fn on_container_button_click(&self, packet: &SContainerButtonClick) { + let screen_handler = self + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if i32::from(screen_handler.sync_id()) != packet.window_id.0 { return; } - screen_handler - .on_button_click(self.as_ref(), packet.button_id.0) - .await; + screen_handler.on_button_click(self, packet.button_id.0); } pub fn has_permission(self: &Arc, server: &Server, node: &str) -> bool { @@ -6861,13 +6938,10 @@ impl InventoryPlayer for Player { self } - fn drop_item(&self, item: ItemStack, _retain_ownership: bool) -> PlayerFuture<'_, ()> { - Box::pin(async move { - self.drop_item(item); - }) + fn drop_item(&self, item: ItemStack, _retain_ownership: bool) { + self.drop_item(item); } - // Synchronous methods remain unchanged fn has_infinite_materials(&self) -> bool { self.gamemode.load() == GameMode::Creative } @@ -6881,419 +6955,364 @@ impl InventoryPlayer for Player { .load(std::sync::atomic::Ordering::Relaxed) } - fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()> { - Box::pin(async move { - self.add_experience_levels(levels); - }) + fn add_experience_levels(&self, levels: i32) { + self.add_experience_levels(levels); } fn enchantment_seed(&self) -> i32 { self.enchantment_seed.load(Ordering::Relaxed) } - fn set_enchantment_seed(&self, seed: i32) -> PlayerFuture<'_, ()> { - Box::pin(async move { - self.enchantment_seed.store(seed, Ordering::Relaxed); - }) + fn set_enchantment_seed(&self, seed: i32) { + self.enchantment_seed.store(seed, Ordering::Relaxed); } fn get_inventory(&self) -> Arc { self.inventory.clone() } - fn enqueue_inventory_packet<'a>( - &'a self, - packet: &'a CSetContainerContent, + fn enqueue_inventory_packet( + &self, + packet: &CSetContainerContent, window_type: Option, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - if let Ok(data) = java.serialize_packet(packet) { - java.enqueue_packet(data).await; - } - } - ClientPlatform::Bedrock(bedrock) => { - use pumpkin_protocol::bedrock::{ - client::inventory_content::CInventoryContent, - network_item::{ - ContainerName, FullContainerName, NetworkItemStackDescriptor, - }, - }; - use pumpkin_protocol::codec::var_uint::VarUInt; - - let window_id = packet.window_id.0 as u32; - if window_id == 0 { - // Java's player screen also contains crafting, armor, and off-hand - // slots. Bedrock container 0 contains only the 36-slot player - // inventory in hotbar-first order. - let slots = self - .inventory - .main_inventory - .read() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .iter() - .map(NetworkItemStackDescriptor::from) - .collect(); - let bedrock_packet = CInventoryContent { - container_id: VarUInt(0), - slots, - full_container_name: FullContainerName { - container_name: ContainerName::Inventory, - dynamic_id: None, - }, - storage_item: NetworkItemStackDescriptor::default(), - }; - if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { - bedrock.enqueue_packet(data).await; - } - } else if matches!( - window_type, - Some( - WindowType::Generic9x1 - | WindowType::Generic9x2 - | WindowType::Generic9x3 - | WindowType::Generic9x4 - | WindowType::Generic9x5 - | WindowType::Generic9x6 - | WindowType::Generic3x3 - ) - ) { - // Java container screens append the player's 36 inventory slots to - // the container slots. Bedrock synchronizes those two inventories - // separately and addresses generic block containers as LevelEntity. - let container_slot_count = packet - .slot_data - .len() - .saturating_sub(PlayerInventory::MAIN_SIZE); - let slots = packet.slot_data[..container_slot_count] - .iter() - .map(|stack| NetworkItemStackDescriptor::from(&*stack.0)) - .collect(); - let bedrock_packet = CInventoryContent { - container_id: VarUInt(window_id), - slots, - full_container_name: FullContainerName { - container_name: ContainerName::LevelEntity, - dynamic_id: None, - }, - storage_item: NetworkItemStackDescriptor::default(), - }; - if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { - bedrock.enqueue_packet(data).await; - } - } + ) { + 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_content::CInventoryContent, + network_item::{ContainerName, FullContainerName, NetworkItemStackDescriptor}, + }; + use pumpkin_protocol::codec::var_uint::VarUInt; - fn enqueue_slot_packet<'a>( - &'a self, - packet: &'a CSetContainerSlot, - window_type: Option, - total_slots: usize, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - if let Ok(data) = java.serialize_packet(packet) { - java.enqueue_packet(data).await; - } - } - ClientPlatform::Bedrock(bedrock) => { - use pumpkin_protocol::bedrock::{ - client::inventory_slot::CInventorySlot, - network_item::{ - ContainerName, FullContainerName, NetworkItemStackDescriptor, - }, - }; - use pumpkin_protocol::codec::var_uint::VarUInt; - - let window_id = packet.window_id; - if window_id == 0 { - if let Some(slot_idx) = bedrock_inventory_slot(packet.slot) { - let item_desc = NetworkItemStackDescriptor::from(&*packet.slot_data.0); - let bedrock_packet = CInventorySlot { - container_id: VarUInt(0), - slot: VarUInt(slot_idx), - 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.enqueue_packet(data).await; - } - } - } else { - let slot_idx = packet.slot as usize; - let item_desc = NetworkItemStackDescriptor::from(&*packet.slot_data.0); - - let bedrock_info = if total_slots >= 36 { - let container_slots = total_slots - 36; - if slot_idx < container_slots { - if window_type == Some(WindowType::Crafting) { - if slot_idx == 0 { - Some((ContainerName::CreatedOutput, 0)) - } else { - Some(( - ContainerName::CraftingInput, - (32 + slot_idx - 1) as u8, - )) - } - } else { - Some((ContainerName::LevelEntity, slot_idx as u8)) - } - } else { - let inv_slot = slot_idx - container_slots; - if inv_slot < 27 { - Some((ContainerName::Inventory, (inv_slot + 9) as u8)) - } else { - Some((ContainerName::Inventory, (inv_slot - 27) as u8)) - } - } - } else { - None - }; - - if let Some((container_name, slot_id)) = bedrock_info { - let bedrock_packet = CInventorySlot { - container_id: VarUInt(window_id as u32), - slot: VarUInt(slot_id as u32), - full_container_name: Some(FullContainerName { - container_name, - dynamic_id: None, - }), - storage_item: None, - item: item_desc, - }; - if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { - bedrock.enqueue_packet(data).await; - } - } - } - } - } - }) - } - - fn enqueue_cursor_packet<'a>(&'a self, packet: &'a CSetCursorItem) -> PlayerFuture<'a, ()> { - Box::pin(async move { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - if let Ok(data) = java.serialize_packet(packet) { - java.enqueue_packet(data).await; - } - } - ClientPlatform::Bedrock(bedrock) => { - use pumpkin_protocol::bedrock::{ - client::inventory_content::CInventoryContent, - network_item::{ - ContainerName, FullContainerName, NetworkItemStackDescriptor, - }, - }; - use pumpkin_protocol::codec::var_uint::VarUInt; - - let item_desc = NetworkItemStackDescriptor::from(&*packet.stack.0); + let window_id = packet.window_id.0 as u32; + if window_id == 0 { + // Java's player screen also contains crafting, armor, and off-hand + // slots. Bedrock container 0 contains only the 36-slot player + // inventory in hotbar-first order. + let slots = self + .inventory + .main_inventory + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .iter() + .map(NetworkItemStackDescriptor::from) + .collect(); let bedrock_packet = CInventoryContent { - container_id: VarUInt(59), - slots: vec![item_desc], + container_id: VarUInt(0), + slots, full_container_name: FullContainerName { - container_name: ContainerName::Cursor, + container_name: ContainerName::Inventory, dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), }; if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { - bedrock.enqueue_packet(data).await; + bedrock.try_enqueue_packet(data); } - } - } - }) - } - - fn enqueue_property_packet<'a>( - &'a self, - packet: &'a CSetContainerProperty, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - self.send_client_packet(packet).await; - }) - } - - fn enqueue_slot_set_packet<'a>( - &'a self, - packet: &'a CSetPlayerInventory, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - match self.client.as_ref() { - ClientPlatform::Java(java) => { - if let Ok(data) = java.serialize_packet(packet) { - java.enqueue_packet(data).await; - } - } - ClientPlatform::Bedrock(bedrock) => { - use pumpkin_protocol::bedrock::{ - client::inventory_slot::CInventorySlot, - network_item::{ - ContainerName, FullContainerName, NetworkItemStackDescriptor, - }, - }; - use pumpkin_protocol::codec::var_uint::VarUInt; - - tracing::info!( - "enqueue_slot_set_packet: slot={}, sending CInventorySlot to Bedrock client", - packet.slot.0 - ); - - 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, + } else if matches!( + window_type, + Some( + WindowType::Generic9x1 + | WindowType::Generic9x2 + | WindowType::Generic9x3 + | WindowType::Generic9x4 + | WindowType::Generic9x5 + | WindowType::Generic9x6 + | WindowType::Generic3x3 + ) + ) { + // Java container screens append the player's 36 inventory slots to + // the container slots. Bedrock synchronizes those two inventories + // separately and addresses generic block containers as LevelEntity. + let container_slot_count = packet + .slot_data + .len() + .saturating_sub(PlayerInventory::MAIN_SIZE); + let slots = packet.slot_data[..container_slot_count] + .iter() + .map(|stack| NetworkItemStackDescriptor::from(&*stack.0)) + .collect(); + let bedrock_packet = CInventoryContent { + container_id: VarUInt(window_id), + slots, + full_container_name: FullContainerName { + container_name: ContainerName::LevelEntity, dynamic_id: None, - }), - storage_item: None, - item: item_desc, + }, + storage_item: NetworkItemStackDescriptor::default(), }; if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { - bedrock.enqueue_packet(data).await; + bedrock.try_enqueue_packet(data); } } } - }) + } } - fn enqueue_set_held_item_packet<'a>( - &'a self, - packet: &'a CSetSelectedSlot, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - self.client - .enqueue_packet_editioned( - packet, - &pumpkin_protocol::bedrock::client::CPlayerHotbar { - selected_slot: pumpkin_protocol::codec::var_uint::VarUInt( - packet.slot as u32, - ), - container_id: 0, - should_select_slot: true, - }, - ) - .await; - }) - } - - fn enqueue_equipment_change<'a>( - &'a self, - slot: &'a EquipmentSlot, - stack: &'a ItemStack, - ) -> PlayerFuture<'a, ()> { - Box::pin(async move { - self.living_entity - .send_equipment_changes(&[(slot.clone(), stack.clone())]); - - if let Some(equippable) = stack.get_data_component::() { - self.world().play_sound_event( - &equippable.equip_sound, - SoundCategory::Players, - &self.position(), - ); - } - }) - } - - fn award_experience(&self, amount: i32) -> PlayerFuture<'_, ()> { - Box::pin(async move { - debug!("Player::award_experience called with amount={amount}"); - if amount > 0 { - 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); + fn enqueue_slot_packet( + &self, + packet: &CSetContainerSlot, + window_type: Option, + total_slots: usize, + ) { + 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; - fn increment_stat( - &self, - category: StatisticCategory, - stat_id: i32, - amount: i32, - ) -> PlayerFuture<'_, ()> { - Box::pin(async move { - self.increment_stat(category, stat_id, amount); - }) - } + let window_id = packet.window_id; + if window_id == 0 { + if let Some(slot_idx) = bedrock_inventory_slot(packet.slot) { + let item_desc = NetworkItemStackDescriptor::from(&*packet.slot_data.0); + let bedrock_packet = CInventorySlot { + container_id: VarUInt(0), + slot: VarUInt(slot_idx), + 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); + } + } + } else { + let slot_idx = packet.slot as usize; + let item_desc = NetworkItemStackDescriptor::from(&*packet.slot_data.0); - fn fire_prepare_item_enchant_event<'a>( - &'a self, - item: &'a ItemStack, - level_requirements: &'a mut [i32; 3], - enchantment_id: &'a mut [i32; 3], - enchantment_level: &'a mut [i32; 3], - bookshelf_count: i32, - ) -> PlayerFuture<'a, bool> { - Box::pin(async move { - let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { - return false; - }; - let Some(server) = self.world().server.upgrade() else { - return false; - }; - let mut event = PrepareItemEnchantEvent::new( - player_arc, - item.clone(), - *level_requirements, - *enchantment_id, - *enchantment_level, - bookshelf_count, - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - return true; + let bedrock_info = if total_slots >= 36 { + let container_slots = total_slots - 36; + if slot_idx < container_slots { + if window_type == Some(WindowType::Crafting) { + if slot_idx == 0 { + Some((ContainerName::CreatedOutput, 0)) + } else { + Some((ContainerName::CraftingInput, (32 + slot_idx - 1) as u8)) + } + } else { + Some((ContainerName::LevelEntity, slot_idx as u8)) + } + } else { + let inv_slot = slot_idx - container_slots; + if inv_slot < 27 { + Some((ContainerName::Inventory, (inv_slot + 9) as u8)) + } else { + Some((ContainerName::Inventory, (inv_slot - 27) as u8)) + } + } + } else { + None + }; + + if let Some((container_name, slot_id)) = bedrock_info { + let bedrock_packet = CInventorySlot { + container_id: VarUInt(window_id as u32), + slot: VarUInt(slot_id as u32), + full_container_name: Some(FullContainerName { + container_name, + dynamic_id: None, + }), + storage_item: None, + item: item_desc, + }; + if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { + bedrock.try_enqueue_packet(data); + } + } + } } - *level_requirements = event.level_requirements; - *enchantment_id = event.enchantment_id; - *enchantment_level = event.enchantment_level; - false - }) + } } - fn fire_enchant_item_event<'a>( - &'a self, - item: &'a ItemStack, + fn enqueue_cursor_packet(&self, packet: &CSetCursorItem) { + 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_content::CInventoryContent, + network_item::{ContainerName, FullContainerName, NetworkItemStackDescriptor}, + }; + use pumpkin_protocol::codec::var_uint::VarUInt; + + let item_desc = NetworkItemStackDescriptor::from(&*packet.stack.0); + let bedrock_packet = CInventoryContent { + container_id: VarUInt(59), + slots: vec![item_desc], + full_container_name: FullContainerName { + container_name: ContainerName::Cursor, + dynamic_id: None, + }, + storage_item: NetworkItemStackDescriptor::default(), + }; + if let Ok(data) = bedrock.serialize_packet(&bedrock_packet) { + bedrock.try_enqueue_packet(data); + } + } + } + } + + fn enqueue_property_packet(&self, packet: &CSetContainerProperty) { + self.try_send_client_packet(packet); + } + + fn enqueue_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; + + tracing::info!( + "enqueue_slot_set_packet: slot={}, sending CInventorySlot to Bedrock client", + packet.slot.0 + ); + + 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); + } + } + } + } + + fn enqueue_set_held_item_packet(&self, packet: &CSetSelectedSlot) { + self.try_enqueue_packet_editioned( + packet, + &pumpkin_protocol::bedrock::client::CPlayerHotbar { + selected_slot: pumpkin_protocol::codec::var_uint::VarUInt(packet.slot as u32), + container_id: 0, + should_select_slot: true, + }, + ); + } + + fn enqueue_equipment_change(&self, slot: &EquipmentSlot, stack: &ItemStack) { + self.living_entity + .send_equipment_changes(&[(slot.clone(), stack.clone())]); + + if let Some(equippable) = stack.get_data_component::() { + self.world().play_sound_event( + &equippable.equip_sound, + SoundCategory::Players, + &self.position(), + ); + } + } + + fn award_experience(&self, amount: i32) { + debug!("Player::award_experience called with amount={amount}"); + if amount > 0 { + 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); + } + } + } + + fn increment_stat(&self, category: StatisticCategory, stat_id: i32, amount: i32) { + self.increment_stat(category, stat_id, amount); + } + + fn fire_prepare_item_enchant_event( + &self, + item: &ItemStack, + level_requirements: &mut [i32; 3], + enchantment_id: &mut [i32; 3], + enchantment_level: &mut [i32; 3], + bookshelf_count: i32, + ) -> bool { + let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { + return false; + }; + let Some(server) = self.world().server.upgrade() else { + return false; + }; + let mut event = PrepareItemEnchantEvent::new( + player_arc, + item.clone(), + *level_requirements, + *enchantment_id, + *enchantment_level, + bookshelf_count, + ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + return true; + } + *level_requirements = event.level_requirements; + *enchantment_id = event.enchantment_id; + *enchantment_level = event.enchantment_level; + false + } + + fn fire_enchant_item_event( + &self, + item: &ItemStack, option: i32, exp_level_cost: i32, - enchantments_to_add: &'a mut Vec<(&'static pumpkin_data::Enchantment, i32)>, - ) -> PlayerFuture<'a, bool> { - Box::pin(async move { - let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { - return false; - }; - let Some(server) = self.world().server.upgrade() else { - return false; - }; - let mut event = EnchantItemEvent::new( - player_arc, - item.clone(), - option, - exp_level_cost, - enchantments_to_add.clone(), - ); - server.plugin_manager.fire(&server, &mut event).await; - if event.cancelled { - return true; - } - *enchantments_to_add = event.enchantments_to_add; - false - }) + enchantments_to_add: &mut Vec<(&'static pumpkin_data::Enchantment, i32)>, + ) -> bool { + let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { + return false; + }; + let Some(server) = self.world().server.upgrade() else { + return false; + }; + let mut event = EnchantItemEvent::new( + player_arc, + item.clone(), + option, + exp_level_cost, + enchantments_to_add.clone(), + ); + server.plugin_manager.fire_blocking(&server, &mut event); + if event.cancelled { + return true; + } + *enchantments_to_add = event.enchantments_to_add; + false + } + + fn close_screen_handler(&self) { + self.close_handled_screen(); } } diff --git a/crates/pumpkin/src/entity/vehicle/minecart.rs b/crates/pumpkin/src/entity/vehicle/minecart.rs index c65e676ca..2d34295fd 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart.rs @@ -732,10 +732,10 @@ impl EntityBase for MinecartEntity { { let container_clone = container.clone(); let world_clone = self.vehicle.entity.world.load_full(); + container_clone.unpack_loot(); tokio::spawn(async move { - container_clone.unpack_loot().await; let inventory: Arc = container_clone; - world_clone.scatter_inventory(&position, &inventory).await; + world_clone.scatter_inventory(&position, &inventory); }); } if let Some(item) = self.drop_item() { @@ -750,24 +750,16 @@ impl EntityBase for MinecartEntity { 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; - }); + minecart.interact(custom_name, player); 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; - }); + minecart.interact(custom_name, player); true } MinecartKind::Rideable(_) => RideableMinecart::interact(&self.vehicle.entity, player), diff --git a/crates/pumpkin/src/entity/vehicle/minecart/chest.rs b/crates/pumpkin/src/entity/vehicle/minecart/chest.rs index 25a7e041c..4403adeaa 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/chest.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/chest.rs @@ -24,7 +24,7 @@ impl ChestMinecart { &self.inventory } - pub(super) async fn interact( + pub(super) fn interact( &self, custom_name: Option, player: &Arc, @@ -40,7 +40,6 @@ impl ChestMinecart { ), false, ) - .await } pub(super) fn write_nbt(&self, nbt: &mut NbtCompound) { diff --git a/crates/pumpkin/src/entity/vehicle/minecart/container.rs b/crates/pumpkin/src/entity/vehicle/minecart/container.rs index c8bdb6b49..ca076c06e 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/container.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/container.rs @@ -1,19 +1,18 @@ use std::any::Any; -use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Mutex, RwLock}; use pumpkin_data::item_stack::ItemStack; use pumpkin_inventory::generic_container_screen_handler::{create_generic_9x3, create_hopper}; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ - BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, + InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; -use tokio::sync::{Mutex, RwLock}; +use pumpkin_world::inventory::{Clearable, Inventory}; use crate::entity::{Entity, player::Player}; use crate::world::loot::fill_chest_inventory; @@ -83,22 +82,32 @@ impl MinecartInventory { } } - pub(super) async fn has_loot_table(&self) -> bool { - self.loot_table.lock().await.is_some() + pub(super) fn has_loot_table(&self) -> bool { + self.loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_some() } - pub(super) async fn unpack_loot(self: &Arc) { - let loot_table = self.loot_table.lock().await.take(); + pub(super) fn unpack_loot(self: &Arc) { + let loot_table = self + .loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); let Some((loot_table, seed)) = loot_table else { return; }; let Some(table) = get_chest_loot_table(&loot_table) else { - *self.loot_table.lock().await = Some((loot_table, seed)); + *self + .loot_table + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((loot_table, seed)); return; }; let inventory: Arc = self.clone(); - fill_chest_inventory(&inventory, table, seed).await; + fill_chest_inventory(&inventory, table, seed); } } @@ -107,39 +116,45 @@ impl Inventory for MinecartInventory { self.size } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let items = self.items.read().await; - items.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + 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 { self.items.read().await[slot].clone() }) + fn get_stack(&self, slot: usize) -> ItemStack { + self.items + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner)[slot] + .clone() } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone()) } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut items = self.items.write().await; - if !items[slot].is_empty() && amount > 0 { - items[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut items = self + .items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if !items[slot].is_empty() && amount > 0 { + items[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - self.items.write().await[slot] = stack; - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + self.items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner)[slot] = stack; } fn as_any(&self) -> &dyn Any { @@ -148,13 +163,11 @@ impl Inventory for MinecartInventory { } impl Clearable for MinecartInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - self.items - .write() - .await - .fill_with(|| ItemStack::EMPTY.clone()); - }) + fn clear(&self) { + self.items + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .fill_with(|| ItemStack::EMPTY.clone()); } } @@ -165,21 +178,19 @@ struct MinecartScreenFactory { } impl ScreenHandlerFactory for MinecartScreenFactory { - fn create_screen_handler<'a>( - &'a self, + fn create_screen_handler( + &self, sync_id: u8, - player_inventory: &'a Arc, - _player: &'a dyn InventoryPlayer, - ) -> BoxFuture<'a, Option> { - Box::pin(async move { - let inventory: Arc = self.inventory.clone(); - let handler = if self.hopper { - create_hopper(sync_id, player_inventory, inventory).await - } else { - create_generic_9x3(sync_id, player_inventory, inventory).await - }; - Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) - }) + player_inventory: &Arc, + _player: &dyn InventoryPlayer, + ) -> Option { + let inventory: Arc = self.inventory.clone(); + let handler = if self.hopper { + create_hopper(sync_id, player_inventory, inventory) + } else { + create_generic_9x3(sync_id, player_inventory, inventory) + }; + Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) } fn get_display_name(&self) -> TextComponent { @@ -187,18 +198,18 @@ impl ScreenHandlerFactory for MinecartScreenFactory { } } -pub(super) async fn open( +pub(super) fn open( custom_name: Option, player: &Arc, inventory: &Arc, title: TextComponent, hopper: bool, ) -> bool { - if player.is_spectator() && inventory.has_loot_table().await { + if player.is_spectator() && inventory.has_loot_table() { return false; } if !player.is_spectator() { - inventory.unpack_loot().await; + inventory.unpack_loot(); } player @@ -210,7 +221,6 @@ pub(super) async fn open( }, None, ) - .await .is_some() } @@ -266,8 +276,8 @@ mod tests { use pumpkin_nbt::compound::NbtCompound; use pumpkin_world::inventory::Inventory; - #[tokio::test] - async fn deferred_mineshaft_loot_is_preserved_until_unpacked() { + #[test] + fn deferred_mineshaft_loot_is_preserved_until_unpacked() { let inventory = std::sync::Arc::new(MinecartInventory::new(27)); let mut source = NbtCompound::new(); source.put_string( @@ -286,8 +296,8 @@ mod tests { assert_eq!(deferred.get_long("LootTableSeed"), Some(1234)); assert!(deferred.get_list("Items").is_none()); - inventory.unpack_loot().await; - assert!(!inventory.is_empty().await); + inventory.unpack_loot(); + assert!(!inventory.is_empty()); let mut unpacked = NbtCompound::new(); inventory.write_nbt(&mut unpacked); @@ -295,19 +305,17 @@ mod tests { assert!(unpacked.get_list("Items").is_some()); } - #[tokio::test] - async fn chest_minecart_items_round_trip_through_nbt() { + #[test] + fn chest_minecart_items_round_trip_through_nbt() { let inventory = MinecartInventory::new(27); - inventory - .set_stack(8, ItemStack::new(3, &Item::POWERED_RAIL)) - .await; + inventory.set_stack(8, ItemStack::new(3, &Item::POWERED_RAIL)); let mut nbt = NbtCompound::new(); inventory.write_nbt(&mut nbt); let restored = MinecartInventory::new(27); restored.read_nbt(&nbt); - let stack = restored.get_stack(8).await; + let stack = restored.get_stack(8); 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 0eb5af2cf..f3b7a4001 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/hopper.rs @@ -51,19 +51,17 @@ impl HopperMinecart { && let Some(source) = block_entity.get_inventory() { for slot in 0..source.size() { - let stack = source.get_stack(slot).await; + let stack = source.get_stack(slot); if stack.is_empty() || !source.can_transfer_to(inventory.as_ref(), slot, &stack) { continue; } let backup = stack.clone(); - let one = source.remove_stack_specific(slot, 1).await; - if HopperBlockEntity::add_one_item(source.as_ref(), inventory.as_ref(), one) - .await - { + let one = source.remove_stack_specific(slot, 1); + if HopperBlockEntity::add_one_item(source.as_ref(), inventory.as_ref(), &one) { return; } - source.set_stack(slot, backup).await; + source.set_stack(slot, backup); } return; } @@ -72,14 +70,14 @@ impl HopperMinecart { Vector3::new(pos.x - 0.5, pos.y + 0.6875, pos.z - 0.5), Vector3::new(pos.x + 0.5, pos.y + 2.0, pos.z + 0.5), ); - if Self::pick_up_item_internal(&world_clone, &inventory, &suction_box).await { + if Self::pick_up_item_internal(&world_clone, &inventory, &suction_box) { return; } - Self::pick_up_item_internal(&world_clone, &inventory, &cart_box).await; + Self::pick_up_item_internal(&world_clone, &inventory, &cart_box); }); } - async fn pick_up_item_internal( + fn pick_up_item_internal( world: &Arc, inventory: &Arc, search_box: &BoundingBox, @@ -98,7 +96,7 @@ impl HopperMinecart { } (stack.clone(), stack.split(1)) }; - if HopperBlockEntity::add_one_item(inventory.as_ref(), inventory.as_ref(), one).await { + if HopperBlockEntity::add_one_item(inventory.as_ref(), inventory.as_ref(), &one) { let is_empty = { let stack = item .get_item_stack() @@ -120,7 +118,7 @@ impl HopperMinecart { false } - pub(super) async fn interact( + pub(super) fn interact( &self, custom_name: Option, player: &Arc, @@ -136,7 +134,6 @@ impl HopperMinecart { ), true, ) - .await } pub(super) fn write_nbt(&self, nbt: &mut NbtCompound) { diff --git a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs index 223574401..15bbedce8 100644 --- a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs @@ -32,31 +32,27 @@ impl BedrockClient { slot_with_stack as usize, ) { if slot_with_stack as usize != target_hotbar_slot { - let target_stack = player.inventory().get_stack(target_hotbar_slot).await; - let source_stack = player.inventory().get_stack(slot_with_stack as usize).await; + let target_stack = player.inventory().get_stack(target_hotbar_slot); + let source_stack = player.inventory().get_stack(slot_with_stack as usize); player .inventory() - .set_stack(target_hotbar_slot, source_stack) - .await; + .set_stack(target_hotbar_slot, source_stack); player .inventory() - .set_stack(slot_with_stack as usize, target_stack) - .await; + .set_stack(slot_with_stack as usize, target_stack); } } else { - let target_stack = player.inventory().get_stack(target_hotbar_slot).await; - let source_stack = player.inventory().get_stack(slot_with_stack as usize).await; + let target_stack = player.inventory().get_stack(target_hotbar_slot); + let source_stack = player.inventory().get_stack(slot_with_stack as usize); player .inventory() - .set_stack(target_hotbar_slot, source_stack) - .await; + .set_stack(target_hotbar_slot, source_stack); player .inventory - .set_stack(slot_with_stack as usize, target_stack) - .await; + .set_stack(slot_with_stack as usize, target_stack); } } else if player.gamemode.load() == GameMode::Creative { - player.inventory.set_stack(target_hotbar_slot, stack).await; + player.inventory.set_stack(target_hotbar_slot, stack); } else { return; } @@ -80,9 +76,8 @@ impl BedrockClient { player .player_screen_handler .lock() - .await - .send_content_updates() - .await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); // Sync main hand equipment to other players let stack_in_hand = player.inventory().held_item(); diff --git a/crates/pumpkin/src/net/bedrock/play/container_close.rs b/crates/pumpkin/src/net/bedrock/play/container_close.rs index 6ec4cba80..102e7be11 100644 --- a/crates/pumpkin/src/net/bedrock/play/container_close.rs +++ b/crates/pumpkin/src/net/bedrock/play/container_close.rs @@ -6,7 +6,7 @@ impl BedrockClient { if packet.container_id == 0 || packet.container_id == 0xff { self.inventory_opened.store(false, Ordering::Relaxed); } - player.on_handled_screen_closed().await; + player.on_handled_screen_closed(); self.enqueue_client_packet(&SContainerClose { container_id: packet.container_id, diff --git a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs index 9b11ec13c..dfa83e91e 100644 --- a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs @@ -14,7 +14,10 @@ impl BedrockClient { let result = 0u8; if packet.actions.is_empty() && packet.legacy_request_id.0 != 0 { - let mut player_screen_handler = player.player_screen_handler.lock().await; + let mut player_screen_handler = player + .player_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for legacy_slot in &packet.legacy_set_item_slots { let mapped_window_id = match legacy_slot.container_id { 28 | 29 => 0, // HotBar or Inventory @@ -28,15 +31,13 @@ impl BedrockClient { { let current_stack = player_screen_handler .get_slot(screen_slot) - .get_cloned_stack() - .await; + .get_cloned_stack(); if !current_stack.is_empty() { player.drop_item(current_stack.clone()); player_screen_handler .get_slot(screen_slot) - .set_stack(ItemStack::EMPTY.clone()) - .await; + .set_stack(ItemStack::EMPTY.clone()); player_screen_handler .set_received_stack(screen_slot, ItemStack::EMPTY.clone()); @@ -57,7 +58,8 @@ impl BedrockClient { } } } - player_screen_handler.send_content_updates().await; + player_screen_handler.send_content_updates(); + drop(player_screen_handler); } for action in &packet.actions { @@ -75,28 +77,28 @@ impl BedrockClient { { let item_stack = descriptor_to_stack(&action.new_item); - let mut player_screen_handler = player.player_screen_handler.lock().await; + let mut player_screen_handler = player + .player_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let is_armor_equipped = player_screen_handler .get_slot(screen_slot) .get_stack() - .await .are_equal(&item_stack); if !is_armor_equipped { if (5..9).contains(&screen_slot) { - player - .enqueue_equipment_change( - &match screen_slot { - 5 => EquipmentSlot::HEAD, - 6 => EquipmentSlot::CHEST, - 7 => EquipmentSlot::LEGS, - 8 => EquipmentSlot::FEET, - _ => unreachable!(), - }, - &item_stack, - ) - .await; + player.enqueue_equipment_change( + &match screen_slot { + 5 => EquipmentSlot::HEAD, + 6 => EquipmentSlot::CHEST, + 7 => EquipmentSlot::LEGS, + 8 => EquipmentSlot::FEET, + _ => unreachable!(), + }, + &item_stack, + ); } else if (36..45).contains(&screen_slot) { let hotbar_slot = screen_slot - 36; if player.inventory().get_selected_slot() == hotbar_slot as u8 { @@ -108,10 +110,10 @@ impl BedrockClient { player_screen_handler .get_slot(screen_slot) - .set_stack(item_stack.clone()) - .await; + .set_stack(item_stack.clone()); player_screen_handler.set_received_stack(screen_slot, item_stack); - player_screen_handler.send_content_updates().await; + player_screen_handler.send_content_updates(); + drop(player_screen_handler); inventory_updated = true; } @@ -324,9 +326,7 @@ impl BedrockClient { !current_equipped.are_items_and_components_equal(&held) }; if should_change { - player - .enqueue_equipment_change(equippable.slot, &held) - .await; + player.enqueue_equipment_change(equippable.slot, &held); let inventory = player.inventory(); let mut equipment_guard = inventory diff --git a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs index a03b60264..acef30981 100644 --- a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs @@ -14,135 +14,364 @@ impl BedrockClient { }; use pumpkin_protocol::bedrock::server::item_stack_request::ItemStackRequestAction; - let current_screen_handler = player.current_screen_handler.lock().await.clone(); - let mut screen_handler = current_screen_handler.lock().await; + let (responses, inventory_updated) = { + let current_screen_handler = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut responses = Vec::with_capacity(packet.requests.len()); + let mut responses = Vec::with_capacity(packet.requests.len()); - for request in packet.requests { - let mut created_item: Option = None; - let mut crafting_inputs_consumed = false; - let mut updates = Vec::new(); - let mut result = 0u8; // 0 = Success, 1 = Error + for request in packet.requests { + let mut created_item: Option = None; + let mut crafting_inputs_consumed = false; + let mut updates = Vec::new(); + let mut result = 0u8; // 0 = Success, 1 = Error - for action in request.actions { - match action { - ItemStackRequestAction::CraftCreative { - creative_item_id, - repetitions, - } => { - let index = (creative_item_id.0.saturating_sub(1)) as usize; - if index < pumpkin_data::bedrock_creative::CREATIVE_ENTRIES.len() { - let entry = pumpkin_data::bedrock_creative::CREATIVE_ENTRIES[index]; - if let Some(mapping) = - pumpkin_data::item::JavaToBedrockItemMapping::from_bedrock( - entry.item_id, - entry.item_aux_value, - ) - { - // Bedrock `repetitions` represents how many stacks to create; use the item's max stack size - let max_stack = ItemStack::static_new_java(1, mapping.java_item) - .get_max_stack_size(); - let count = ((max_stack as u16) * (repetitions as u16)) - .min(u8::MAX as u16) - as u8; - created_item = Some(ItemStack::new(count, mapping.java_item)); - } else { - tracing::warn!( - "Failed to map bedrock item id {} and data {} to Java item", - entry.item_id, - entry.item_aux_value - ); - result = 1; - break; - } - } else { - tracing::warn!( - "Creative item index {} out of bounds (len: {})", - index, - pumpkin_data::bedrock_creative::CREATIVE_ENTRIES.len() - ); - result = 1; - break; - } - } - ItemStackRequestAction::Take { - count, - source, - destination, - } - | ItemStackRequestAction::Place { - count, - source, - destination, - } => { - let mut source_stack = - get_slot_stack(&*screen_handler, &source, created_item.as_ref()).await; - if source_stack.is_empty() && created_item.is_none() { - tracing::debug!("Source stack is empty in Take/Place"); - result = 1; - break; - } - let count = count.min(source_stack.item_count); - if count > 0 { - let mut dest_stack = get_slot_stack( - &*screen_handler, - &destination, - created_item.as_ref(), - ) - .await; - if dest_stack.is_empty() { - dest_stack = source_stack.copy_with_count(count); - } else if dest_stack.are_items_and_components_equal(&source_stack) { - dest_stack.item_count = dest_stack.item_count.saturating_add(count); - } else { - tracing::debug!( - "Destination stack is not compatible with source stack" - ); - result = 1; - break; - } - - let merchant_result = screen_handler.window_type() - == Some(pumpkin_data::screen::WindowType::Merchant) - && matches!( - source.container_name.container_name, - ContainerName::CreatedOutput - | ContainerName::TradeResultPreview - | ContainerName::Trade2ResultPreview - ); - if merchant_result { - if count != source_stack.item_count { + for action in request.actions { + match action { + ItemStackRequestAction::CraftCreative { + creative_item_id, + repetitions, + } => { + let index = (creative_item_id.0.saturating_sub(1)) as usize; + if index < pumpkin_data::bedrock_creative::CREATIVE_ENTRIES.len() { + let entry = pumpkin_data::bedrock_creative::CREATIVE_ENTRIES[index]; + if let Some(mapping) = + pumpkin_data::item::JavaToBedrockItemMapping::from_bedrock( + entry.item_id, + entry.item_aux_value, + ) + { + // Bedrock `repetitions` represents how many stacks to create; use the item's max stack size + let max_stack = + ItemStack::static_new_java(1, mapping.java_item) + .get_max_stack_size(); + let count = ((max_stack as u16) * (repetitions as u16)) + .min(u8::MAX as u16) + as u8; + created_item = Some(ItemStack::new(count, mapping.java_item)); + } else { + tracing::warn!( + "Failed to map bedrock item id {} and data {} to Java item", + entry.item_id, + entry.item_aux_value + ); result = 1; break; } - let Some(handler) = screen_handler + } else { + tracing::warn!( + "Creative item index {} out of bounds (len: {})", + index, + pumpkin_data::bedrock_creative::CREATIVE_ENTRIES.len() + ); + result = 1; + break; + } + } + ItemStackRequestAction::Take { + count, + source, + destination, + } + | ItemStackRequestAction::Place { + count, + source, + destination, + } => { + let mut source_stack = + get_slot_stack(&*screen_handler, &source, created_item.as_ref()); + if source_stack.is_empty() && created_item.is_none() { + tracing::debug!("Source stack is empty in Take/Place"); + result = 1; + break; + } + let count = count.min(source_stack.item_count); + if count > 0 { + let mut dest_stack = get_slot_stack( + &*screen_handler, + &destination, + created_item.as_ref(), + ); + if dest_stack.is_empty() { + dest_stack = source_stack.copy_with_count(count); + } else if dest_stack.are_items_and_components_equal(&source_stack) { + dest_stack.item_count = + dest_stack.item_count.saturating_add(count); + } else { + tracing::debug!( + "Destination stack is not compatible with source stack" + ); + result = 1; + break; + } + + let merchant_result = screen_handler.window_type() + == Some(pumpkin_data::screen::WindowType::Merchant) + && matches!( + source.container_name.container_name, + ContainerName::CreatedOutput + | ContainerName::TradeResultPreview + | ContainerName::Trade2ResultPreview + ); + if merchant_result { + if count != source_stack.item_count { + result = 1; + break; + } + let Some(handler) = screen_handler .as_any_mut() .downcast_mut::() else { result = 1; break; }; - if !handler.complete_bedrock_trade(player.as_ref()).await { - result = 1; - break; + if !handler.complete_bedrock_trade(player.as_ref()) { + result = 1; + break; + } + + update_slot_stack( + player, + handler, + &destination, + dest_stack.clone(), + ); + for (container_name, slot_id, screen_slot) in [ + (ContainerName::Trade2Ingredient1, 4, 0), + (ContainerName::Trade2Ingredient2, 5, 1), + (ContainerName::Trade2ResultPreview, 50, 2), + ] { + let stack = handler.get_behaviour().slots[screen_slot] + .get_cloned_stack(); + record_update( + &mut updates, + FullContainerName { + container_name, + dynamic_id: None, + }, + slot_id, + &stack, + ); + } + record_update( + &mut updates, + source.container_name.clone(), + source.slot_id, + ItemStack::EMPTY, + ); + record_update( + &mut updates, + destination.container_name.clone(), + destination.slot_id, + &dest_stack, + ); + continue; } + source_stack.decrement(count); + if source.container_name.container_name + == ContainerName::CreatedOutput + && let Some(ref mut stack) = created_item + { + stack.decrement(count); + if stack.is_empty() { + created_item = None; + } + } + let source_stack = if source_stack.is_empty() { + ItemStack::EMPTY.clone() + } else { + source_stack + }; + update_slot_stack( player, - handler, + &mut *screen_handler, + &source, + source_stack.clone(), + ); + update_slot_stack( + player, + &mut *screen_handler, &destination, dest_stack.clone(), + ); + + record_update( + &mut updates, + source.container_name.clone(), + source.slot_id, + &source_stack, + ); + record_update( + &mut updates, + destination.container_name.clone(), + destination.slot_id, + &dest_stack, + ); + } + } + ItemStackRequestAction::Swap { slot1, slot2 } => { + let stack1 = + get_slot_stack(&*screen_handler, &slot1, created_item.as_ref()); + let stack2 = + get_slot_stack(&*screen_handler, &slot2, created_item.as_ref()); + + update_slot_stack(player, &mut *screen_handler, &slot1, stack2.clone()); + update_slot_stack(player, &mut *screen_handler, &slot2, stack1.clone()); + + record_update( + &mut updates, + slot1.container_name.clone(), + slot1.slot_id, + &stack2, + ); + record_update( + &mut updates, + slot2.container_name.clone(), + slot2.slot_id, + &stack1, + ); + } + ItemStackRequestAction::Drop { + count, + source, + randomly: _, + } => { + let mut source_stack = + get_slot_stack(&*screen_handler, &source, created_item.as_ref()); + if source_stack.is_empty() { + result = 1; + break; + } + let count = count.min(source_stack.item_count); + if count > 0 { + let dropped_stack = source_stack.copy_with_count(count); + player.drop_item(dropped_stack); + + source_stack.decrement(count); + let source_stack = if source_stack.is_empty() { + ItemStack::EMPTY.clone() + } else { + source_stack + }; + + update_slot_stack( + player, + &mut *screen_handler, + &source, + source_stack.clone(), + ); + + record_update( + &mut updates, + source.container_name.clone(), + source.slot_id, + &source_stack, + ); + } + } + ItemStackRequestAction::Destroy { count, source } + | ItemStackRequestAction::Consume { count, source } => { + if screen_handler.window_type() + == Some(pumpkin_data::screen::WindowType::Merchant) + && matches!( + source.container_name.container_name, + ContainerName::TradeIngredient1 + | ContainerName::TradeIngredient2 + | ContainerName::Trade2Ingredient1 + | ContainerName::Trade2Ingredient2 ) - .await; + { + // MerchantScreenHandler consumes both payment slots atomically when + // Bedrock subsequently takes the CreatedOutput result. + continue; + } + if crafting_inputs_consumed + && source.container_name.container_name + == ContainerName::CraftingInput + { + let source_stack = get_slot_stack( + &*screen_handler, + &source, + created_item.as_ref(), + ); + record_update( + &mut updates, + source.container_name.clone(), + source.slot_id, + &source_stack, + ); + continue; + } + + let mut source_stack = + get_slot_stack(&*screen_handler, &source, created_item.as_ref()); + if source_stack.is_empty() { + result = 1; + break; + } + let count = count.min(source_stack.item_count); + if count > 0 { + source_stack.decrement(count); + let source_stack = if source_stack.is_empty() { + ItemStack::EMPTY.clone() + } else { + source_stack + }; + + update_slot_stack( + player, + &mut *screen_handler, + &source, + source_stack.clone(), + ); + + record_update( + &mut updates, + source.container_name.clone(), + source.slot_id, + &source_stack, + ); + } + } + ItemStackRequestAction::CraftRecipe { + recipe_id, + repetitions, + } + | ItemStackRequestAction::CraftRecipeAuto { + recipe_id, + repetitions, + } => { + if screen_handler.window_type() + == Some(pumpkin_data::screen::WindowType::Merchant) + { + let Some(handler) = screen_handler + .as_any_mut() + .downcast_mut::() + else { + result = 1; + break; + }; + let trade = recipe_id.0.saturating_sub(1) as usize; + if trade >= handler.offers.len() { + result = 1; + break; + } + handler.set_selected_offer(trade); for (container_name, slot_id, screen_slot) in [ (ContainerName::Trade2Ingredient1, 4, 0), (ContainerName::Trade2Ingredient2, 5, 1), (ContainerName::Trade2ResultPreview, 50, 2), ] { let stack = handler.get_behaviour().slots[screen_slot] - .get_cloned_stack() - .await; + .get_cloned_stack(); record_update( &mut updates, FullContainerName { @@ -153,365 +382,142 @@ impl BedrockClient { &stack, ); } - record_update( - &mut updates, - source.container_name.clone(), - source.slot_id, - ItemStack::EMPTY, - ); - record_update( - &mut updates, - destination.container_name.clone(), - destination.slot_id, - &dest_stack, - ); continue; } - source_stack.decrement(count); - if source.container_name.container_name == ContainerName::CreatedOutput - && let Some(ref mut stack) = created_item - { - stack.decrement(count); - if stack.is_empty() { - created_item = None; + if repetitions > 0 { + screen_handler.update_to_client(); + + let is_player = screen_handler.window_type().is_none(); + let grid_size = if is_player { 4 } else { 9 }; + let bedrock_grid_start = if is_player { 28 } else { 32 }; + for i in 0..grid_size { + let grid_slot_index = 1 + i; + let grid_slot = screen_handler.get_behaviour().slots + [grid_slot_index] + .clone(); + let grid_stack = grid_slot.get_cloned_stack(); + tracing::info!( + "Crafting Grid slot {i} (slot index {grid_slot_index}): Item ID: {}, Count: {}", + grid_stack.item.id, + grid_stack.item_count + ); + } + + let output_slot = screen_handler.get_behaviour().slots[0].clone(); + let output_stack = output_slot.get_cloned_stack(); + + if output_stack.is_empty() + || repetitions > output_slot.get_max_item_count() + { + tracing::warn!("Client sent an invalid crafting request"); + result = 1; + break; + } + + let mut total_crafted = output_stack.clone(); + total_crafted.item_count = + total_crafted.item_count.saturating_mul(repetitions); + created_item = Some(total_crafted); + + for _ in 0..repetitions { + output_slot.on_take_item(player.as_ref(), &output_stack); + } + crafting_inputs_consumed = true; + + // Record updates for all grid slots so Bedrock client is notified of consumed ingredients! + let is_player = screen_handler.window_type().is_none(); + let grid_size = if is_player { 4 } else { 9 }; + for i in 0..grid_size { + let grid_slot_index = 1 + i; + let grid_slot = screen_handler.get_behaviour().slots + [grid_slot_index] + .clone(); + let grid_stack = grid_slot.get_cloned_stack(); + record_update( + &mut updates, + FullContainerName { + container_name: ContainerName::CraftingInput, + dynamic_id: None, + }, + (bedrock_grid_start + i) as u8, + &grid_stack, + ); } } - let source_stack = if source_stack.is_empty() { - ItemStack::EMPTY.clone() - } else { - source_stack - }; - - update_slot_stack( - player, - &mut *screen_handler, - &source, - source_stack.clone(), - ) - .await; - update_slot_stack( - player, - &mut *screen_handler, - &destination, - dest_stack.clone(), - ) - .await; - - record_update( - &mut updates, - source.container_name.clone(), - source.slot_id, - &source_stack, - ); - record_update( - &mut updates, - destination.container_name.clone(), - destination.slot_id, - &dest_stack, - ); + } + ItemStackRequestAction::CraftResultsDeprecated { .. } + | ItemStackRequestAction::MineBlock { .. } + | ItemStackRequestAction::BeaconPayment { .. } + | ItemStackRequestAction::Create { .. } + | ItemStackRequestAction::LabTableCombine + | ItemStackRequestAction::Optional { .. } + | ItemStackRequestAction::Grindstone { .. } + | ItemStackRequestAction::Loom { .. } + | ItemStackRequestAction::CraftNonImplemented => { + // Successful no-ops to prevent client-side transaction rollbacks } } - ItemStackRequestAction::Swap { slot1, slot2 } => { - let stack1 = - get_slot_stack(&*screen_handler, &slot1, created_item.as_ref()).await; - let stack2 = - get_slot_stack(&*screen_handler, &slot2, created_item.as_ref()).await; + } - update_slot_stack(player, &mut *screen_handler, &slot1, stack2.clone()) - .await; - update_slot_stack(player, &mut *screen_handler, &slot2, stack1.clone()) - .await; - - record_update( - &mut updates, - slot1.container_name.clone(), - slot1.slot_id, - &stack2, + let mut container_infos = Vec::new(); + if result == 0 { + for update in updates { + let container_info = container_infos.iter_mut().find( + |info: &&mut ItemStackResponseContainerInfo| { + info.full_container_name == update.container_name + }, ); - record_update( - &mut updates, - slot2.container_name.clone(), - slot2.slot_id, - &stack1, - ); - } - ItemStackRequestAction::Drop { - count, - source, - randomly: _, - } => { - let mut source_stack = - get_slot_stack(&*screen_handler, &source, created_item.as_ref()).await; - if source_stack.is_empty() { - result = 1; - break; - } - let count = count.min(source_stack.item_count); - if count > 0 { - let dropped_stack = source_stack.copy_with_count(count); - player.drop_item(dropped_stack); - source_stack.decrement(count); - let source_stack = if source_stack.is_empty() { - ItemStack::EMPTY.clone() - } else { - source_stack - }; + let slot_info = ItemStackResponseSlotInfo { + requested_slot: update.slot_id, + slot: update.slot_id, + amount: update.count, + item_stack_net_id: update.stack_id, + custom_name: String::new(), + filtered_custom_name: String::new(), + durability_correction: VarInt(0), + }; - update_slot_stack( - player, - &mut *screen_handler, - &source, - source_stack.clone(), - ) - .await; - - record_update( - &mut updates, - source.container_name.clone(), - source.slot_id, - &source_stack, - ); + if let Some(info) = container_info { + info.slots.push(slot_info); + } else { + container_infos.push(ItemStackResponseContainerInfo { + full_container_name: update.container_name, + slots: vec![slot_info], + }); } } - ItemStackRequestAction::Destroy { count, source } - | ItemStackRequestAction::Consume { count, source } => { - if screen_handler.window_type() - == Some(pumpkin_data::screen::WindowType::Merchant) - && matches!( - source.container_name.container_name, - ContainerName::TradeIngredient1 - | ContainerName::TradeIngredient2 - | ContainerName::Trade2Ingredient1 - | ContainerName::Trade2Ingredient2 - ) + } + + responses.push(ItemStackResponseInfo { + result, + client_request_id: request.request_id, + containers: container_infos, + }); + } + + // Send updates to Java client + screen_handler.send_content_updates(); + + // Collect inventory updates if we modified player inventory + let mut inventory_updated = false; + for response in &responses { + if response.result == 0 { + for info in &response.containers { + if info.full_container_name.container_name == ContainerName::Inventory + || info.full_container_name.container_name + == ContainerName::CombinedHotBarAndInventory + || info.full_container_name.container_name == ContainerName::HotBar { - // MerchantScreenHandler consumes both payment slots atomically when - // Bedrock subsequently takes the CreatedOutput result. - continue; + inventory_updated = true; } - if crafting_inputs_consumed - && source.container_name.container_name == ContainerName::CraftingInput - { - let source_stack = - get_slot_stack(&*screen_handler, &source, created_item.as_ref()) - .await; - record_update( - &mut updates, - source.container_name.clone(), - source.slot_id, - &source_stack, - ); - continue; - } - - let mut source_stack = - get_slot_stack(&*screen_handler, &source, created_item.as_ref()).await; - if source_stack.is_empty() { - result = 1; - break; - } - let count = count.min(source_stack.item_count); - if count > 0 { - source_stack.decrement(count); - let source_stack = if source_stack.is_empty() { - ItemStack::EMPTY.clone() - } else { - source_stack - }; - - update_slot_stack( - player, - &mut *screen_handler, - &source, - source_stack.clone(), - ) - .await; - - record_update( - &mut updates, - source.container_name.clone(), - source.slot_id, - &source_stack, - ); - } - } - ItemStackRequestAction::CraftRecipe { - recipe_id, - repetitions, - } - | ItemStackRequestAction::CraftRecipeAuto { - recipe_id, - repetitions, - } => { - if screen_handler.window_type() - == Some(pumpkin_data::screen::WindowType::Merchant) - { - let Some(handler) = screen_handler - .as_any_mut() - .downcast_mut::() - else { - result = 1; - break; - }; - let trade = recipe_id.0.saturating_sub(1) as usize; - if trade >= handler.offers.len() { - result = 1; - break; - } - handler.set_selected_offer(trade).await; - for (container_name, slot_id, screen_slot) in [ - (ContainerName::Trade2Ingredient1, 4, 0), - (ContainerName::Trade2Ingredient2, 5, 1), - (ContainerName::Trade2ResultPreview, 50, 2), - ] { - let stack = handler.get_behaviour().slots[screen_slot] - .get_cloned_stack() - .await; - record_update( - &mut updates, - FullContainerName { - container_name, - dynamic_id: None, - }, - slot_id, - &stack, - ); - } - continue; - } - - if repetitions > 0 { - screen_handler.update_to_client().await; - - let is_player = screen_handler.window_type().is_none(); - let grid_size = if is_player { 4 } else { 9 }; - let bedrock_grid_start = if is_player { 28 } else { 32 }; - for i in 0..grid_size { - let grid_slot_index = 1 + i; - let grid_slot = - screen_handler.get_behaviour().slots[grid_slot_index].clone(); - let grid_stack = grid_slot.get_cloned_stack().await; - tracing::info!( - "Crafting Grid slot {i} (slot index {grid_slot_index}): Item ID: {}, Count: {}", - grid_stack.item.id, - grid_stack.item_count - ); - } - - let output_slot = screen_handler.get_behaviour().slots[0].clone(); - let output_stack = output_slot.get_cloned_stack().await; - - if output_stack.is_empty() - || repetitions > output_slot.get_max_item_count().await - { - tracing::warn!("Client sent an invalid crafting request"); - result = 1; - break; - } - - let mut total_crafted = output_stack.clone(); - total_crafted.item_count = - total_crafted.item_count.saturating_mul(repetitions); - created_item = Some(total_crafted); - - for _ in 0..repetitions { - output_slot - .on_take_item(player.as_ref(), &output_stack) - .await; - } - crafting_inputs_consumed = true; - - // Record updates for all grid slots so Bedrock client is notified of consumed ingredients! - let is_player = screen_handler.window_type().is_none(); - let grid_size = if is_player { 4 } else { 9 }; - for i in 0..grid_size { - let grid_slot_index = 1 + i; - let grid_slot = - screen_handler.get_behaviour().slots[grid_slot_index].clone(); - let grid_stack = grid_slot.get_cloned_stack().await; - record_update( - &mut updates, - FullContainerName { - container_name: ContainerName::CraftingInput, - dynamic_id: None, - }, - (bedrock_grid_start + i) as u8, - &grid_stack, - ); - } - } - } - ItemStackRequestAction::CraftResultsDeprecated { .. } - | ItemStackRequestAction::MineBlock { .. } - | ItemStackRequestAction::BeaconPayment { .. } - | ItemStackRequestAction::Create { .. } - | ItemStackRequestAction::LabTableCombine - | ItemStackRequestAction::Optional { .. } - | ItemStackRequestAction::Grindstone { .. } - | ItemStackRequestAction::Loom { .. } - | ItemStackRequestAction::CraftNonImplemented => { - // Successful no-ops to prevent client-side transaction rollbacks } } } - let mut container_infos = Vec::new(); - if result == 0 { - for update in updates { - let container_info = container_infos.iter_mut().find( - |info: &&mut ItemStackResponseContainerInfo| { - info.full_container_name == update.container_name - }, - ); - - let slot_info = ItemStackResponseSlotInfo { - requested_slot: update.slot_id, - slot: update.slot_id, - amount: update.count, - item_stack_net_id: update.stack_id, - custom_name: String::new(), - filtered_custom_name: String::new(), - durability_correction: VarInt(0), - }; - - if let Some(info) = container_info { - info.slots.push(slot_info); - } else { - container_infos.push(ItemStackResponseContainerInfo { - full_container_name: update.container_name, - slots: vec![slot_info], - }); - } - } - } - - responses.push(ItemStackResponseInfo { - result, - client_request_id: request.request_id, - containers: container_infos, - }); - } - - // Send updates to Java client - screen_handler.send_content_updates().await; - - // Collect inventory updates if we modified player inventory - let mut inventory_updated = false; - for response in &responses { - if response.result == 0 { - for info in &response.containers { - if info.full_container_name.container_name == ContainerName::Inventory - || info.full_container_name.container_name - == ContainerName::CombinedHotBarAndInventory - || info.full_container_name.container_name == ContainerName::HotBar - { - inventory_updated = true; - } - } - } - } + (responses, inventory_updated) + }; // Send Bedrock specific responses and updates self.enqueue_client_packet(&CItemStackResponse { responses }) @@ -833,7 +839,7 @@ pub(crate) fn record_update( } } -async fn get_slot_stack( +fn get_slot_stack( screen_handler: &dyn ScreenHandler, slot_info: &pumpkin_protocol::bedrock::server::item_stack_request::ItemStackRequestSlotInfo, created_item: Option<&ItemStack>, @@ -848,31 +854,33 @@ async fn get_slot_stack( .get_behaviour() .cursor_stack .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .clone(); } - if let Some(screen_slot) = map_bedrock_container_slot( + map_bedrock_container_slot( screen_handler, slot_info.container_name.container_name, slot_info.slot_id, - ) { - screen_handler.get_behaviour().slots[screen_slot] - .get_cloned_stack() - .await - } else { - ItemStack::EMPTY.clone() - } + ) + .map_or_else( + || ItemStack::EMPTY.clone(), + |screen_slot| screen_handler.get_behaviour().slots[screen_slot].get_cloned_stack(), + ) } #[allow(clippy::unreachable)] -async fn update_slot_stack( +fn update_slot_stack( player: &Player, screen_handler: &mut dyn ScreenHandler, slot_info: &pumpkin_protocol::bedrock::server::item_stack_request::ItemStackRequestSlotInfo, new_stack: ItemStack, ) { if slot_info.container_name.container_name == ContainerName::Cursor { - let mut cursor_lock = screen_handler.get_behaviour().cursor_stack.lock().await; + let mut cursor_lock = screen_handler + .get_behaviour() + .cursor_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); *cursor_lock = new_stack; return; } @@ -883,23 +891,20 @@ async fn update_slot_stack( ) { let is_player_screen = screen_handler.window_type().is_none(); if is_player_screen { - let current_stack = screen_handler.get_behaviour().slots[screen_slot] - .get_cloned_stack() - .await; + let current_stack = + screen_handler.get_behaviour().slots[screen_slot].get_cloned_stack(); if !current_stack.are_items_and_components_equal(&new_stack) { if (5..9).contains(&screen_slot) { - player - .enqueue_equipment_change( - &match screen_slot { - 5 => EquipmentSlot::HEAD, - 6 => EquipmentSlot::CHEST, - 7 => EquipmentSlot::LEGS, - 8 => EquipmentSlot::FEET, - _ => unreachable!(), - }, - &new_stack, - ) - .await; + player.enqueue_equipment_change( + &match screen_slot { + 5 => EquipmentSlot::HEAD, + 6 => EquipmentSlot::CHEST, + 7 => EquipmentSlot::LEGS, + 8 => EquipmentSlot::FEET, + _ => unreachable!(), + }, + &new_stack, + ); } else if (36..45).contains(&screen_slot) { let hotbar_slot = screen_slot - 36; if player.inventory().get_selected_slot() == hotbar_slot as u8 { @@ -910,9 +915,7 @@ async fn update_slot_stack( } } - screen_handler.get_behaviour().slots[screen_slot] - .set_stack(new_stack.clone()) - .await; + screen_handler.get_behaviour().slots[screen_slot].set_stack(new_stack.clone()); screen_handler.set_received_stack(screen_slot, new_stack); } } @@ -933,7 +936,7 @@ mod tests { Arc::new(Mutex::new(EntityEquipment::new())), Arc::new(build_equipment_slots()), )); - let handler = CraftingTableScreenHandler::new(1, &inventory, None).await; + let handler = CraftingTableScreenHandler::new(1, &inventory, None); assert_eq!( map_bedrock_container_slot(&handler, ContainerName::Inventory, 26), diff --git a/crates/pumpkin/src/net/java/mod.rs b/crates/pumpkin/src/net/java/mod.rs index 1ff95d9d3..e97d124c9 100644 --- a/crates/pumpkin/src/net/java/mod.rs +++ b/crates/pumpkin/src/net/java/mod.rs @@ -958,9 +958,10 @@ impl JavaClient { .await; } id if id == SContainerButtonClick::to_id(version) => { - player - .on_container_button_click(SContainerButtonClick::read(&mut payload, &version)?) - .await; + player.on_container_button_click(&SContainerButtonClick::read( + &mut payload, + &version, + )?); } id if id == SSetHeldItem::to_id(version) => { self.handle_set_held_item( @@ -1012,8 +1013,7 @@ impl JavaClient { player, server, SCloseContainer::read(&mut payload, &version)?, - ) - .await; + ); } id if id == SChunkBatch::to_id(version) => { self.handle_chunk_batch(player, SChunkBatch::read(&mut payload, &version)?) diff --git a/crates/pumpkin/src/net/java/play/client_command.rs b/crates/pumpkin/src/net/java/play/client_command.rs index 77a0c87e3..e95baee38 100644 --- a/crates/pumpkin/src/net/java/play/client_command.rs +++ b/crates/pumpkin/src/net/java/play/client_command.rs @@ -15,9 +15,14 @@ impl JavaClient { player.world().clone().respawn_player(player, false).await; { - let screen_handler = player.current_screen_handler.lock().await; - let mut screen_handler = screen_handler.lock().await; - screen_handler.sync_state().await; + let screen_handler = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut screen_handler = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + screen_handler.sync_state(); }; // Restore abilities based on gamemode after respawn diff --git a/crates/pumpkin/src/net/java/play/close_container.rs b/crates/pumpkin/src/net/java/play/close_container.rs index b661ab792..617576e5e 100644 --- a/crates/pumpkin/src/net/java/play/close_container.rs +++ b/crates/pumpkin/src/net/java/play/close_container.rs @@ -2,12 +2,12 @@ use super::*; impl JavaClient { - pub async fn handle_close_container( + pub fn handle_close_container( &self, player: &Arc, _server: &Server, _packet: SCloseContainer, ) { - player.on_handled_screen_closed().await; + player.on_handled_screen_closed(); } } diff --git a/crates/pumpkin/src/net/java/play/pick_item.rs b/crates/pumpkin/src/net/java/play/pick_item.rs index 832474b77..76791ca1e 100644 --- a/crates/pumpkin/src/net/java/play/pick_item.rs +++ b/crates/pumpkin/src/net/java/play/pick_item.rs @@ -46,9 +46,8 @@ impl JavaClient { player .player_screen_handler .lock() - .await - .send_content_updates() - .await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); } pub async fn handle_pick_item_from_entity( @@ -108,9 +107,8 @@ impl JavaClient { player .player_screen_handler .lock() - .await - .send_content_updates() - .await; + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); } } } diff --git a/crates/pumpkin/src/net/java/play/place_recipe.rs b/crates/pumpkin/src/net/java/play/place_recipe.rs index b03331d14..8e2a70ab2 100644 --- a/crates/pumpkin/src/net/java/play/place_recipe.rs +++ b/crates/pumpkin/src/net/java/play/place_recipe.rs @@ -42,11 +42,17 @@ impl JavaClient { }) .count(); let cooking_display_count = RECIPES_COOKING.len(); - let dynamic_recipes = server.recipe_manager.get_dynamic_recipes().await; + let dynamic_recipes = server.recipe_manager.get_dynamic_recipes(); let (grid_width, crafting_inv) = { - let screen_handler_arc = player.current_screen_handler.lock().await.clone(); - let handler = screen_handler_arc.lock().await; + let screen_handler_arc = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let grid_width: usize = match handler.window_type() { Some(WindowType::Crafting) => 3, None => 2, // player inventory 2x2 @@ -148,7 +154,7 @@ impl JavaClient { let recipe_matches = { let mut ok = true; for (idx, ing) in ingredient_slots.iter().enumerate() { - let stack = crafting_inv.get_stack(idx).await; + let stack = crafting_inv.get_stack(idx); match ing { None => { if !stack.is_empty() { @@ -172,7 +178,7 @@ impl JavaClient { let mut min = u8::MAX; for (idx, ing) in ingredient_slots.iter().enumerate() { if ing.is_some() { - let stack = crafting_inv.get_stack(idx).await; + let stack = crafting_inv.get_stack(idx); if !stack.is_empty() { min = min.min(stack.item_count); } @@ -185,9 +191,9 @@ impl JavaClient { // Always clear the grid first, returning items to inventory. for i in 0..grid_size { - let stack = crafting_inv.remove_stack(i).await; + let stack = crafting_inv.remove_stack(i); if !stack.is_empty() { - player.inventory.offer(stack, false, player.as_ref()).await; + player.inventory.offer(stack, false, player.as_ref()); } } @@ -195,7 +201,7 @@ impl JavaClient { let active_ingredients: Vec> = ingredient_slots.iter().flatten().copied().collect(); let amount_to_craft = if use_max { - compute_biggest_craftable(&active_ingredients, &player.inventory).await + compute_biggest_craftable(&active_ingredients, &player.inventory) } else if recipe_matches { current_min.saturating_add(1) } else { @@ -203,21 +209,35 @@ impl JavaClient { }; if amount_to_craft == 0 { - let screen_handler_arc = player.current_screen_handler.lock().await.clone(); - screen_handler_arc.lock().await.send_content_updates().await; + let screen_handler_arc = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); return; } // Fill each grid slot with exactly `amount_to_craft` matching items. for (idx, ing) in ingredient_slots.iter().enumerate() { let Some(ingredient) = ing else { continue }; - let taken = take_n_ingredient(&player.inventory, ingredient, amount_to_craft).await; + let taken = take_n_ingredient(&player.inventory, ingredient, amount_to_craft); if !taken.is_empty() { - crafting_inv.set_stack(idx, taken).await; + crafting_inv.set_stack(idx, taken); } } - let screen_handler_arc = player.current_screen_handler.lock().await.clone(); - screen_handler_arc.lock().await.send_content_updates().await; + let screen_handler_arc = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .send_content_updates(); } } diff --git a/crates/pumpkin/src/net/java/play/select_trade.rs b/crates/pumpkin/src/net/java/play/select_trade.rs index 3f695c6cb..9c018558f 100644 --- a/crates/pumpkin/src/net/java/play/select_trade.rs +++ b/crates/pumpkin/src/net/java/play/select_trade.rs @@ -14,8 +14,14 @@ impl JavaClient { return; } - let screen_handler = player.current_screen_handler.lock().await; - let mut screen_handler = screen_handler.lock().await; + let screen_handler = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); if !screen_handler.can_use(player.as_ref()) { return; } @@ -23,9 +29,7 @@ impl JavaClient { .as_any_mut() .downcast_mut::() { - merchant - .set_selected_offer(packet.selected_slot.0 as usize) - .await; + merchant.set_selected_offer(packet.selected_slot.0 as usize); } } } diff --git a/crates/pumpkin/src/net/java/play/set_beacon.rs b/crates/pumpkin/src/net/java/play/set_beacon.rs index 6086f26c5..599e32332 100644 --- a/crates/pumpkin/src/net/java/play/set_beacon.rs +++ b/crates/pumpkin/src/net/java/play/set_beacon.rs @@ -7,38 +7,76 @@ use pumpkin_world::inventory::Inventory; impl JavaClient { pub async fn handle_set_beacon(&self, player: &Arc, packet: &SSetBeacon) { - let screen_handler_lock = player.current_screen_handler.lock().await; - let mut screen_handler = screen_handler_lock.lock().await; + let is_valid = { + let screen_handler_lock = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let mut screen_handler = screen_handler_lock + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); - let Some(beacon_handler) = screen_handler - .as_any_mut() - .downcast_mut::() - else { - debug!( - "Player {} interacted with invalid menu (expected beacon)", - player.gameprofile.name - ); - return; + let Some(beacon_handler) = screen_handler + .as_any_mut() + .downcast_mut::() + else { + debug!( + "Player {} interacted with invalid menu (expected beacon)", + player.gameprofile.name + ); + return; + }; + + let Some(beacon_entity) = beacon_handler + .inventory + .as_any() + .downcast_ref::() + else { + return; + }; + + // Check if payment slot has an item + if beacon_entity + .payment + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_empty() + { + return; + } + + let levels = beacon_entity.levels.load(Ordering::Relaxed); + let primary_id = packet.primary_effect.map(|v| v.0); + let secondary_id = packet.secondary_effect.map(|v| v.0); + + if BeaconBlockEntity::validate_effects(primary_id, secondary_id, levels) { + beacon_entity + .primary_effect + .store(primary_id.unwrap_or(-1), Ordering::Relaxed); + beacon_entity + .secondary_effect + .store(secondary_id.unwrap_or(-1), Ordering::Relaxed); + + // Remove 1 item from payment slot + beacon_entity.remove_stack_specific(0, 1); + beacon_entity.mark_dirty(); + + screen_handler.sync_state(); + + info!( + "Player {} updated beacon effects: primary {:?}, secondary {:?}", + player.gameprofile.name, primary_id, secondary_id + ); + true + } else { + false + } }; - let Some(beacon_entity) = beacon_handler - .inventory - .as_any() - .downcast_ref::() - else { - return; - }; - - // Check if payment slot has an item - if beacon_entity.payment.lock().await.is_empty() { - return; - } - - let levels = beacon_entity.levels.load(Ordering::Relaxed); - let primary_id = packet.primary_effect.map(|v| v.0); - let secondary_id = packet.secondary_effect.map(|v| v.0); - - if !BeaconBlockEntity::validate_effects(primary_id, secondary_id, levels) { + if !is_valid { + let primary_id = packet.primary_effect.map(|v| v.0); + let secondary_id = packet.secondary_effect.map(|v| v.0); warn!( "Player {} tried to set invalid beacon effects: primary {:?}, secondary {:?}", player.gameprofile.name, primary_id, secondary_id @@ -48,25 +86,6 @@ impl JavaClient { &[], )) .await; - return; } - - beacon_entity - .primary_effect - .store(primary_id.unwrap_or(-1), Ordering::Relaxed); - beacon_entity - .secondary_effect - .store(secondary_id.unwrap_or(-1), Ordering::Relaxed); - - // Remove 1 item from payment slot - beacon_entity.remove_stack_specific(0, 1).await; - beacon_entity.mark_dirty(); - - screen_handler.sync_state().await; - - info!( - "Player {} updated beacon effects: primary {:?}, secondary {:?}", - player.gameprofile.name, primary_id, secondary_id - ); } } diff --git a/crates/pumpkin/src/net/java/play/set_creative_slot.rs b/crates/pumpkin/src/net/java/play/set_creative_slot.rs index f76a4bcba..a8950a056 100644 --- a/crates/pumpkin/src/net/java/play/set_creative_slot.rs +++ b/crates/pumpkin/src/net/java/play/set_creative_slot.rs @@ -36,30 +36,30 @@ impl JavaClient { item_stack.is_empty() || item_stack.item_count <= item_stack.get_max_stack_size(); if valid_slot && is_legal { - let mut player_screen_handler = player.player_screen_handler.lock().await; + let mut player_screen_handler = player + .player_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let is_armor_equipped = player_screen_handler .get_slot(packet.slot as usize) .get_stack() - .await .are_equal(&item_stack); if !is_armor_equipped { if (5..9).contains(&packet.slot) { - player - .enqueue_equipment_change( - &match packet.slot { - 5 => EquipmentSlot::HEAD, - 6 => EquipmentSlot::CHEST, - 7 => EquipmentSlot::LEGS, - 8 => EquipmentSlot::FEET, - _ => { - tracing::error!("Invalid armor slot: {}", packet.slot); - EquipmentSlot::HEAD - } - }, - &item_stack, - ) - .await; + player.enqueue_equipment_change( + &match packet.slot { + 5 => EquipmentSlot::HEAD, + 6 => EquipmentSlot::CHEST, + 7 => EquipmentSlot::LEGS, + 8 => EquipmentSlot::FEET, + _ => { + tracing::error!("Invalid armor slot: {}", packet.slot); + EquipmentSlot::HEAD + } + }, + &item_stack, + ); } else if (36..45).contains(&packet.slot) { let slot = packet.slot - 36; if player.inventory().get_selected_slot() == slot as u8 { @@ -71,10 +71,9 @@ impl JavaClient { player_screen_handler .get_slot(packet.slot as usize) - .set_stack(item_stack.clone()) - .await; + .set_stack(item_stack.clone()); player_screen_handler.set_received_stack(packet.slot as usize, item_stack); - player_screen_handler.send_content_updates().await; + player_screen_handler.send_content_updates(); drop(player_screen_handler); } else if is_negative && is_legal { // Item drop diff --git a/crates/pumpkin/src/net/java/play/use_item.rs b/crates/pumpkin/src/net/java/play/use_item.rs index 0deefb18f..1f3c4350e 100644 --- a/crates/pumpkin/src/net/java/play/use_item.rs +++ b/crates/pumpkin/src/net/java/play/use_item.rs @@ -58,8 +58,7 @@ impl JavaClient { PlayerInteractEvent::new(player, InteractAction::RightClickAir, &Block::AIR, None) }; let (item_for_use, stack_for_use) = (item_in_hand.item, item_in_hand.clone()); - self.prepare_hand_item_for_use(player, hand, &mut item_in_hand) - .await; + Self::prepare_hand_item_for_use(player, hand, &mut item_in_hand); if !self .should_continue_use_after_fish_event(server, player, hand, item_for_use) @@ -77,12 +76,7 @@ impl JavaClient { }} } - async fn prepare_hand_item_for_use( - &self, - player: &Arc, - hand: Hand, - held: &mut ItemStack, - ) { + fn prepare_hand_item_for_use(player: &Arc, hand: Hand, held: &mut ItemStack) { let inventory = player.inventory(); if let Some(cooldown) = held.get_use_cooldown() { @@ -135,7 +129,7 @@ impl JavaClient { return; } - player.enqueue_equipment_change(&slot, held).await; + player.enqueue_equipment_change(&slot, held); let equipped = if current_equipped.is_empty() { let equipped = held.clone(); diff --git a/crates/pumpkin/src/net/java/recipe_helper.rs b/crates/pumpkin/src/net/java/recipe_helper.rs index acd5d82a5..e79f50655 100644 --- a/crates/pumpkin/src/net/java/recipe_helper.rs +++ b/crates/pumpkin/src/net/java/recipe_helper.rs @@ -20,7 +20,7 @@ impl GenericIngredient<'_> { } } -pub async fn take_n_ingredient( +pub fn take_n_ingredient( inventory: &PlayerInventory, ingredient: &GenericIngredient<'_>, count: u8, @@ -51,7 +51,7 @@ pub async fn take_n_ingredient( result.unwrap_or_else(|| ItemStack::EMPTY.clone()) } -pub async fn compute_biggest_craftable( +pub fn compute_biggest_craftable( ingredients: &[GenericIngredient<'_>], inventory: &PlayerInventory, ) -> u8 { diff --git a/crates/pumpkin/src/plugin/api/gui.rs b/crates/pumpkin/src/plugin/api/gui.rs index cc25a3892..d89d32310 100644 --- a/crates/pumpkin/src/plugin/api/gui.rs +++ b/crates/pumpkin/src/plugin/api/gui.rs @@ -1,14 +1,12 @@ use std::any::Any; use std::sync::Arc; -use tokio::sync::RwLock; +use std::sync::RwLock; use pumpkin_data::{item_stack::ItemStack, screen::WindowType}; -use pumpkin_inventory::screen_handler::{ - InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, -}; +use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use pumpkin_inventory::slot::NormalSlot; use pumpkin_util::text::TextComponent; -use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; +use pumpkin_world::inventory::{Clearable, Inventory}; pub struct PluginGui { pub window_type: WindowType, @@ -32,74 +30,79 @@ impl PluginInventory { } impl Clearable for PluginInventory { - fn clear(&self) -> std::pin::Pin + Send + '_>> { - Box::pin(async move { - let mut slots = self.slots.write().await; - slots.fill_with(|| ItemStack::EMPTY.clone()); - }) + fn clear(&self) { + let mut slots = self + .slots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + slots.fill_with(|| ItemStack::EMPTY.clone()); } } impl Inventory for PluginInventory { fn size(&self) -> usize { - self.slots.blocking_read().len() + self.slots + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .len() } - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - let slots = self.slots.read().await; - slots.iter().all(ItemStack::is_empty) - }) + fn is_empty(&self) -> bool { + let slots = self + .slots + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + slots.iter().all(ItemStack::is_empty) } - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let slots = self.slots.read().await; - slots - .get(slot) - .cloned() - .unwrap_or_else(|| ItemStack::EMPTY.clone()) - }) + fn get_stack(&self, slot: usize) -> ItemStack { + let slots = self + .slots + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + slots + .get(slot) + .cloned() + .unwrap_or_else(|| ItemStack::EMPTY.clone()) } - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut slots = self.slots.write().await; - if slot < slots.len() { - std::mem::replace(&mut slots[slot], ItemStack::EMPTY.clone()) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack(&self, slot: usize) -> ItemStack { + let mut slots = self + .slots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < slots.len() { + std::mem::replace(&mut slots[slot], ItemStack::EMPTY.clone()) + } else { + ItemStack::EMPTY.clone() + } } - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut slots = self.slots.write().await; - if slot < slots.len() && !slots[slot].is_empty() && amount > 0 { - slots[slot].split(amount) - } else { - ItemStack::EMPTY.clone() - } - }) + fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { + let mut slots = self + .slots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < slots.len() && !slots[slot].is_empty() && amount > 0 { + slots[slot].split(amount) + } else { + ItemStack::EMPTY.clone() + } } - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - let mut slots = self.slots.write().await; - if slot < slots.len() { - slots[slot] = stack; - } - }) + fn set_stack(&self, slot: usize, stack: ItemStack) { + let mut slots = self + .slots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if slot < slots.len() { + slots[slot] = stack; + } } - fn on_open(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move {}) - } + fn on_open(&self) {} - fn on_close(&self) -> InventoryFuture<'_, ()> { - Box::pin(async move {}) - } + fn on_close(&self) {} fn as_any(&self) -> &dyn Any { self @@ -139,11 +142,9 @@ impl PluginScreenHandler { } impl ScreenHandler for PluginScreenHandler { - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) + fn on_closed(&mut self, player: &dyn InventoryPlayer) { + self.default_on_closed(player); + self.inventory.on_close(); } fn as_any(&self) -> &dyn Any { @@ -162,11 +163,7 @@ impl ScreenHandler for PluginScreenHandler { &mut self.behaviour } - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - _slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { ItemStack::EMPTY.clone() }) + fn quick_move(&mut self, _player: &dyn InventoryPlayer, _slot_index: i32) -> ItemStack { + ItemStack::EMPTY.clone() } } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs index f4a4404b1..fbbab43d5 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs @@ -333,7 +333,7 @@ impl HostContainerBlockEntity for PluginHostState { .get::(&Resource::new_own(res.rep())) .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; let inventory = container.provider.inventory.clone(); - Ok(inventory.is_empty().await) + Ok(inventory.is_empty()) } async fn get_stack( @@ -346,7 +346,7 @@ impl HostContainerBlockEntity for PluginHostState { .get::(&Resource::new_own(res.rep())) .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; let inventory = container.provider.inventory.clone(); - let stack = inventory.get_stack(slot as usize).await; + let stack = inventory.get_stack(slot as usize); if stack.is_empty() { Ok(None) } else { @@ -376,7 +376,7 @@ impl HostContainerBlockEntity for PluginHostState { None => pumpkin_data::item_stack::ItemStack::EMPTY.clone(), }; - inventory.set_stack(slot as usize, stack).await; + inventory.set_stack(slot as usize, stack); Ok(()) } @@ -390,7 +390,7 @@ impl HostContainerBlockEntity for PluginHostState { .get::(&Resource::new_own(res.rep())) .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; let inventory = container.provider.inventory.clone(); - let removed = inventory.remove_stack(slot as usize).await; + let removed = inventory.remove_stack(slot as usize); if removed.is_empty() { Ok(None) } else { @@ -406,7 +406,7 @@ impl HostContainerBlockEntity for PluginHostState { .get::(&Resource::new_own(res.rep())) .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; let inventory = container.provider.inventory.clone(); - inventory.clear().await; + inventory.clear(); Ok(()) } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/gui.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/gui.rs index d0b2e7f1e..4c0407f37 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/gui.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/gui.rs @@ -124,11 +124,17 @@ impl gui::HostGui for PluginHostState { slot: u32, item: Resource, ) -> wasmtime::Result<()> { - let gui = self.get_gui_res(&res)?.provider.lock().await; - let mut slots = gui.inventory.slots.write().await; - if (slot as usize) < slots.len() { + let item_stack = { let item_stack = self.get_item_stack(&item)?; - let item_stack = item_stack.lock().await.clone(); + item_stack.lock().await.clone() + }; + let gui = self.get_gui_res(&res)?.provider.lock().await; + let mut slots = gui + .inventory + .slots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if (slot as usize) < slots.len() { slots[slot as usize] = item_stack; } Ok(()) @@ -141,7 +147,11 @@ impl gui::HostGui for PluginHostState { ) -> wasmtime::Result>> { let stack = { let gui = self.get_gui_res(&res)?.provider.lock().await; - let slots = gui.inventory.slots.read().await; + let slots = gui + .inventory + .slots + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); if (slot as usize) < slots.len() { let stack = &slots[slot as usize]; if stack.is_empty() { @@ -191,7 +201,7 @@ impl gui::HostGui for PluginHostState { async fn clear_items(&mut self, res: Resource) -> wasmtime::Result<()> { use pumpkin_world::inventory::Clearable; let gui = self.get_gui_res(&res)?.provider.lock().await; - gui.inventory.clear().await; + gui.inventory.clear(); Ok(()) } 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 63c99acdd..aa54e1ae7 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 @@ -1,7 +1,6 @@ use pumpkin_protocol::bedrock::client::PackIdVersion; use std::sync::Arc; use std::sync::atomic::Ordering; -use tokio::sync::Mutex; use wasmtime::component::Resource; use crate::plugin::api::gui::PluginScreenHandler; @@ -1104,7 +1103,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { pumpkin_util::Hand::Left => PlayerInventory::OFF_HAND_SLOT, }; - player.inventory().set_stack(slot, stack.clone()).await; + player.inventory().set_stack(slot, stack.clone()); // Sync to client let stack_serializer = ItemStackSerializer::from(stack); @@ -1127,10 +1126,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { pumpkin_data::item_stack::ItemStack::EMPTY.clone() }; - player - .inventory() - .set_stack(slot as usize, stack.clone()) - .await; + player.inventory().set_stack(slot as usize, stack.clone()); // Sync to client let stack_serializer = ItemStackSerializer::from(stack); @@ -1146,7 +1142,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { slot: u8, ) -> wasmtime::Result>> { let player = player_from_resource(self, &player)?; - let stack = player.inventory().get_stack(slot as usize).await; + let stack = player.inventory().get_stack(slot as usize); if stack.is_empty() { Ok(None) } else { @@ -1163,7 +1159,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { ) -> wasmtime::Result>> { let player = player_from_resource(self, &player)?; let ec = player.ender_chest_inventory(); - let stack = ec.get_stack(slot as usize).await; + let stack = ec.get_stack(slot as usize); if stack.is_empty() { Ok(None) } else { @@ -1187,17 +1183,29 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { }; let ec = player.ender_chest_inventory(); - ec.set_stack(slot as usize, stack.clone()).await; + ec.set_stack(slot as usize, stack.clone()); // If the player currently has their ender chest screen open, sync slot - let screen_handler_arc = player.current_screen_handler.lock().await.clone(); - let handler = screen_handler_arc.lock().await; - if let Some(generic) = handler - .as_any() - .downcast_ref::() - && generic.inventory.as_any().is::() - { - let sync_id = handler.sync_id(); + let sync_id = { + let screen_handler_arc = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(generic) = handler + .as_any() + .downcast_ref::() + && generic.inventory.as_any().is::() + { + Some(handler.sync_id()) + } else { + None + } + }; + if let Some(sync_id) = sync_id { let stack_serializer = ItemStackSerializer::from(stack); let packet = CSetContainerSlot::new(sync_id as i8, 0, slot as i16, &stack_serializer); player.send_client_packet(&packet).await; @@ -1209,17 +1217,29 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn clear_ender_chest(&mut self, player: Resource) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; let ec = player.ender_chest_inventory(); - ec.clear().await; + ec.clear(); // If the player currently has their ender chest screen open, sync all slots - let screen_handler_arc = player.current_screen_handler.lock().await.clone(); - let handler = screen_handler_arc.lock().await; - if let Some(generic) = handler - .as_any() - .downcast_ref::() - && generic.inventory.as_any().is::() - { - let sync_id = handler.sync_id(); + let sync_id = { + let screen_handler_arc = player + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(); + let handler = screen_handler_arc + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(generic) = handler + .as_any() + .downcast_ref::() + && generic.inventory.as_any().is::() + { + Some(handler.sync_id()) + } else { + None + } + }; + if let Some(sync_id) = sync_id { let empty_serializer = ItemStackSerializer::from(pumpkin_data::item_stack::ItemStack::EMPTY.clone()); for slot in 0..27 { @@ -1234,7 +1254,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn open_ender_chest(&mut self, player: Resource) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.open_ender_chest().await; + player.open_ender_chest(); Ok(()) } @@ -2184,7 +2204,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { player.increment_screen_handler_sync_id(); let sync_id = player.screen_handler_sync_id.load(Ordering::Relaxed); - let screen_handler = Arc::new(Mutex::new(PluginScreenHandler::new( + let screen_handler: Arc< + std::sync::Mutex, + > = Arc::new(std::sync::Mutex::new(PluginScreenHandler::new( sync_id, gui.window_type, &gui.inventory, @@ -2192,9 +2214,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { gui.allow_put_items, ))); - player - .open_handled_screen_direct(screen_handler, gui.title.clone()) - .await; + player.open_handled_screen_direct(screen_handler, &gui.title); Ok(()) } diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs index fd7838816..5166dae96 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -716,7 +716,7 @@ impl Server { self; &mut PlayerLoginEvent::new(player.clone(), TextComponent::text("You have been kicked from the server")); 'after: { - player.screen_handler_sync_handler.store_player(player.clone()).await; + player.screen_handler_sync_handler.store_player(player.clone()); if world .add_player(&player) .is_ok() { diff --git a/crates/pumpkin/src/server/recipe.rs b/crates/pumpkin/src/server/recipe.rs index 8ae4f74ad..a8a09a644 100644 --- a/crates/pumpkin/src/server/recipe.rs +++ b/crates/pumpkin/src/server/recipe.rs @@ -1,7 +1,6 @@ use std::sync::RwLock; use pumpkin_inventory::crafting::recipe_provider::RecipeProvider; -use pumpkin_inventory::slot::BoxFuture; pub use pumpkin_protocol::codec::recipe::DynamicRecipe; pub struct RecipeManager { @@ -63,12 +62,10 @@ impl RecipeManager { } impl RecipeProvider for RecipeManager { - fn get_dynamic_recipes(&self) -> BoxFuture<'_, Vec> { - Box::pin(async move { - self.dynamic_recipes - .read() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone() - }) + fn get_dynamic_recipes(&self) -> Vec { + self.dynamic_recipes + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } } diff --git a/crates/pumpkin/src/world/loot.rs b/crates/pumpkin/src/world/loot.rs index cf3792ce0..8918a0819 100644 --- a/crates/pumpkin/src/world/loot.rs +++ b/crates/pumpkin/src/world/loot.rs @@ -769,7 +769,7 @@ pub fn generate_chest_loot( } /// Items are scattered randomly across the 27 chest slots. -pub async fn fill_chest_inventory( +pub fn fill_chest_inventory( inventory: &std::sync::Arc, table: &pumpkin_util::chest_loot_table::ChestLootTable, seed: i64, @@ -799,7 +799,7 @@ pub async fn fill_chest_inventory( let Some(slot) = available_slots.pop() else { break; }; - inventory.set_stack(slot, item).await; + inventory.set_stack(slot, item); } } diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index a1ce3d290..0978007c0 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -2712,9 +2712,7 @@ impl World { client.send_game_packet(data).await; } - player - .on_screen_handler_opened(player.player_screen_handler.clone()) - .await; + player.on_screen_handler_opened(&player.player_screen_handler); { let mut abilities = player @@ -3623,11 +3621,9 @@ impl World { player.send_abilities_update(); // Sync selected slot - player - .enqueue_set_held_item_packet(&CSetSelectedSlot::new( - player.get_inventory().get_selected_slot() as i8, - )) - .await; + player.enqueue_set_held_item_packet(&CSetSelectedSlot::new( + player.get_inventory().get_selected_slot() as i8, + )); if client.version.load() >= JavaMinecraftVersion::V_1_20_2 { // Start waiting for level chunks. Sets the "Loading Terrain" screen (Added in 1.20.2) @@ -3711,9 +3707,7 @@ impl World { } player.has_played_before.store(true, Ordering::Relaxed); - player - .on_screen_handler_opened(player.player_screen_handler.clone()) - .await; + player.on_screen_handler_opened(&player.player_screen_handler); player.send_active_effects(); player.breath_manager.send_air_supply(player); @@ -3727,7 +3721,7 @@ impl World { if let Ok(data) = java_client.serialize_packet(&settings_packet) { java_client.send_packet_now(data).await; } - let dynamic_recipes = server.recipe_manager.get_dynamic_recipes().await; + let dynamic_recipes = server.recipe_manager.get_dynamic_recipes(); let add_packet = CRecipeBookAdd::new(true, &dynamic_recipes); if let Ok(data) = java_client.serialize_packet(&add_packet) { java_client.send_packet_now(data).await; @@ -4199,7 +4193,7 @@ impl World { if !keep_inventory { player.set_experience(0, 0.0, 0); - player.inventory.clear().await; + player.inventory.clear(); } // Set entity position BEFORE loading chunks, so chunks load at the right location @@ -5188,10 +5182,7 @@ impl World { let players = self.players.load(); for player in players.iter() { if player.open_container_pos.load() == Some(*position) { - let player = player.clone(); - tokio::spawn(async move { - player.close_handled_screen().await; - }); + player.close_handled_screen(); } } } @@ -5229,7 +5220,6 @@ impl World { pub fn strike_lightning(self: &Arc, pos: Vector3, effect_only: bool) { use pumpkin_data::entity::EntityType; use uuid::Uuid; - let server_ref = self.server.upgrade(); if let Some(server_ref) = server_ref { let mut event = @@ -5263,7 +5253,7 @@ impl World { } /* ItemScatterer.java */ - pub async fn scatter_inventory( + pub fn scatter_inventory( self: &Arc, position: &BlockPos, inventory: &Arc, @@ -5273,7 +5263,7 @@ impl World { f64::from(position.0.x), f64::from(position.0.y), f64::from(position.0.z), - inventory.remove_stack(i).await, + inventory.remove_stack(i), ); } }