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.