From 3b2b547d22e798fb1c914f6b128b8fa47e455342 Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Sat, 7 Dec 2024 19:44:48 -0800 Subject: [PATCH 1/7] Added chest and furnace --- pumpkin-inventory/Cargo.toml | 1 + pumpkin-inventory/src/open_container.rs | 51 +++++++++++++++- pumpkin/src/block/block_manager.rs | 13 ++++ pumpkin/src/block/blocks/chest.rs | 70 ++++++++++++++++++++++ pumpkin/src/block/blocks/crafting_table.rs | 19 ++++-- pumpkin/src/block/blocks/furnace.rs | 59 ++++++++++++++++++ pumpkin/src/block/blocks/mod.rs | 2 + pumpkin/src/block/mod.rs | 5 ++ pumpkin/src/block/pumpkin_block.rs | 2 + pumpkin/src/client/player_packet.rs | 10 ++++ pumpkin/src/entity/player.rs | 8 ++- pumpkin/src/world/mod.rs | 10 ++++ 12 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 pumpkin/src/block/blocks/chest.rs create mode 100644 pumpkin/src/block/blocks/furnace.rs diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 28beef94..ac680e6d 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true pumpkin-world = { path = "../pumpkin-world" } pumpkin-registry = {path = "../pumpkin-registry"} pumpkin-macros = { path = "../pumpkin-macros" } +pumpkin-core = { path = "../pumpkin-core" } log.workspace = true rayon.workspace = true diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index fdabf912..ec68578f 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -1,11 +1,16 @@ use crate::crafting::check_if_matches_crafting; use crate::{Container, WindowType}; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::ItemStack; use std::sync::Arc; use tokio::sync::Mutex; pub struct OpenContainer { + // TODO: unique id should be here players: Vec, container: Arc>>, + location: Option, + block: Option, } impl OpenContainer { @@ -36,16 +41,30 @@ impl OpenContainer { } } - pub fn new_empty_container(player_id: i32) -> Self { + pub fn new_empty_container( + player_id: i32, + location: Option, + block: Option, + ) -> Self { Self { players: vec![player_id], container: Arc::new(Mutex::new(Box::new(C::default()))), + location, + block, } } pub fn all_player_ids(&self) -> Vec { self.players.clone() } + + pub fn get_location(&self) -> Option { + self.location + } + + pub fn get_block(&self) -> Option { + self.block.clone() + } } #[derive(Default)] pub struct Chest([Option; 27]); @@ -139,3 +158,33 @@ impl Container for CraftingTable { }) } } + +#[derive(Default)] +pub struct Furnace { + cook: Option, + fuel: Option, + output: Option, +} + +impl Container for Furnace { + fn window_type(&self) -> &'static WindowType { + &WindowType::Furnace + } + + fn window_name(&self) -> &'static str { + "Furnace" + } + fn all_slots(&mut self) -> Vec<&mut Option> { + let mut slots = vec![&mut self.cook]; + slots.push(&mut self.fuel); + slots.push(&mut self.output); + slots + } + + fn all_slots_ref(&self) -> Vec> { + let mut slots = vec![self.cook.as_ref()]; + slots.push(self.fuel.as_ref()); + slots.push(self.output.as_ref()); + slots + } +} diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 50ab938e..01b3c08a 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -81,6 +81,19 @@ impl BlockManager { } } + pub async fn on_close( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + let pumpkin_block = self.get_pumpkin_block(block); + if let Some(pumpkin_block) = pumpkin_block { + pumpkin_block.on_close(player, location, server).await; + } + } + #[must_use] pub fn get_pumpkin_block(&self, block: &Block) -> Option<&Arc> { self.blocks diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs new file mode 100644 index 00000000..1600bb09 --- /dev/null +++ b/pumpkin/src/block/blocks/chest.rs @@ -0,0 +1,70 @@ +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::get_block, item::item_registry::Item}; + +use crate::{ + block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock}, + entity::player::Player, + server::Server, +}; + +#[pumpkin_block("minecraft:chest")] +pub struct ChestBlock; + +#[async_trait] +impl PumpkinBlock for ChestBlock { + async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { + self.open_chest_block(player, _location, server).await; + player + .world() + .play_block_sound(sound!("block.chest.open"), _location) + .await; + } + + async fn on_use_with_item<'a>( + &self, + player: &Player, + _location: WorldPosition, + _item: &Item, + server: &Server, + ) -> BlockActionResult { + self.open_chest_block(player, _location, server).await; + BlockActionResult::Consume + } + + async fn on_close<'a>(&self, player: &Player, _location: WorldPosition, _server: &Server) { + player + .world() + .play_block_sound(sound!("block.chest.close"), _location) + .await; + } +} + +impl ChestBlock { + pub async fn open_chest_block( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + let entity_id = player.entity_id(); + // TODO: This should be a unique identifier for the each block container + player.open_container.store(Some(2)); + { + let mut open_containers = server.open_containers.write().await; + if let Some(chest) = open_containers.get_mut(&2) { + chest.add_player(entity_id); + } else { + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:chest").cloned(), + ); + open_containers.insert(2, open_container); + } + } + player.open_container(server, WindowType::Generic9x3).await; + } +} diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 22e224fc..962d5662 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{CraftingTable, OpenContainer, WindowType}; use pumpkin_macros::pumpkin_block; -use pumpkin_world::item::item_registry::Item; +use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; @@ -14,7 +14,7 @@ pub struct CraftingTableBlock; #[async_trait] impl PumpkinBlock for CraftingTableBlock { async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_crafting_screen(player, server).await; + self.open_crafting_screen(player, _location, server).await; } async fn on_use_with_item<'a>( @@ -24,13 +24,18 @@ impl PumpkinBlock for CraftingTableBlock { _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_crafting_screen(player, server).await; + self.open_crafting_screen(player, _location, server).await; BlockActionResult::Consume } } impl CraftingTableBlock { - pub async fn open_crafting_screen(&self, player: &Player, server: &Server) { + pub async fn open_crafting_screen( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { //TODO: Adjust /craft command to real crafting table let entity_id = player.entity_id(); player.open_container.store(Some(1)); @@ -39,7 +44,11 @@ impl CraftingTableBlock { if let Some(ender_chest) = open_containers.get_mut(&1) { ender_chest.add_player(entity_id); } else { - let open_container = OpenContainer::new_empty_container::(entity_id); + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:crafting_table").cloned(), + ); open_containers.insert(1, open_container); } } diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs new file mode 100644 index 00000000..8fc47949 --- /dev/null +++ b/pumpkin/src/block/blocks/furnace.rs @@ -0,0 +1,59 @@ +use crate::block::block_manager::BlockActionResult; +use crate::entity::player::Player; +use async_trait::async_trait; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::Furnace; +use pumpkin_inventory::{OpenContainer, WindowType}; +use pumpkin_macros::pumpkin_block; +use pumpkin_world::block::block_registry::get_block; +use pumpkin_world::item::item_registry::Item; + +use crate::{block::pumpkin_block::PumpkinBlock, server::Server}; + +#[pumpkin_block("minecraft:furnace")] +pub struct FurnaceBlock; + +#[async_trait] +impl PumpkinBlock for FurnaceBlock { + async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { + self.open_furnace_screen(player, _location, server).await; + } + + async fn on_use_with_item<'a>( + &self, + player: &Player, + _location: WorldPosition, + _item: &Item, + server: &Server, + ) -> BlockActionResult { + self.open_furnace_screen(player, _location, server).await; + BlockActionResult::Consume + } +} + +impl FurnaceBlock { + pub async fn open_furnace_screen( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + //TODO: Adjust /craft command to real crafting table + let entity_id = player.entity_id(); + player.open_container.store(Some(3)); + { + let mut open_containers = server.open_containers.write().await; + if let Some(ender_chest) = open_containers.get_mut(&3) { + ender_chest.add_player(entity_id); + } else { + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:furnace").cloned(), + ); + open_containers.insert(3, open_container); + } + } + player.open_container(server, WindowType::Furnace).await; + } +} diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 8e7fde73..4bdfa819 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -1,2 +1,4 @@ +pub(crate) mod chest; pub(crate) mod crafting_table; +pub(crate) mod furnace; pub(crate) mod jukebox; diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 10d1bc3f..b0779033 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -1,3 +1,6 @@ +use blocks::chest::ChestBlock; +use blocks::furnace::FurnaceBlock; + use crate::block::block_manager::BlockManager; use crate::block::blocks::crafting_table::CraftingTableBlock; use crate::block::blocks::jukebox::JukeboxBlock; @@ -13,6 +16,8 @@ pub fn default_block_manager() -> Arc { manager.register(JukeboxBlock); manager.register(CraftingTableBlock); + manager.register(FurnaceBlock); + manager.register(ChestBlock); Arc::new(manager) } diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 78a3bd05..1849ea5a 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -29,4 +29,6 @@ pub trait PumpkinBlock: Send + Sync { async fn on_placed<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} async fn on_broken<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + + async fn on_close<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 1dd55bf0..24837952 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -803,6 +803,16 @@ impl Player { if let Some(id) = open_container { let mut open_containers = server.open_containers.write().await; if let Some(container) = open_containers.get_mut(&id) { + // If container contains both a location and a type, run the on_close block_manager handler + if let Some(pos) = container.get_location() { + if let Some(block) = container.get_block() { + server + .block_manager + .on_close(&block, self, pos, server) //block, self, location, server) + .await; + } + } + // Remove the player from the container container.remove_player(self.entity_id()); } self.open_container.store(None); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index bfa1d595..6a0a8c91 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -22,7 +22,9 @@ use pumpkin_core::{ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_macros::sound; -use pumpkin_protocol::server::play::{SCookieResponse as SPCookieResponse, SPlayPingRequest}; +use pumpkin_protocol::server::play::{ + SCloseContainer, SCookieResponse as SPCookieResponse, SPlayPingRequest, +}; use pumpkin_protocol::{ bytebuf::packet_id::Packet, client::play::{ @@ -694,6 +696,10 @@ impl Player { SPCookieResponse::PACKET_ID => { self.handle_cookie_response(SPCookieResponse::read(bytebuf)?); } + SCloseContainer::PACKET_ID => { + self.handle_close_container(server, SCloseContainer::read(bytebuf)?) + .await; + } _ => { log::warn!("Failed to handle player packet id {}", packet.id.0); // TODO: We give an error if all play packets are implemented diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 4edd8c3c..3ff68086 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -167,6 +167,16 @@ impl World { .await; } + pub async fn play_block_sound(&self, sound_id: u16, position: WorldPosition) { + let new_vec = Vector3::new( + f64::from(position.0.x) + 0.5, + f64::from(position.0.y) + 0.5, + f64::from(position.0.z) + 0.5, + ); + self.play_sound(sound_id, SoundCategory::Blocks, &new_vec) + .await; + } + pub async fn play_record(&self, record_id: i32, position: WorldPosition) { self.broadcast_packet_all(&CLevelEvent::new(1010, position, record_id, false)) .await; From 2578b076bae348ebd72b8afe36b3a33cd832a777 Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Mon, 9 Dec 2024 00:26:42 -0800 Subject: [PATCH 2/7] Corrected interactive block functionality --- pumpkin-core/src/math/position.rs | 2 +- pumpkin-inventory/src/lib.rs | 7 ++ pumpkin-inventory/src/open_container.rs | 16 +++- pumpkin/src/block/block_manager.rs | 18 +++- pumpkin/src/block/blocks/chest.rs | 81 ++++++++++++---- pumpkin/src/block/blocks/crafting_table.rs | 104 ++++++++++++++++++--- pumpkin/src/block/blocks/furnace.rs | 72 ++++++++++---- pumpkin/src/block/blocks/jukebox.rs | 18 +++- pumpkin/src/block/pumpkin_block.rs | 40 +++++++- pumpkin/src/client/player_packet.rs | 16 ++-- pumpkin/src/server/mod.rs | 31 ++++++ 11 files changed, 337 insertions(+), 68 deletions(-) diff --git a/pumpkin-core/src/math/position.rs b/pumpkin-core/src/math/position.rs index 83989064..a3b82cbe 100644 --- a/pumpkin-core/src/math/position.rs +++ b/pumpkin-core/src/math/position.rs @@ -5,7 +5,7 @@ use crate::math::vector2::Vector2; use num_traits::Euclid; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] /// Aka Block Position pub struct WorldPosition(pub Vector3); diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index bf56dca6..f1273b05 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -94,6 +94,13 @@ pub trait Container: Sync + Send { fn all_slots_ref(&self) -> Vec>; + fn clear_all_slots(&mut self) { + let all_slots = self.all_slots(); + for stack in all_slots { + *stack = None; + } + } + fn all_combinable_slots(&self) -> Vec> { self.all_slots_ref() } diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index ec68578f..03727016 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -6,7 +6,6 @@ use pumpkin_world::item::ItemStack; use std::sync::Arc; use tokio::sync::Mutex; pub struct OpenContainer { - // TODO: unique id should be here players: Vec, container: Arc>>, location: Option, @@ -54,6 +53,21 @@ impl OpenContainer { } } + pub fn is_location(&self, try_position: WorldPosition) -> bool { + if let Some(location) = self.location { + location == try_position + } else { + false + } + } + + pub async fn on_destroy(&self) { + let mut container = self.container.lock().await; + + container.clear_all_slots(); + // TODO drop all items by default + } + pub fn all_player_ids(&self) -> Vec { self.players.clone() } diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 01b3c08a..5a48b255 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -2,6 +2,7 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; use crate::server::Server; use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::OpenContainer; use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; use std::collections::HashMap; @@ -34,7 +35,7 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_use(player, location, server).await; + pumpkin_block.on_use(block, player, location, server).await; } } @@ -49,7 +50,7 @@ impl BlockManager { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block - .on_use_with_item(player, location, item, server) + .on_use_with_item(block, player, location, item, server) .await; } BlockActionResult::Continue @@ -64,7 +65,9 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_placed(player, location, server).await; + pumpkin_block + .on_placed(block, player, location, server) + .await; } } @@ -77,7 +80,9 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_broken(player, location, server).await; + pumpkin_block + .on_broken(block, player, location, server) + .await; } } @@ -87,10 +92,13 @@ impl BlockManager { player: &Player, location: WorldPosition, server: &Server, + container: &mut OpenContainer, ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_close(player, location, server).await; + pumpkin_block + .on_close(block, player, location, server, container) + .await; } } diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index 1600bb09..2d334eb2 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -2,7 +2,7 @@ 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::get_block, item::item_registry::Item}; +use pumpkin_world::{block::block_registry::Block, item::item_registry::Item}; use crate::{ block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock}, @@ -15,8 +15,15 @@ pub struct ChestBlock; #[async_trait] impl PumpkinBlock for ChestBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_chest_block(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_chest_block(block, player, _location, server) + .await; player .world() .play_block_sound(sound!("block.chest.open"), _location) @@ -25,16 +32,47 @@ impl PumpkinBlock for ChestBlock { async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_chest_block(player, _location, server).await; + self.open_chest_block(block, player, _location, server) + .await; BlockActionResult::Consume } - async fn on_close<'a>(&self, player: &Player, _location: WorldPosition, _server: &Server) { + async fn on_broken<'a>( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + // TODO: drop all items and close screen if different player breaks block + let entity_id = player.entity_id(); + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good ct ID: {}", container_id); + + container.on_destroy().await; + + container.remove_player(entity_id); + player.open_container.store(None); + } + } + } + + async fn on_close<'a>( + &self, + _block: &Block, + player: &Player, + _location: WorldPosition, + _server: &Server, + _container: &OpenContainer, + ) { player .world() .play_block_sound(sound!("block.chest.close"), _location) @@ -45,25 +83,34 @@ impl PumpkinBlock for ChestBlock { impl ChestBlock { pub async fn open_chest_block( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { let entity_id = player.entity_id(); - // TODO: This should be a unique identifier for the each block container - player.open_container.store(Some(2)); - { + if let Some(container_id) = server.get_container_id(location, block.clone()).await { let mut open_containers = server.open_containers.write().await; - if let Some(chest) = open_containers.get_mut(&2) { - chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:chest").cloned(), - ); - open_containers.insert(2, open_container); + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good chest ID: {}", container_id); + container.add_player(entity_id); + player.open_container.store(Some(container_id.into())); } + // drop(open_containers); + } else { + let mut open_containers = server.open_containers.write().await; + + let new_id = server.new_container_id(); + log::info!("New chest ID: {}", new_id); + + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + Some(block.clone()), + ); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + // drop(open_containers); } player.open_container(server, WindowType::Generic9x3).await; } diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 962d5662..0c53ad58 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -6,52 +6,126 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{CraftingTable, OpenContainer, WindowType}; use pumpkin_macros::pumpkin_block; -use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; +use pumpkin_world::{block::block_registry::Block, item::item_registry::Item}; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; #[async_trait] impl PumpkinBlock for CraftingTableBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_crafting_screen(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_crafting_screen(block, player, _location, server) + .await; } async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_crafting_screen(player, _location, server).await; + self.open_crafting_screen(block, player, _location, server) + .await; BlockActionResult::Consume } + + async fn on_broken<'a>( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + // TODO: drop all items and close screen if different player breaks block + let entity_id = player.entity_id(); + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + if let Some(ender_chest) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good ct ID: {}", container_id); + + ender_chest.on_destroy().await; + + ender_chest.remove_player(entity_id); + player.open_container.store(None); + } + } + } + + async fn on_close<'a>( + &self, + _block: &Block, + player: &Player, + _location: WorldPosition, + _server: &Server, + container: &OpenContainer, + ) { + log::info!("On Close CT"); + let entity_id = player.entity_id(); + + for player_id in container.all_player_ids() { + if entity_id == player_id { + container.on_destroy().await; + } + } + + // TODO: should re-add all items to player or drop? + } } impl CraftingTableBlock { pub async fn open_crafting_screen( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { //TODO: Adjust /craft command to real crafting table let entity_id = player.entity_id(); - player.open_container.store(Some(1)); - { - let mut open_containers = server.open_containers.write().await; - if let Some(ender_chest) = open_containers.get_mut(&1) { + let mut open_containers = server.open_containers.write().await; + let mut id_to_use = -1; + + for (id, container) in open_containers.iter() { + if let Some(a_block) = container.get_block() { + if a_block.id == block.id && container.all_player_ids().is_empty() { + id_to_use = *id as i64; + } + } + } + + if id_to_use == -1 { + let new_id = server.new_container_id(); + + log::info!("New ct ID: {}", new_id); + + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + Some(block.clone()), + ); + + open_containers.insert(new_id.into(), open_container); + + player.open_container.store(Some(new_id.into())); + // drop(open_containers); + } else { + log::info!("Using previous ct ID: {}", id_to_use); + if let Some(ender_chest) = open_containers.get_mut(&(id_to_use as u64)) { ender_chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:crafting_table").cloned(), - ); - open_containers.insert(1, open_container); + player + .open_container + .store(Some(id_to_use.try_into().unwrap())); } } + drop(open_containers); player .open_container(server, WindowType::CraftingTable) .await; diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index 8fc47949..c00a5feb 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -5,7 +5,7 @@ use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::Furnace; use pumpkin_inventory::{OpenContainer, WindowType}; use pumpkin_macros::pumpkin_block; -use pumpkin_world::block::block_registry::get_block; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; use crate::{block::pumpkin_block::PumpkinBlock, server::Server}; @@ -15,44 +15,84 @@ pub struct FurnaceBlock; #[async_trait] impl PumpkinBlock for FurnaceBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_furnace_screen(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_furnace_screen(block, player, _location, server) + .await; } async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_furnace_screen(player, _location, server).await; + self.open_furnace_screen(block, player, _location, server) + .await; BlockActionResult::Consume } + + async fn on_broken<'a>( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + // TODO: drop all items and close screen if different player breaks block + let entity_id = player.entity_id(); + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good ct ID: {}", container_id); + + container.on_destroy().await; + + container.remove_player(entity_id); + player.open_container.store(None); + } + } + } } impl FurnaceBlock { pub async fn open_furnace_screen( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { - //TODO: Adjust /craft command to real crafting table let entity_id = player.entity_id(); - player.open_container.store(Some(3)); - { + if let Some(container_id) = server.get_container_id(location, block.clone()).await { let mut open_containers = server.open_containers.write().await; - if let Some(ender_chest) = open_containers.get_mut(&3) { - ender_chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:furnace").cloned(), - ); - open_containers.insert(3, open_container); + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good furnance ID: {}", container_id); + container.add_player(entity_id); + player.open_container.store(Some(container_id.into())); } + // drop(open_containers); + } else { + let mut open_containers = server.open_containers.write().await; + + let new_id = server.new_container_id(); + log::info!("New furnace ID: {}", new_id); + + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + Some(block.clone()), + ); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + // drop(open_containers); } player.open_container(server, WindowType::Furnace).await; } diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index 3c52395b..4836c4be 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -6,6 +6,7 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::pumpkin_block; use pumpkin_registry::SYNCED_REGISTRIES; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; #[pumpkin_block("minecraft:jukebox")] @@ -13,7 +14,13 @@ pub struct JukeboxBlock; #[async_trait] impl PumpkinBlock for JukeboxBlock { - async fn on_use<'a>(&self, player: &Player, location: WorldPosition, _server: &Server) { + async fn on_use<'a>( + &self, + _block: &Block, + player: &Player, + location: WorldPosition, + _server: &Server, + ) { // For now just stop the music at this position let world = &player.living_entity.entity.world; @@ -22,6 +29,7 @@ impl PumpkinBlock for JukeboxBlock { async fn on_use_with_item<'a>( &self, + _block: &Block, player: &Player, location: WorldPosition, item: &Item, @@ -49,7 +57,13 @@ impl PumpkinBlock for JukeboxBlock { BlockActionResult::Consume } - async fn on_broken<'a>(&self, player: &Player, location: WorldPosition, _server: &Server) { + async fn on_broken<'a>( + &self, + _block: &Block, + player: &Player, + location: WorldPosition, + _server: &Server, + ) { // For now just stop the music at this position let world = &player.living_entity.entity.world; diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 1849ea5a..c0a65c31 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -3,6 +3,8 @@ use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::OpenContainer; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; pub trait BlockMetadata { @@ -15,9 +17,17 @@ pub trait BlockMetadata { #[async_trait] pub trait PumpkinBlock: Send + Sync { - async fn on_use<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_use<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } async fn on_use_with_item<'a>( &self, + _block: &Block, _player: &Player, _location: WorldPosition, _item: &Item, @@ -26,9 +36,31 @@ pub trait PumpkinBlock: Send + Sync { BlockActionResult::Continue } - async fn on_placed<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_placed<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } - async fn on_broken<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_broken<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } - async fn on_close<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_close<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + _container: &OpenContainer, + ) { + } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 24837952..ae5cc77d 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -17,7 +17,7 @@ use pumpkin_core::{ text::TextComponent, GameMode, }; -use pumpkin_inventory::{InventoryError, WindowType}; +use pumpkin_inventory::InventoryError; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; use pumpkin_protocol::{ client::play::CCommandSuggestions, @@ -790,11 +790,13 @@ impl Player { // TODO: // This function will in the future be used to keep track of if the client is in a valid state. // But this is not possible yet - pub async fn handle_close_container(&self, server: &Server, packet: SCloseContainer) { - let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else { - self.kick(TextComponent::text("Invalid window ID")).await; - return; - }; + pub async fn handle_close_container(&self, server: &Server, _packet: SCloseContainer) { + // TODO: This should check if player sent this packet before + // let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else { + // log::info!("Closed ID: {}", packet.window_id.0); + // self.kick(TextComponent::text("Invalid window ID")).await; + // return; + // }; // window_id 0 represents both 9x1 Generic AND inventory here let mut inventory = self.inventory().lock().await; @@ -808,7 +810,7 @@ impl Player { if let Some(block) = container.get_block() { server .block_manager - .on_close(&block, self, pos, server) //block, self, location, server) + .on_close(&block, self, pos, server, container) //block, self, location, server) .await; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index b01d7d0c..67616c73 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,6 +1,7 @@ use connection_cache::{CachedBranding, CachedStatus}; use key_store::KeyStore; use pumpkin_config::BASIC_CONFIG; +use pumpkin_core::math::position::WorldPosition; use pumpkin_core::math::vector2::Vector2; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; @@ -9,9 +10,11 @@ use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket}; use pumpkin_registry::{DimensionType, Registry}; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::dimension::Dimension; use rand::prelude::SliceRandom; use std::collections::HashMap; +use std::sync::atomic::AtomicU32; use std::{ sync::{ atomic::{AtomicI32, Ordering}, @@ -61,6 +64,8 @@ pub struct Server { pub drag_handler: DragHandler, /// Assigns unique IDs to entities. entity_id: AtomicI32, + /// Assigns unique IDs to containers. + container_id: AtomicU32, /// Manages authentication with a authentication server, if enabled. pub auth_client: Option, /// The server's custom bossbars @@ -108,6 +113,7 @@ impl Server { drag_handler: DragHandler::new(), // 0 is invalid entity_id: 2.into(), + container_id: 0.into(), worlds: vec![Arc::new(world)], dimensions: vec![ DimensionType::Overworld, @@ -192,6 +198,26 @@ impl Server { .cloned() } + pub async fn get_container_id(&self, location: WorldPosition, block: Block) -> Option { + let open_containers = self.open_containers.read().await; + // TODO: do better than brute force + for (id, container) in open_containers.iter() { + if container.is_location(location) { + if let Some(container_block) = container.get_block() { + if container_block.id == block.id { + log::debug!("Found container id: {}", id); + return Some(*id as u32); + } + } + } + } + log::debug!("No container found... this should not happen."); + + drop(open_containers); + + None + } + /// Broadcasts a packet to all players in all worlds. /// /// This function sends the specified packet to every connected player in every world managed by the server. @@ -303,6 +329,11 @@ impl Server { self.entity_id.fetch_add(1, Ordering::SeqCst) } + /// Generates a new container id + pub fn new_container_id(&self) -> u32 { + self.container_id.fetch_add(1, Ordering::SeqCst) + } + pub fn get_branding(&self) -> CPluginMessage<'_> { self.server_branding.get_branding() } From 97537a22d30c3bf7e9f5ec5d2fa71ae4d69c50c7 Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:20:04 -0800 Subject: [PATCH 3/7] Fixed merge --- pumpkin/src/block/block_manager.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 6a8d14db..5a48b255 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -102,19 +102,6 @@ impl BlockManager { } } - pub async fn on_close( - &self, - block: &Block, - player: &Player, - location: WorldPosition, - server: &Server, - ) { - let pumpkin_block = self.get_pumpkin_block(block); - if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_close(player, location, server).await; - } - } - #[must_use] pub fn get_pumpkin_block(&self, block: &Block) -> Option<&Arc> { self.blocks From f47c409f234394d783b35d1179eee918a678331a Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:36:11 -0800 Subject: [PATCH 4/7] Added standard functions and player remove on destroy --- pumpkin-inventory/src/open_container.rs | 9 ++- pumpkin/src/block/blocks/chest.rs | 50 ++++------------ pumpkin/src/block/blocks/crafting_table.rs | 18 +----- pumpkin/src/block/blocks/furnace.rs | 49 ++++------------ pumpkin/src/block/blocks/mod.rs | 68 ++++++++++++++++++++++ pumpkin/src/client/container.rs | 1 + 6 files changed, 97 insertions(+), 98 deletions(-) diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 3d4d69d1..2e2b4d10 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -5,8 +5,10 @@ use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::ItemStack; use std::sync::Arc; use tokio::sync::Mutex; + pub struct OpenContainer { // TODO: unique id should be here + // TODO: should this be uuid? players: Vec, container: Arc>>, location: Option, @@ -62,11 +64,8 @@ impl OpenContainer { } } - pub async fn on_destroy(&self) { - let mut container = self.container.lock().await; - - container.clear_all_slots(); - // TODO drop all items by default + pub async fn clear_all_slots(&self) { + self.container.lock().await.clear_all_slots(); } pub fn all_player_ids(&self) -> Vec { diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index 2d334eb2..123b91bc 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -50,19 +50,7 @@ impl PumpkinBlock for ChestBlock { location: WorldPosition, server: &Server, ) { - // TODO: drop all items and close screen if different player breaks block - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; - if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good ct ID: {}", container_id); - - container.on_destroy().await; - - container.remove_player(entity_id); - player.open_container.store(None); - } - } + super::standard_on_destroy_with_container(block, player, location, server).await; } async fn on_close<'a>( @@ -77,6 +65,7 @@ impl PumpkinBlock for ChestBlock { .world() .play_block_sound(sound!("block.chest.close"), _location) .await; + // TODO: send entity updates close } } @@ -88,30 +77,15 @@ impl ChestBlock { location: WorldPosition, server: &Server, ) { - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; - if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good chest ID: {}", container_id); - container.add_player(entity_id); - player.open_container.store(Some(container_id.into())); - } - // drop(open_containers); - } else { - let mut open_containers = server.open_containers.write().await; - - let new_id = server.new_container_id(); - log::info!("New chest ID: {}", new_id); - - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - Some(block.clone()), - ); - open_containers.insert(new_id.into(), open_container); - player.open_container.store(Some(new_id.into())); - // drop(open_containers); - } - player.open_container(server, WindowType::Generic9x3).await; + // TODO: shouldn't Chest and window type be constrained together to avoid errors? + super::standard_open_container::( + block, + player, + location, + server, + WindowType::Generic9x3, + ) + .await; + // TODO: send entity updates open } } diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 0c53ad58..eeab1051 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -44,19 +44,7 @@ impl PumpkinBlock for CraftingTableBlock { location: WorldPosition, server: &Server, ) { - // TODO: drop all items and close screen if different player breaks block - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; - if let Some(ender_chest) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good ct ID: {}", container_id); - - ender_chest.on_destroy().await; - - ender_chest.remove_player(entity_id); - player.open_container.store(None); - } - } + super::standard_on_destroy_with_container(block, player, location, server).await; } async fn on_close<'a>( @@ -67,12 +55,11 @@ impl PumpkinBlock for CraftingTableBlock { _server: &Server, container: &OpenContainer, ) { - log::info!("On Close CT"); let entity_id = player.entity_id(); for player_id in container.all_player_ids() { if entity_id == player_id { - container.on_destroy().await; + container.clear_all_slots().await; } } @@ -115,7 +102,6 @@ impl CraftingTableBlock { open_containers.insert(new_id.into(), open_container); player.open_container.store(Some(new_id.into())); - // drop(open_containers); } else { log::info!("Using previous ct ID: {}", id_to_use); if let Some(ender_chest) = open_containers.get_mut(&(id_to_use as u64)) { diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index c00a5feb..1f84e8a0 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -3,7 +3,7 @@ use crate::entity::player::Player; use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::Furnace; -use pumpkin_inventory::{OpenContainer, WindowType}; +use pumpkin_inventory::WindowType; use pumpkin_macros::pumpkin_block; use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; @@ -46,19 +46,7 @@ impl PumpkinBlock for FurnaceBlock { location: WorldPosition, server: &Server, ) { - // TODO: drop all items and close screen if different player breaks block - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; - if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good ct ID: {}", container_id); - - container.on_destroy().await; - - container.remove_player(entity_id); - player.open_container.store(None); - } - } + super::standard_on_destroy_with_container(block, player, location, server).await; } } @@ -70,30 +58,13 @@ impl FurnaceBlock { location: WorldPosition, server: &Server, ) { - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; - if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good furnance ID: {}", container_id); - container.add_player(entity_id); - player.open_container.store(Some(container_id.into())); - } - // drop(open_containers); - } else { - let mut open_containers = server.open_containers.write().await; - - let new_id = server.new_container_id(); - log::info!("New furnace ID: {}", new_id); - - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - Some(block.clone()), - ); - open_containers.insert(new_id.into(), open_container); - player.open_container.store(Some(new_id.into())); - // drop(open_containers); - } - player.open_container(server, WindowType::Furnace).await; + super::standard_open_container::( + block, + player, + location, + server, + WindowType::Furnace, + ) + .await; } } diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 4bdfa819..15ffdcee 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -1,4 +1,72 @@ +use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::{Container, OpenContainer, WindowType}; +use pumpkin_world::block::block_registry::Block; + +use crate::{entity::player::Player, server::Server}; + pub(crate) mod chest; pub(crate) mod crafting_table; pub(crate) mod furnace; pub(crate) mod jukebox; + +/// The standard destroy with container removes the player forcibly from the container, +/// drops items to the floor and back to the player's inventory if the item stack is in movement. +pub async fn standard_on_destroy_with_container( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, +) { + // TODO: drop all items and back to players inventory if in motion + let entity_id = player.entity_id(); + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good ct ID: {}", container_id); + container.remove_player(entity_id); + container.clear_all_slots().await; + player.open_container.store(None); + close_all_in_container(player, container).await; + } + } +} + +/// The standard open container creates a new container if a container of the same block +/// type does not exist at the selected block location. If a container of the same type exists, the player +/// is added to the currently connected players to that container. +pub async fn standard_open_container( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + window_type: WindowType, +) { + let entity_id = player.entity_id(); + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + log::info!("Good ID: {}", container_id); + container.add_player(entity_id); + player.open_container.store(Some(container_id.into())); + } + } else { + let mut open_containers = server.open_containers.write().await; + + let new_id = server.new_container_id(); + log::info!("New ID: {}", new_id); + + let open_container = + OpenContainer::new_empty_container::(entity_id, Some(location), Some(block.clone())); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + } + player.open_container(server, window_type).await; +} + +pub async fn close_all_in_container(player: &Player, container: &OpenContainer) { + for id in container.all_player_ids() { + if let Some(y) = player.world().get_player_by_entityid(id).await { + y.close_container().await; + } + } +} diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index e8902638..10b631fa 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -82,6 +82,7 @@ impl Player { } /// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent + // TODO: is this just bc ids are not synced? pub async fn close_container(&self) { let mut inventory = self.inventory().lock().await; inventory.total_opened_containers += 1; From f8bea62f4f8069e9c890a279d51f6f512eec0e38 Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:19:57 -0800 Subject: [PATCH 5/7] Added unique block standard functions --- pumpkin-inventory/src/open_container.rs | 8 +++ pumpkin/src/block/blocks/chest.rs | 2 +- pumpkin/src/block/blocks/crafting_table.rs | 55 ++++------------ pumpkin/src/block/blocks/furnace.rs | 2 +- pumpkin/src/block/blocks/mod.rs | 75 +++++++++++++++++----- pumpkin/src/server/mod.rs | 29 ++++++++- 6 files changed, 108 insertions(+), 63 deletions(-) diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 2e2b4d10..3025ed92 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -68,6 +68,10 @@ impl OpenContainer { self.container.lock().await.clear_all_slots(); } + pub fn clear_all_players(&mut self) { + self.players = vec![]; + } + pub fn all_player_ids(&self) -> Vec { self.players.clone() } @@ -76,6 +80,10 @@ impl OpenContainer { self.location } + pub async fn set_location(&mut self, location: Option) { + self.location = location; + } + pub fn get_block(&self) -> Option { self.block.clone() } diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index 123b91bc..313d9a5b 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -50,7 +50,7 @@ impl PumpkinBlock for ChestBlock { location: WorldPosition, server: &Server, ) { - super::standard_on_destroy_with_container(block, player, location, server).await; + super::standard_on_broken_with_container(block, player, location, server).await; } async fn on_close<'a>( diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index eeab1051..01ad4179 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -44,7 +44,7 @@ impl PumpkinBlock for CraftingTableBlock { location: WorldPosition, server: &Server, ) { - super::standard_on_destroy_with_container(block, player, location, server).await; + super::standard_on_broken_with_container(block, player, location, server).await; } async fn on_close<'a>( @@ -56,14 +56,15 @@ impl PumpkinBlock for CraftingTableBlock { container: &OpenContainer, ) { let entity_id = player.entity_id(); - for player_id in container.all_player_ids() { if entity_id == player_id { container.clear_all_slots().await; } } - // TODO: should re-add all items to player or drop? + // 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) } } @@ -75,45 +76,13 @@ impl CraftingTableBlock { location: WorldPosition, server: &Server, ) { - //TODO: Adjust /craft command to real crafting table - let entity_id = player.entity_id(); - let mut open_containers = server.open_containers.write().await; - let mut id_to_use = -1; - - for (id, container) in open_containers.iter() { - if let Some(a_block) = container.get_block() { - if a_block.id == block.id && container.all_player_ids().is_empty() { - id_to_use = *id as i64; - } - } - } - - if id_to_use == -1 { - let new_id = server.new_container_id(); - - log::info!("New ct ID: {}", new_id); - - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - Some(block.clone()), - ); - - open_containers.insert(new_id.into(), open_container); - - player.open_container.store(Some(new_id.into())); - } else { - log::info!("Using previous ct ID: {}", id_to_use); - if let Some(ender_chest) = open_containers.get_mut(&(id_to_use as u64)) { - ender_chest.add_player(entity_id); - player - .open_container - .store(Some(id_to_use.try_into().unwrap())); - } - } - drop(open_containers); - player - .open_container(server, WindowType::CraftingTable) - .await; + super::standard_open_container_unique::( + block, + player, + location, + server, + WindowType::CraftingTable, + ) + .await; } } diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index 1f84e8a0..1ecdf995 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -46,7 +46,7 @@ impl PumpkinBlock for FurnaceBlock { location: WorldPosition, server: &Server, ) { - super::standard_on_destroy_with_container(block, player, location, server).await; + super::standard_on_broken_with_container(block, player, location, server).await; } } diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 15ffdcee..d3785d66 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -10,23 +10,23 @@ pub(crate) mod furnace; pub(crate) mod jukebox; /// The standard destroy with container removes the player forcibly from the container, -/// drops items to the floor and back to the player's inventory if the item stack is in movement. -pub async fn standard_on_destroy_with_container( +/// drops items to the floor, and back to the player's inventory if the item stack is in movement. +pub async fn standard_on_broken_with_container( block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { // TODO: drop all items and back to players inventory if in motion - let entity_id = player.entity_id(); - if let Some(container_id) = server.get_container_id(location, block.clone()).await { + if let Some(all_container_ids) = server.get_all_container_ids(location, block.clone()).await { let mut open_containers = server.open_containers.write().await; - if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good ct ID: {}", container_id); - container.remove_player(entity_id); - container.clear_all_slots().await; - player.open_container.store(None); - close_all_in_container(player, container).await; + for individual_id in all_container_ids { + if let Some(container) = open_containers.get_mut(&u64::from(individual_id)) { + container.clear_all_slots().await; + player.open_container.store(None); + close_all_in_container(player, container).await; + container.clear_all_players(); + } } } } @@ -42,31 +42,72 @@ pub async fn standard_open_container( window_type: WindowType, ) { let entity_id = player.entity_id(); + let mut open_containers = server.open_containers.write().await; + // If container exists, add player to container, otherwise create new container if let Some(container_id) = server.get_container_id(location, block.clone()).await { - let mut open_containers = server.open_containers.write().await; + log::debug!("Using previous standard container ID: {}", container_id); if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { - log::info!("Good ID: {}", container_id); container.add_player(entity_id); player.open_container.store(Some(container_id.into())); } } else { - let mut open_containers = server.open_containers.write().await; - let new_id = server.new_container_id(); - log::info!("New ID: {}", new_id); + log::debug!("Creating new standard container ID: {}", new_id); + let open_container = + OpenContainer::new_empty_container::(entity_id, Some(location), Some(block.clone())); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + } + player.open_container(server, window_type).await; +} +pub async fn standard_open_container_unique( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + window_type: WindowType, +) { + let entity_id = player.entity_id(); + let mut open_containers = server.open_containers.write().await; + let mut id_to_use = -1; + + // TODO: we can do better than brute force + for (id, container) in open_containers.iter() { + if let Some(a_block) = container.get_block() { + if a_block.id == block.id && container.all_player_ids().is_empty() { + id_to_use = *id as i64; + } + } + } + + if id_to_use == -1 { + let new_id = server.new_container_id(); + log::debug!("Creating new unqiue container ID: {}", new_id); let open_container = OpenContainer::new_empty_container::(entity_id, Some(location), Some(block.clone())); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + } else { + log::debug!("Using previous unqiue container ID: {}", id_to_use); + if let Some(unique_container) = open_containers.get_mut(&(id_to_use as u64)) { + unique_container.set_location(Some(location)).await; + unique_container.add_player(entity_id); + player + .open_container + .store(Some(id_to_use.try_into().unwrap())); + } } + drop(open_containers); player.open_container(server, window_type).await; } pub async fn close_all_in_container(player: &Player, container: &OpenContainer) { for id in container.all_player_ids() { - if let Some(y) = player.world().get_player_by_entityid(id).await { - y.close_container().await; + if let Some(remote_player) = player.world().get_player_by_entityid(id).await { + remote_player.close_container().await; } } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 4dac4f79..5bf35b9d 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -60,6 +60,7 @@ pub struct Server { /// Caches game registries for efficient access. pub cached_registry: Vec, /// Tracks open containers used for item interactions. + // TODO: should have per player open_containers pub open_containers: RwLock>, pub drag_handler: DragHandler, /// Assigns unique IDs to entities. @@ -194,6 +195,8 @@ impl Server { .cloned() } + /// Returns the first id with a matching location and block type. If this is used with unique + /// blocks, the output will return a random result. pub async fn get_container_id(&self, location: WorldPosition, block: Block) -> Option { let open_containers = self.open_containers.read().await; // TODO: do better than brute force @@ -207,13 +210,37 @@ impl Server { } } } - log::debug!("No container found... this should not happen."); + log::error!("No container found... this should not happen."); drop(open_containers); None } + pub async fn get_all_container_ids( + &self, + location: WorldPosition, + block: Block, + ) -> Option> { + let open_containers = self.open_containers.read().await; + let mut matching_container_ids: Vec = vec![]; + // TODO: do better than brute force + for (id, container) in open_containers.iter() { + if container.is_location(location) { + if let Some(container_block) = container.get_block() { + if container_block.id == block.id { + log::debug!("Found matching container id: {}", id); + matching_container_ids.push(*id as u32); + } + } + } + } + + drop(open_containers); + + Some(matching_container_ids) + } + /// Broadcasts a packet to all players in all worlds. /// /// This function sends the specified packet to every connected player in every world managed by the server. From 9ff93f5c16af70bbc295304b0b995f9a2f556d6f Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:35:14 -0800 Subject: [PATCH 6/7] Fix clippy --- pumpkin/src/net/packet/play.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 6fdaffc7..d6ad4509 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -18,7 +18,7 @@ use pumpkin_core::{ text::TextComponent, GameMode, }; -use pumpkin_inventory::{InventoryError, WindowType}; +use pumpkin_inventory::InventoryError; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; use pumpkin_protocol::{ From 035fd59b645f26fa78141c4811b62eadedfd8aa0 Mon Sep 17 00:00:00 2001 From: OfficialKris <37947442+OfficialKris@users.noreply.github.com> Date: Wed, 25 Dec 2024 08:26:36 -0800 Subject: [PATCH 7/7] Fix deadlock in standard function --- pumpkin/src/block/blocks/mod.rs | 3 ++- pumpkin/src/server/mod.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index d3785d66..b0b12802 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -42,15 +42,16 @@ pub async fn standard_open_container( window_type: WindowType, ) { let entity_id = player.entity_id(); - let mut open_containers = server.open_containers.write().await; // If container exists, add player to container, otherwise create new container if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; log::debug!("Using previous standard container ID: {}", container_id); if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { container.add_player(entity_id); player.open_container.store(Some(container_id.into())); } } else { + let mut open_containers = server.open_containers.write().await; let new_id = server.new_container_id(); log::debug!("Creating new standard container ID: {}", new_id); let open_container = diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 8f87204e..050c5d8c 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -214,7 +214,6 @@ impl Server { } } } - log::error!("No container found... this should not happen."); drop(open_containers);