From ad2fd3c9a52a18986e6c9d6ee5399f21da1d39b7 Mon Sep 17 00:00:00 2001 From: Kris <37947442+OfficialKris@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:02:32 -0500 Subject: [PATCH] Added chest opening and closing animation (#421) * Added basic chest opening and closing * Added number tracking * Added play chest action function --- pumpkin-inventory/src/open_container.rs | 4 ++ .../src/client/play/c_block_event.rs | 31 ++++++++ pumpkin-protocol/src/client/play/mod.rs | 2 + pumpkin/src/block/blocks/chest.rs | 71 +++++++++++++++---- pumpkin/src/block/blocks/crafting_table.rs | 4 +- pumpkin/src/block/pumpkin_block.rs | 2 +- 6 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_block_event.rs diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 3025ed92..c563ee47 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -76,6 +76,10 @@ impl OpenContainer { self.players.clone() } + pub fn get_number_of_players(&self) -> usize { + self.players.len() + } + pub fn get_location(&self) -> Option { self.location } diff --git a/pumpkin-protocol/src/client/play/c_block_event.rs b/pumpkin-protocol/src/client/play/c_block_event.rs new file mode 100644 index 00000000..c1b2e6e0 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_block_event.rs @@ -0,0 +1,31 @@ +use pumpkin_core::math::position::WorldPosition; + +use pumpkin_macros::client_packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize)] +#[client_packet("play:block_event")] +pub struct CBlockAction<'a> { + location: &'a WorldPosition, + action_id: u8, + action_parameter: u8, + block_type: VarInt, +} + +impl<'a> CBlockAction<'a> { + pub fn new( + location: &'a WorldPosition, + action_id: u8, + action_parameter: u8, + block_type: VarInt, + ) -> Self { + Self { + location, + action_id, + action_parameter, + block_type, + } + } +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 79f7e3ea..68a7cc57 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -2,6 +2,7 @@ mod bossevent_action; mod c_acknowledge_block; mod c_actionbar; mod c_block_destroy_stage; +mod c_block_event; mod c_block_update; mod c_boss_event; mod c_center_chunk; @@ -72,6 +73,7 @@ pub use bossevent_action::*; pub use c_acknowledge_block::*; pub use c_actionbar::*; pub use c_block_destroy_stage::*; +pub use c_block_event::*; pub use c_block_update::*; pub use c_boss_event::*; pub use c_center_chunk::*; diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index 313d9a5b..5a83570e 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -2,7 +2,11 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{Chest, OpenContainer, WindowType}; use pumpkin_macros::{pumpkin_block, sound}; -use pumpkin_world::{block::block_registry::Block, item::item_registry::Item}; +use pumpkin_protocol::{client::play::CBlockAction, codec::var_int::VarInt}; +use pumpkin_world::{ + block::block_registry::{get_block, Block}, + item::item_registry::Item, +}; use crate::{ block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock}, @@ -10,6 +14,12 @@ use crate::{ server::Server, }; +#[derive(PartialEq)] +pub enum ChestState { + IsOpened, + IsClosed, +} + #[pumpkin_block("minecraft:chest")] pub struct ChestBlock; @@ -24,10 +34,6 @@ impl PumpkinBlock for ChestBlock { ) { self.open_chest_block(block, player, _location, server) .await; - player - .world() - .play_block_sound(sound!("block.chest.open"), _location) - .await; } async fn on_use_with_item<'a>( @@ -57,15 +63,14 @@ impl PumpkinBlock for ChestBlock { &self, _block: &Block, player: &Player, - _location: WorldPosition, - _server: &Server, - _container: &OpenContainer, + location: WorldPosition, + server: &Server, + container: &mut OpenContainer, ) { - player - .world() - .play_block_sound(sound!("block.chest.close"), _location) + container.remove_player(player.entity_id()); + + self.play_chest_action(container, player, location, server, ChestState::IsClosed) .await; - // TODO: send entity updates close } } @@ -86,6 +91,46 @@ impl ChestBlock { WindowType::Generic9x3, ) .await; - // TODO: send entity updates open + + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let open_containers = server.open_containers.read().await; + if let Some(container) = open_containers.get(&u64::from(container_id)) { + self.play_chest_action(container, player, location, server, ChestState::IsOpened) + .await; + } + } + } + + pub async fn play_chest_action( + &self, + container: &OpenContainer, + player: &Player, + location: WorldPosition, + server: &Server, + state: ChestState, + ) { + let num_players = container.get_number_of_players() as u8; + if state == ChestState::IsClosed && num_players == 0 { + player + .world() + .play_block_sound(sound!("block.chest.close"), location) + .await; + } else if state == ChestState::IsOpened && num_players == 1 { + player + .world() + .play_block_sound(sound!("block.chest.open"), location) + .await; + } + + if let Some(e) = get_block("minecraft:chest").cloned() { + server + .broadcast_packet_all(&CBlockAction::new( + &location, + 1, + num_players, + VarInt(e.id.into()), + )) + .await; + } } } diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 01ad4179..c879f060 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -53,7 +53,7 @@ impl PumpkinBlock for CraftingTableBlock { player: &Player, _location: WorldPosition, _server: &Server, - container: &OpenContainer, + container: &mut OpenContainer, ) { let entity_id = player.entity_id(); for player_id in container.all_player_ids() { @@ -62,6 +62,8 @@ impl PumpkinBlock for CraftingTableBlock { } } + container.remove_player(entity_id); + // TODO: items should be re-added to player inventory or dropped dependending on if they are in movement. // TODO: unique containers should be implemented as a separate stack internally (optimizes large player servers for example) // TODO: ephemeral containers (crafting tables) might need to be separate data structure than stored (ender chest) diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index c0a65c31..90de5718 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -60,7 +60,7 @@ pub trait PumpkinBlock: Send + Sync { _player: &Player, _location: WorldPosition, _server: &Server, - _container: &OpenContainer, + _container: &mut OpenContainer, ) { } }