diff --git a/crates/pumpkin-inventory/src/generic_container_screen_handler.rs b/crates/pumpkin-inventory/src/generic_container_screen_handler.rs index 7673bf2f0..448eafc22 100644 --- a/crates/pumpkin-inventory/src/generic_container_screen_handler.rs +++ b/crates/pumpkin-inventory/src/generic_container_screen_handler.rs @@ -27,6 +27,7 @@ pub fn create_generic_9x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, + player: &dyn InventoryPlayer, ) -> GenericContainerScreenHandler { GenericContainerScreenHandler::new( WindowType::Generic9x3, @@ -35,6 +36,7 @@ pub fn create_generic_9x3( inventory, 3, 9, + player.is_spectator(), ) } @@ -45,6 +47,7 @@ pub fn create_generic_9x6( sync_id: u8, player_inventory: &Arc, inventory: Arc, + player: &dyn InventoryPlayer, ) -> GenericContainerScreenHandler { GenericContainerScreenHandler::new( WindowType::Generic9x6, @@ -53,6 +56,7 @@ pub fn create_generic_9x6( inventory, 6, 9, + player.is_spectator(), ) } @@ -63,6 +67,7 @@ pub fn create_generic_3x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, + player: &dyn InventoryPlayer, ) -> GenericContainerScreenHandler { GenericContainerScreenHandler::new( WindowType::Generic3x3, @@ -71,6 +76,7 @@ pub fn create_generic_3x3( inventory, 3, 3, + player.is_spectator(), ) } @@ -79,6 +85,7 @@ pub fn create_crafter_3x3( sync_id: u8, player_inventory: &Arc, inventory: Arc, + player: &dyn InventoryPlayer, ) -> GenericContainerScreenHandler { GenericContainerScreenHandler::new( WindowType::Crafter3x3, @@ -87,6 +94,7 @@ pub fn create_crafter_3x3( inventory, 3, 3, + player.is_spectator(), ) } @@ -97,6 +105,7 @@ pub fn create_hopper( sync_id: u8, player_inventory: &Arc, inventory: Arc, + player: &dyn InventoryPlayer, ) -> GenericContainerScreenHandler { GenericContainerScreenHandler::new( WindowType::Hopper, @@ -105,6 +114,7 @@ pub fn create_hopper( inventory, 1, 5, + player.is_spectator(), ) } @@ -119,6 +129,8 @@ pub struct GenericContainerScreenHandler { pub rows: u8, /// Number of columns in the container grid. pub columns: u8, + /// Whether the opener is in spectator mode. + pub is_spectator: bool, /// Core screen handler behavior (slots, sync ID, listeners). behaviour: ScreenHandlerBehaviour, } @@ -133,6 +145,7 @@ impl GenericContainerScreenHandler { /// - `inventory` - The container's inventory /// - `rows` - Number of rows in the container /// - `columns` - Number of columns in the container + /// - `is_spectator` - Whether the opener is a spectator fn new( screen_type: WindowType, sync_id: u8, @@ -140,16 +153,19 @@ impl GenericContainerScreenHandler { inventory: Arc, rows: u8, columns: u8, + is_spectator: bool, ) -> Self { let mut handler = Self { inventory, rows, columns, + is_spectator, behaviour: ScreenHandlerBehaviour::new(sync_id, Some(screen_type)), }; - // TODO: Add player entity as a parameter - handler.inventory.on_open(); + if !is_spectator { + handler.inventory.on_open(); + } handler.add_inventory_slots(); let player_inventory: Arc = player_inventory.clone(); @@ -190,7 +206,9 @@ impl ScreenHandler for GenericContainerScreenHandler { fn on_closed(&mut self, player: &dyn InventoryPlayer) { self.default_on_closed(player); - self.inventory.on_close(); + if !self.is_spectator && !player.is_spectator() { + self.inventory.on_close(); + } } /// Quick move logic for generic containers. diff --git a/crates/pumpkin-inventory/src/screen_handler.rs b/crates/pumpkin-inventory/src/screen_handler.rs index b1cfd0a20..7cef97f7f 100644 --- a/crates/pumpkin-inventory/src/screen_handler.rs +++ b/crates/pumpkin-inventory/src/screen_handler.rs @@ -135,6 +135,11 @@ pub trait InventoryPlayer: Send + Sync { /// Checks if the player is in creative mode. fn is_creative(&self) -> bool; + /// Checks if the player is in spectator mode. + fn is_spectator(&self) -> bool { + false + } + /// Gets the player's experience level. fn experience_level(&self) -> i32; diff --git a/crates/pumpkin/src/block/blocks/anvil.rs b/crates/pumpkin/src/block/blocks/anvil.rs index 6ecca285d..c887d8fb3 100644 --- a/crates/pumpkin/src/block/blocks/anvil.rs +++ b/crates/pumpkin/src/block/blocks/anvil.rs @@ -1,8 +1,8 @@ use crate::block::blocks::falling::FallingBlock; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, - PathComputationType, PlacedArgs, + BlockBehaviour, GetScreenHandlerFactoryArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, + OnPlaceArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs, }; use pumpkin_data::block_properties::WallTorchLikeProperties; @@ -40,17 +40,32 @@ impl AnvilBlock { impl BlockBehaviour for AnvilBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithAnvil as i32, - 1, - ); - args.player - .open_handled_screen(&AnvilScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithAnvil as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(AnvilScreenFactory)) + } + fn placed(&self, args: PlacedArgs<'_>) { FallingBlock::placed(&FallingBlock, args); } diff --git a/crates/pumpkin/src/block/blocks/barrel.rs b/crates/pumpkin/src/block/blocks/barrel.rs index e3f9a3c03..9bb35a4e4 100644 --- a/crates/pumpkin/src/block/blocks/barrel.rs +++ b/crates/pumpkin/src/block/blocks/barrel.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, PlacedArgs}; +use crate::block::{GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -28,9 +28,9 @@ impl ScreenHandlerFactory for BarrelScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()); + let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone(), player); let concrete_arc = Arc::new(Mutex::new(handler)); Some(concrete_arc as SharedScreenHandler) @@ -55,21 +55,34 @@ impl BlockBehaviour for BarrelBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::OpenBarrel as i32, 1, ); args.player - .open_handled_screen(&BarrelScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(BarrelScreenFactory(inventory))) + } + fn placed(&self, args: PlacedArgs<'_>) { let barrel_block_entity = BarrelBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(barrel_block_entity)); diff --git a/crates/pumpkin/src/block/blocks/beacon.rs b/crates/pumpkin/src/block/blocks/beacon.rs index 97e80c786..a7484b315 100644 --- a/crates/pumpkin/src/block/blocks/beacon.rs +++ b/crates/pumpkin/src/block/blocks/beacon.rs @@ -1,7 +1,6 @@ use std::sync::Arc; 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; @@ -13,7 +12,7 @@ use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::Inventory; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs}; +use crate::block::{BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs}; // Create the factory just like ChestScreenFactory struct BeaconScreenFactory(Arc); @@ -44,23 +43,34 @@ pub struct BeaconBlock; impl BlockBehaviour for BeaconBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - let block_entity = args.world.get_block_entity(args.position); + self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) + .map_or(BlockActionResult::Fail, |factory| { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithBeacon as i32, + 1, + ); - // Extract the inventory from the entity - let Some(inventory) = block_entity.and_then(BlockEntity::get_inventory) else { - return BlockActionResult::Fail; - }; + // Open the screen using the factory + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithBeacon as i32, - 1, - ); + BlockActionResult::Success + }) + } - // Open the screen using the factory - args.player - .open_handled_screen(&BeaconScreenFactory(inventory), Some(*args.position)); - - BlockActionResult::Success + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(BeaconScreenFactory(inventory))) } } diff --git a/crates/pumpkin/src/block/blocks/blast_furnace.rs b/crates/pumpkin/src/block/blocks/blast_furnace.rs index d51f40a63..099415ad3 100644 --- a/crates/pumpkin/src/block/blocks/blast_furnace.rs +++ b/crates/pumpkin/src/block/blocks/blast_furnace.rs @@ -18,8 +18,8 @@ use pumpkin_world::inventory::Inventory; use crate::{ block::{ - BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, + NormalUseArgs, OnPlaceArgs, PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -78,27 +78,39 @@ pub struct BlastFurnaceBlock; impl BlockBehaviour for BlastFurnaceBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InteractWithBlastFurnace as i32, 1, ); - let blasting_furnace_screen_factory = BlastingFurnaceScreenFactory::new( - inventory, - property_delegate, - experience_container, - ); args.player - .open_handled_screen(&blasting_furnace_screen_factory, Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.clone().get_inventory()?; + let property_delegate = block_entity.clone().to_property_delegate()?; + let experience_container = block_entity.to_experience_container()?; + Some(Box::new(BlastingFurnaceScreenFactory::new( + inventory, + property_delegate, + experience_container, + ))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = FurnaceLikeProperties::default(args.block); props.facing = args diff --git a/crates/pumpkin/src/block/blocks/brewing_stand.rs b/crates/pumpkin/src/block/blocks/brewing_stand.rs index 35f2c736d..ecfe2eca9 100644 --- a/crates/pumpkin/src/block/blocks/brewing_stand.rs +++ b/crates/pumpkin/src/block/blocks/brewing_stand.rs @@ -1,7 +1,9 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::block::{GetComparatorOutputArgs, PathComputationType, PlacedArgs}; +use crate::block::{ + GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, PathComputationType, PlacedArgs, +}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -48,22 +50,35 @@ pub struct BrewingStandBlock; impl BlockBehaviour for BrewingStandBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(pd) = block_entity.clone().to_property_delegate() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InteractWithBrewingstand as i32, 1, ); args.player - .open_handled_screen(&BrewingScreenFactory(inventory, pd), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.clone().get_inventory()?; + let pd = block_entity.to_property_delegate()?; + Some(Box::new(BrewingScreenFactory(inventory, pd))) + } + fn placed(&self, args: PlacedArgs<'_>) { let be = BrewingStandBlockEntity::new(*args.position); args.world.add_block_entity(Arc::new(be)); diff --git a/crates/pumpkin/src/block/blocks/cartography_table.rs b/crates/pumpkin/src/block/blocks/cartography_table.rs index 8e016f517..a57fbef07 100644 --- a/crates/pumpkin/src/block/blocks/cartography_table.rs +++ b/crates/pumpkin/src/block/blocks/cartography_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs}; +use crate::block::{BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::cartography_table_screen_handler::CartographyTableScreenHandler; @@ -17,16 +17,31 @@ pub struct CartographyTableBlock; impl BlockBehaviour for CartographyTableBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithCartographyTable as i32, - 1, - ); - args.player - .open_handled_screen(&CartographyTableScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithCartographyTable as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(CartographyTableScreenFactory)) + } } struct CartographyTableScreenFactory; diff --git a/crates/pumpkin/src/block/blocks/chests.rs b/crates/pumpkin/src/block/blocks/chests.rs index 43837d24a..94c77432d 100644 --- a/crates/pumpkin/src/block/blocks/chests.rs +++ b/crates/pumpkin/src/block/blocks/chests.rs @@ -23,8 +23,9 @@ use std::sync::Mutex; use crate::block::{ BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, - GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType, - PlacedArgs, PlayerPlacedArgs, RandomTickArgs, registry::BlockActionResult, + GetRedstonePowerArgs, GetScreenHandlerFactoryArgs, NormalUseArgs, OnPlaceArgs, + OnSyncedBlockEventArgs, PathComputationType, PlacedArgs, PlayerPlacedArgs, RandomTickArgs, + registry::BlockActionResult, }; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -39,12 +40,12 @@ impl ScreenHandlerFactory for ChestScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { let concrete_handler = if self.0.size() > 27 { - create_generic_9x6(sync_id, player_inventory, self.0.clone()) + create_generic_9x6(sync_id, player_inventory, self.0.clone(), player) } else { - create_generic_9x3(sync_id, player_inventory, self.0.clone()) + create_generic_9x3(sync_id, player_inventory, self.0.clone(), player) }; let concrete_arc = Arc::new(Mutex::new(concrete_handler)); @@ -165,32 +166,17 @@ fn get_chest_comparator_output(args: &GetComparatorOutputArgs<'_>) -> Option } } -fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { - let stat = if args.block.id == Block::TRAPPED_CHEST.id { - pumpkin_data::statistic::CustomStatistic::TriggerTrappedChest - } else { - pumpkin_data::statistic::CustomStatistic::OpenChest - }; - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - stat as i32, - 1, - ); +fn get_chest_screen_handler_factory( + args: GetScreenHandlerFactoryArgs<'_>, +) -> Option> { let state = args.world.get_block_state_id(args.position); let first_chest = args.world.get_block_entity(args.position); - // Spectators cannot open chests with a pending loot table. - // The loot is only generated on first open by a non-spectator. let player_is_spectator = args.player.gamemode.load() == GameMode::Spectator; - if player_is_spectator - && let Some(ref entity) = first_chest - && entity.has_loot_table() - { - return BlockActionResult::Success; - } // Unpack deferred loot table on first open (non-spectator only). - if let Some(ref entity) = first_chest + if !player_is_spectator + && let Some(ref entity) = first_chest && let Some((loot_key, seed)) = entity.take_loot_table() && let Some(table) = get_loot_table(&loot_key) && let Some(inv) = entity.clone().get_inventory() @@ -199,9 +185,7 @@ fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { inv.mark_dirty(); } - let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else { - return BlockActionResult::Fail; - }; + let first_inventory = first_chest.and_then(BlockEntity::get_inventory)?; let chest_props = ChestLikeProperties::from_state_id(state); let connected_towards = match chest_props.r#type { @@ -211,13 +195,13 @@ fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { }; if is_chest_blocked(args.world, args.position) { - return BlockActionResult::Success; + return None; } if let Some(direction) = connected_towards { let neighbor_pos = args.position.offset(direction.to_offset()); if is_chest_blocked(args.world, &neighbor_pos) { - return BlockActionResult::Success; + return None; } } @@ -237,8 +221,31 @@ fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { first_inventory }; - args.player - .open_handled_screen(&ChestScreenFactory(inventory), Some(*args.position)); + Some(Box::new(ChestScreenFactory(inventory))) +} + +fn normal_use_chest_impl(args: &NormalUseArgs<'_>) -> BlockActionResult { + let stat = if args.block.id == Block::TRAPPED_CHEST.id { + pumpkin_data::statistic::CustomStatistic::TriggerTrappedChest + } else { + pumpkin_data::statistic::CustomStatistic::OpenChest + }; + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + stat as i32, + 1, + ); + + if let Some(factory) = get_chest_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } @@ -293,6 +300,13 @@ impl BlockBehaviour for ChestBlock { normal_use_chest_impl(&args) } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + get_chest_screen_handler_factory(args) + } + fn broken(&self, args: BrokenArgs<'_>) { broken_chest_impl(&args); } @@ -365,6 +379,13 @@ impl BlockBehaviour for CopperChestBlock { normal_use_chest_impl(&args) } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + get_chest_screen_handler_factory(args) + } + fn broken(&self, args: BrokenArgs<'_>) { broken_chest_impl(&args); } @@ -428,6 +449,13 @@ impl BlockBehaviour for TrappedChestBlock { normal_use_chest_impl(&args) } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + get_chest_screen_handler_factory(args) + } + fn broken(&self, args: BrokenArgs<'_>) { broken_chest_impl(&args); } diff --git a/crates/pumpkin/src/block/blocks/crafting_table.rs b/crates/pumpkin/src/block/blocks/crafting_table.rs index ffd4b9efa..0605194c8 100644 --- a/crates/pumpkin/src/block/blocks/crafting_table.rs +++ b/crates/pumpkin/src/block/blocks/crafting_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs}; +use crate::block::{BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler; @@ -17,19 +17,32 @@ pub struct CraftingTableBlock; impl BlockBehaviour for CraftingTableBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithCraftingTable as i32, - 1, - ); - let recipe_manager = args.server.recipe_manager.clone(); - args.player.open_handled_screen( - &CraftingTableScreenFactory(recipe_manager), - Some(*args.position), - ); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithCraftingTable as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let recipe_manager = args.server.recipe_manager.clone(); + Some(Box::new(CraftingTableScreenFactory(recipe_manager))) + } } struct CraftingTableScreenFactory(Arc); diff --git a/crates/pumpkin/src/block/blocks/enchanting_table.rs b/crates/pumpkin/src/block/blocks/enchanting_table.rs index 505ad4fa5..b541cca3c 100644 --- a/crates/pumpkin/src/block/blocks/enchanting_table.rs +++ b/crates/pumpkin/src/block/blocks/enchanting_table.rs @@ -3,7 +3,9 @@ use std::sync::Mutex; use crate::block::entities::enchanting_table::EnchantingTableBlockEntity; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType, PlacedArgs}; +use crate::block::{ + BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs, PathComputationType, PlacedArgs, +}; use pumpkin_data::{Block, BlockState, BlockStateId, translation}; use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler; use pumpkin_inventory::player::player_inventory::PlayerInventory; @@ -25,6 +27,23 @@ impl BlockBehaviour for EnchantingTableBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } + BlockActionResult::Success + } + + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { let mut bookshelf_count = 0; for off_z in -1..=1 { @@ -70,14 +89,10 @@ impl BlockBehaviour for EnchantingTableBlock { let bookshelf_count = bookshelf_count.min(15); let seed = args.player.enchantment_seed(); - args.player.open_handled_screen( - &EnchantingTableScreenFactory { - bookshelf_count, - seed, - }, - Some(*args.position), - ); - BlockActionResult::Success + Some(Box::new(EnchantingTableScreenFactory { + bookshelf_count, + seed, + })) } fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool { diff --git a/crates/pumpkin/src/block/blocks/ender_chest.rs b/crates/pumpkin/src/block/blocks/ender_chest.rs index 1c9925b7c..5b4453424 100644 --- a/crates/pumpkin/src/block/blocks/ender_chest.rs +++ b/crates/pumpkin/src/block/blocks/ender_chest.rs @@ -3,8 +3,8 @@ use std::sync::Mutex; use crate::block::entities::ender_chest::EnderChestBlockEntity; use crate::block::{ - BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType, - PlacedArgs, registry::BlockActionResult, + BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs, OnPlaceArgs, + OnSyncedBlockEventArgs, PathComputationType, PlacedArgs, registry::BlockActionResult, }; use crate::world::World; use pumpkin_data::block_properties::LadderLikeProperties; @@ -30,12 +30,14 @@ impl ScreenHandlerFactory for EnderChestScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - if let Some(tracker) = &self.tracker { + if !player.is_spectator() + && let Some(tracker) = &self.tracker + { self.inventory.set_tracker(tracker.clone()); } - let handler = create_generic_9x3(sync_id, player_inventory, self.inventory.clone()); + let handler = create_generic_9x3(sync_id, player_inventory, self.inventory.clone(), player); let concrete_arc = Arc::new(Mutex::new(handler)); Some(concrete_arc as SharedScreenHandler) @@ -71,8 +73,32 @@ impl BlockBehaviour for EnderChestBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + // TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true); + } + + BlockActionResult::Success + } + + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { if is_chest_blocked(args.world, args.position) { - return BlockActionResult::Success; + return None; } let block_entity = if let Some(be) = args.world.get_block_entity(args.position) { @@ -83,28 +109,16 @@ impl BlockBehaviour for EnderChestBlock { be }; - if let Some(block_entity) = block_entity + let block_entity = block_entity .as_any() - .downcast_ref::() - { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32, - 1, - ); - let tracker = block_entity.get_tracker(); - 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); - } + .downcast_ref::()?; - BlockActionResult::Success + let tracker = block_entity.get_tracker(); + let inventory = args.player.ender_chest_inventory(); + Some(Box::new(EnderChestScreenFactory { + inventory: inventory.clone(), + tracker: Some(tracker), + })) } fn placed(&self, args: PlacedArgs<'_>) { diff --git a/crates/pumpkin/src/block/blocks/furnace.rs b/crates/pumpkin/src/block/blocks/furnace.rs index 2d2314397..0502835ab 100644 --- a/crates/pumpkin/src/block/blocks/furnace.rs +++ b/crates/pumpkin/src/block/blocks/furnace.rs @@ -17,8 +17,8 @@ use pumpkin_world::inventory::Inventory; use crate::{ block::{ - BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, + NormalUseArgs, OnPlaceArgs, PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -77,24 +77,39 @@ pub struct FurnaceBlock; impl BlockBehaviour for FurnaceBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InteractWithFurnace as i32, 1, ); - let furnace_screen_factory = - FurnaceScreenFactory::new(inventory, property_delegate, experience_container); args.player - .open_handled_screen(&furnace_screen_factory, Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.clone().get_inventory()?; + let property_delegate = block_entity.clone().to_property_delegate()?; + let experience_container = block_entity.to_experience_container()?; + Some(Box::new(FurnaceScreenFactory::new( + inventory, + property_delegate, + experience_container, + ))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = FurnaceLikeProperties::default(args.block); props.facing = args diff --git a/crates/pumpkin/src/block/blocks/grindstone.rs b/crates/pumpkin/src/block/blocks/grindstone.rs index a2f7197f0..abf940cac 100644 --- a/crates/pumpkin/src/block/blocks/grindstone.rs +++ b/crates/pumpkin/src/block/blocks/grindstone.rs @@ -19,8 +19,8 @@ use pumpkin_world::world::BlockAccessor; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, - PathComputationType, + BlockBehaviour, CanPlaceAtArgs, GetScreenHandlerFactoryArgs, GetStateForNeighborUpdateArgs, + NormalUseArgs, OnPlaceArgs, PathComputationType, }; use pumpkin_data::BlockState; @@ -31,17 +31,32 @@ pub struct GrindstoneBlock; impl BlockBehaviour for GrindstoneBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithGrindstone as i32, - 1, - ); - args.player - .open_handled_screen(&GrindstoneScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithGrindstone as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(GrindstoneScreenFactory)) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = GrindstoneLikeProperties::from_state_id(args.block.default_state.id); (props.face, props.facing) = diff --git a/crates/pumpkin/src/block/blocks/hopper.rs b/crates/pumpkin/src/block/blocks/hopper.rs index 98ce4aa4b..d1ce8ccce 100644 --- a/crates/pumpkin/src/block/blocks/hopper.rs +++ b/crates/pumpkin/src/block/blocks/hopper.rs @@ -3,7 +3,8 @@ use std::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::{ - GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, PlacedArgs, + GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, OnNeighborUpdateArgs, OnPlaceArgs, + PathComputationType, PlacedArgs, }; use crate::block::{ registry::BlockActionResult, @@ -32,9 +33,9 @@ impl ScreenHandlerFactory for HopperBlockScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone()); + let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone(), player); let concrete_arc = Arc::new(Mutex::new(concrete_handler)); Some(concrete_arc as SharedScreenHandler) @@ -55,21 +56,34 @@ type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties impl BlockBehaviour for HopperBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InspectHopper as i32, 1, ); args.player - .open_handled_screen(&HopperBlockScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(HopperBlockScreenFactory(inventory))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = HopperLikeProperties::default(args.block); props.facing = match args.direction { diff --git a/crates/pumpkin/src/block/blocks/lectern.rs b/crates/pumpkin/src/block/blocks/lectern.rs index 01f118f1a..72b44ac2a 100644 --- a/crates/pumpkin/src/block/blocks/lectern.rs +++ b/crates/pumpkin/src/block/blocks/lectern.rs @@ -5,8 +5,8 @@ use crate::block::entities::lectern::LecternBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, - GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, - PathComputationType, PlacedArgs, UseWithItemArgs, + GetRedstonePowerArgs, GetScreenHandlerFactoryArgs, NormalUseArgs, OnPlaceArgs, + OnScheduledTickArgs, OnStateReplacedArgs, PathComputationType, PlacedArgs, UseWithItemArgs, }; use crate::entity::Entity; use crate::entity::item::ItemEntity; @@ -162,39 +162,50 @@ impl BlockBehaviour for LecternBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) + .map_or(BlockActionResult::Pass, |factory| { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithLectern as i32, + 1, + ); + + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + + BlockActionResult::Success + }) + } + + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { let props = LecternLikeProperties::from_state_id(args.world.get_block_state(args.position).id); if !props.has_book { - return BlockActionResult::Pass; + return None; } - let Some(block_entity) = args.world.get_block_entity(args.position) else { - return BlockActionResult::Pass; - }; - let Some(inventory) = block_entity.get_inventory() else { - return BlockActionResult::Pass; - }; - - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithLectern as i32, - 1, - ); + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; let controller = Arc::new(LecternPageController { world: args.world.clone(), position: *args.position, inventory: inventory.clone(), }); - args.player.open_handled_screen( - &LecternScreenFactory { - inventory, - controller, - }, - Some(*args.position), - ); - BlockActionResult::Success + Some(Box::new(LecternScreenFactory { + inventory, + controller, + })) } fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { diff --git a/crates/pumpkin/src/block/blocks/loom.rs b/crates/pumpkin/src/block/blocks/loom.rs index e7f56b176..f3ef04d36 100644 --- a/crates/pumpkin/src/block/blocks/loom.rs +++ b/crates/pumpkin/src/block/blocks/loom.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs, OnPlaceArgs}; use crate::entity::EntityBase; use pumpkin_data::block_properties::WallTorchLikeProperties; @@ -34,16 +34,31 @@ impl BlockBehaviour for LoomBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithLoom as i32, - 1, - ); - args.player - .open_handled_screen(&LoomScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithLoom as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(LoomScreenFactory)) + } } struct LoomScreenFactory; diff --git a/crates/pumpkin/src/block/blocks/redstone/crafter.rs b/crates/pumpkin/src/block/blocks/redstone/crafter.rs index 3a29f7f40..ac99442e1 100644 --- a/crates/pumpkin/src/block/blocks/redstone/crafter.rs +++ b/crates/pumpkin/src/block/blocks/redstone/crafter.rs @@ -7,8 +7,8 @@ use crate::block::entities::crafter::CrafterBlockEntity; use crate::block::entities::hopper::HopperBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, NormalUseArgs, + OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::entity::Entity; use crate::entity::item::ItemEntity; @@ -42,9 +42,9 @@ impl ScreenHandlerFactory for CrafterScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let handler = create_crafter_3x3(sync_id, player_inventory, self.0.clone()); + let handler = create_crafter_3x3(sync_id, player_inventory, self.0.clone(), player); let screen_handler_arc = Arc::new(Mutex::new(handler)); Some(screen_handler_arc as SharedScreenHandler) @@ -221,15 +221,28 @@ impl CrafterBlock { impl BlockBehaviour for CrafterBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player - .open_handled_screen(&CrafterScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(CrafterScreenFactory(inventory))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = CrafterLikeProperties::default(args.block); let facing = args.direction; diff --git a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs index f00edd104..b2e380e5c 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -8,8 +8,8 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::blocks::tnt::TNTBlock; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, NormalUseArgs, + OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::entity::decoration::armor_stand::ArmorStandEntity; use crate::entity::item::ItemEntity; @@ -69,9 +69,9 @@ impl ScreenHandlerFactory for DispenserScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()); + let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone(), player); let screen_handler_arc = Arc::new(Mutex::new(handler)); Some(screen_handler_arc as SharedScreenHandler) @@ -124,20 +124,33 @@ const fn to_data3d(facing: Facing) -> i32 { impl BlockBehaviour for DispenserBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InspectDispenser as i32, 1, ); args.player - .open_handled_screen(&DispenserScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(DispenserScreenFactory(inventory))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = DispenserLikeProperties::default(args.block); props.facing = args.player.get_entity().get_facing().opposite(); diff --git a/crates/pumpkin/src/block/blocks/redstone/dropper.rs b/crates/pumpkin/src/block/blocks/redstone/dropper.rs index 2ae199cfd..e08377259 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dropper.rs @@ -5,8 +5,8 @@ use std::sync::Mutex; use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, PlacedArgs, + BlockBehaviour, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, NormalUseArgs, + OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::entity::item::ItemEntity; use crate::entity::{Entity, EntityBase}; @@ -37,9 +37,9 @@ impl ScreenHandlerFactory for DropperScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()); + let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone(), player); let screen_handler_arc = Arc::new(Mutex::new(handler)); Some(screen_handler_arc as SharedScreenHandler) @@ -86,20 +86,33 @@ const fn to_data3d(facing: Facing) -> i32 { impl BlockBehaviour for DropperBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InspectDropper as i32, 1, ); args.player - .open_handled_screen(&DropperScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(DropperScreenFactory(inventory))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = DispenserLikeProperties::default(args.block); props.facing = args.player.get_entity().get_facing().opposite(); diff --git a/crates/pumpkin/src/block/blocks/shulker_box.rs b/crates/pumpkin/src/block/blocks/shulker_box.rs index 0d6184400..05733d723 100644 --- a/crates/pumpkin/src/block/blocks/shulker_box.rs +++ b/crates/pumpkin/src/block/blocks/shulker_box.rs @@ -1,7 +1,10 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::block::{GetComparatorOutputArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs}; +use crate::block::{ + GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, OnPlaceArgs, OnSyncedBlockEventArgs, + PlacedArgs, +}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -26,9 +29,9 @@ impl ScreenHandlerFactory for ShulkerBoxScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { - let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()); + let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone(), player); let screen_handler_arc = Arc::new(Mutex::new(handler)); Some(screen_handler_arc as SharedScreenHandler) @@ -68,21 +71,34 @@ impl BlockBehaviour for ShulkerBoxBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.get_inventory() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::OpenShulkerBox as i32, 1, ); args.player - .open_handled_screen(&ShulkerBoxScreenFactory(inventory), Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.get_inventory()?; + Some(Box::new(ShulkerBoxScreenFactory(inventory))) + } + fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(inventory) = block_entity.get_inventory() diff --git a/crates/pumpkin/src/block/blocks/smithing_table.rs b/crates/pumpkin/src/block/blocks/smithing_table.rs index 01fb069d8..84f517f40 100644 --- a/crates/pumpkin/src/block/blocks/smithing_table.rs +++ b/crates/pumpkin/src/block/blocks/smithing_table.rs @@ -1,5 +1,5 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs}; +use crate::block::{BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs}; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; @@ -17,16 +17,31 @@ pub struct SmithingTableBlock; impl BlockBehaviour for SmithingTableBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithSmithingTable as i32, - 1, - ); - args.player - .open_handled_screen(&SmithingTableScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithSmithingTable as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(SmithingTableScreenFactory)) + } } struct SmithingTableScreenFactory; diff --git a/crates/pumpkin/src/block/blocks/smoker.rs b/crates/pumpkin/src/block/blocks/smoker.rs index 9cfe8b442..e9e07eacb 100644 --- a/crates/pumpkin/src/block/blocks/smoker.rs +++ b/crates/pumpkin/src/block/blocks/smoker.rs @@ -17,8 +17,8 @@ use pumpkin_world::inventory::Inventory; use crate::{ block::{ - BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - PlacedArgs, registry::BlockActionResult, + BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, + NormalUseArgs, OnPlaceArgs, PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; @@ -76,24 +76,39 @@ pub struct SmokerBlock; impl BlockBehaviour for SmokerBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(inventory) = block_entity.clone().get_inventory() - && let Some(property_delegate) = block_entity.clone().to_property_delegate() - && let Some(experience_container) = block_entity.to_experience_container() - { + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InteractWithSmoker as i32, 1, ); - let smoker_screen_factory = - SmokerScreenFactory::new(inventory, property_delegate, experience_container); args.player - .open_handled_screen(&smoker_screen_factory, Some(*args.position)); + .open_handled_screen(factory.as_ref(), Some(*args.position)); } crate::block::registry::BlockActionResult::Consume } + fn get_screen_handler_factory( + &self, + args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + let block_entity = args.world.get_block_entity(args.position)?; + let inventory = block_entity.clone().get_inventory()?; + let property_delegate = block_entity.clone().to_property_delegate()?; + let experience_container = block_entity.to_experience_container()?; + Some(Box::new(SmokerScreenFactory::new( + inventory, + property_delegate, + experience_container, + ))) + } + fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut props = FurnaceLikeProperties::default(args.block); props.facing = args diff --git a/crates/pumpkin/src/block/blocks/stonecutter.rs b/crates/pumpkin/src/block/blocks/stonecutter.rs index cb5a4d043..fb22ac910 100644 --- a/crates/pumpkin/src/block/blocks/stonecutter.rs +++ b/crates/pumpkin/src/block/blocks/stonecutter.rs @@ -1,5 +1,7 @@ use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs, OnPlaceArgs, PathComputationType}; +use crate::block::{ + BlockBehaviour, GetScreenHandlerFactoryArgs, NormalUseArgs, OnPlaceArgs, PathComputationType, +}; use pumpkin_data::block_properties::WallTorchLikeProperties; use pumpkin_data::{BlockState, BlockStateId, translation}; @@ -30,17 +32,32 @@ impl BlockBehaviour for StonecutterBlock { } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - args.player.increment_stat( - pumpkin_data::statistic::StatisticCategory::Custom, - pumpkin_data::statistic::CustomStatistic::InteractWithStonecutter as i32, - 1, - ); - args.player - .open_handled_screen(&StonecutterScreenFactory, Some(*args.position)); + if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + }) { + args.player.increment_stat( + pumpkin_data::statistic::StatisticCategory::Custom, + pumpkin_data::statistic::CustomStatistic::InteractWithStonecutter as i32, + 1, + ); + args.player + .open_handled_screen(factory.as_ref(), Some(*args.position)); + } BlockActionResult::Success } + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + Some(Box::new(StonecutterScreenFactory)) + } + fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool { false } diff --git a/crates/pumpkin/src/block/mod.rs b/crates/pumpkin/src/block/mod.rs index 995af2484..026f1b03e 100644 --- a/crates/pumpkin/src/block/mod.rs +++ b/crates/pumpkin/src/block/mod.rs @@ -25,6 +25,7 @@ use pumpkin_data::BlockDirection; use pumpkin_data::block_rotation::{Mirror, Rotation}; use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::item_stack::ItemStack; +use pumpkin_inventory::screen_handler::ScreenHandlerFactory; use pumpkin_protocol::java::server::play::SUseItemOn; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::vector3::Vector3; @@ -85,6 +86,13 @@ pub trait BlockBehaviour: Send + Sync { BlockActionResult::Pass } + fn get_screen_handler_factory( + &self, + _args: GetScreenHandlerFactoryArgs<'_>, + ) -> Option> { + None + } + fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { BlockActionResult::PassToDefaultBlockAction } @@ -224,6 +232,15 @@ pub struct NormalUseArgs<'a> { pub hit: &'a BlockHitResult<'a>, } +#[derive(Clone, Copy)] +pub struct GetScreenHandlerFactoryArgs<'a> { + pub server: &'a Server, + pub world: &'a Arc, + pub block: &'a Block, + pub position: &'a BlockPos, + pub player: &'a Arc, +} + pub struct UseWithItemArgs<'a> { pub server: &'a Server, pub world: &'a Arc, diff --git a/crates/pumpkin/src/block/registry.rs b/crates/pumpkin/src/block/registry.rs index 05ec98816..6f95ad39d 100644 --- a/crates/pumpkin/src/block/registry.rs +++ b/crates/pumpkin/src/block/registry.rs @@ -171,9 +171,9 @@ use crate::block::fluid::lava::FlowingLava; use crate::block::fluid::water::FlowingWater; use crate::block::{ BlockBehaviour, BlockHitResult, BlockMetadata, BonemealArgs, FluidMetadata, - GetInsideCollisionShapeArgs, OnEntityCollisionArgs, OnEntityStepArgs, OnLandedUponArgs, - OnProjectileHitArgs, PathComputationType, UpdateEntityMovementAfterFallOnArgs, - stop_vertical_movement_after_fall, + GetInsideCollisionShapeArgs, GetScreenHandlerFactoryArgs, OnEntityCollisionArgs, + OnEntityStepArgs, OnLandedUponArgs, OnProjectileHitArgs, PathComputationType, + UpdateEntityMovementAfterFallOnArgs, stop_vertical_movement_after_fall, }; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -187,6 +187,7 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::{Block, BlockDirection, BlockId, BlockState}; +use pumpkin_inventory::screen_handler::ScreenHandlerFactory; use pumpkin_protocol::java::server::play::SUseItemOn; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::position::BlockPos; @@ -905,6 +906,27 @@ impl BlockRegistry { BlockActionResult::Pass } + pub fn get_screen_handler_factory( + &self, + block: &Block, + player: &Arc, + position: &BlockPos, + server: &Server, + world: &Arc, + ) -> Option> { + let pumpkin_block = self.get_pumpkin_block(block.id); + if let Some(pumpkin_block) = pumpkin_block { + return pumpkin_block.get_screen_handler_factory(GetScreenHandlerFactoryArgs { + server, + world, + block, + position, + player, + }); + } + None + } + pub fn explode(&self, block: &Block, world: &Arc, position: &BlockPos) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index dd1acfa44..5b2b5d73d 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -7135,6 +7135,10 @@ impl InventoryPlayer for Player { self.gamemode.load() == GameMode::Creative } + fn is_spectator(&self) -> bool { + self.gamemode.load() == GameMode::Spectator + } + fn experience_level(&self) -> i32 { self.experience_level .load(std::sync::atomic::Ordering::Relaxed) diff --git a/crates/pumpkin/src/entity/vehicle/minecart/container.rs b/crates/pumpkin/src/entity/vehicle/minecart/container.rs index 533bc74e8..b7ccc1c48 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/container.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/container.rs @@ -182,13 +182,13 @@ impl ScreenHandlerFactory for MinecartScreenFactory { &self, sync_id: u8, player_inventory: &Arc, - _player: &dyn InventoryPlayer, + player: &dyn InventoryPlayer, ) -> Option { let inventory: Arc = self.inventory.clone(); let handler = if self.hopper { - create_hopper(sync_id, player_inventory, inventory) + create_hopper(sync_id, player_inventory, inventory, player) } else { - create_generic_9x3(sync_id, player_inventory, inventory) + create_generic_9x3(sync_id, player_inventory, inventory, player) }; Some(Arc::new(Mutex::new(handler)) as SharedScreenHandler) } diff --git a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs index e80d57166..107a8a7ed 100644 --- a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs @@ -159,7 +159,15 @@ impl BedrockClient { }; if player.gamemode.load() == GameMode::Spectator { - // TODO: openMenu ? + if let Some(factory) = server.block_registry.get_screen_handler_factory( + block, + player, + &data.block_position, + &server, + &world, + ) { + player.open_handled_screen(factory.as_ref(), Some(data.block_position)); + } return; } diff --git a/crates/pumpkin/src/net/java/play/use_item_on.rs b/crates/pumpkin/src/net/java/play/use_item_on.rs index a287dfc00..e79a02235 100644 --- a/crates/pumpkin/src/net/java/play/use_item_on.rs +++ b/crates/pumpkin/src/net/java/play/use_item_on.rs @@ -34,7 +34,36 @@ impl JavaClient { }; if player.gamemode.load() == GameMode::Spectator { - // TODO: openMenu + let entity = &player.get_entity(); + let world = entity.world.load_full(); + let block = world.get_block(&position); + + let event = PlayerInteractEvent::new( + player, + InteractAction::RightClickBlock, + block, + Some(position), + ); + + send_cancellable_blocking! {{ + server; + event; + 'cancelled: { + let state_id = world.get_block_state_id(&position); + player.try_send_client_packet(&CBlockUpdate::new( + position, + VarInt(i32::from(state_id.as_u16())), + )); + return Ok(()); + } + }} + + if let Some(factory) = server + .block_registry + .get_screen_handler_factory(block, player, &position, server, &world) + { + player.open_handled_screen(factory.as_ref(), Some(position)); + } return Ok(()); }