From 53c3a013c20e3af0d19a51bcc0c6e99bad393b7a Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Sat, 31 Aug 2024 11:41:13 +0200 Subject: [PATCH] add OpenedContainer to Server we will store all opened containers there until closed by all players, and when closed we will save contents to the block in the world. We should implement Drop for OpenedContainer to achieve this. --- pumpkin-inventory/src/lib.rs | 33 +++++++--- pumpkin-inventory/src/open_container.rs | 46 ++++++++++++++ pumpkin-inventory/src/player.rs | 4 ++ pumpkin/src/client/container.rs | 84 +++++++++++++------------ pumpkin/src/entity/player.rs | 24 +++---- pumpkin/src/server.rs | 20 ++++-- 6 files changed, 145 insertions(+), 66 deletions(-) create mode 100644 pumpkin-inventory/src/open_container.rs diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index 799e68a9f..3f854db60 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -4,11 +4,14 @@ use num_derive::{FromPrimitive, ToPrimitive}; use pumpkin_world::item::ItemStack; pub mod container_click; +mod open_container; pub mod player; pub mod window_property; +pub use open_container::OpenContainer; + /// https://wiki.vg/Inventory -#[derive(Debug, ToPrimitive, FromPrimitive, Clone, Eq, PartialEq)] +#[derive(Debug, ToPrimitive, FromPrimitive, Clone, Copy, Eq, PartialEq)] pub enum WindowType { // not used Generic9x1, @@ -56,8 +59,8 @@ impl WindowType { "WINDOW TITLE" } } - -pub trait Container { +// Container needs Sync + Send to be able to be in async Server +pub trait Container: Sync + Send { fn window_type(&self) -> &WindowType; fn handle_item_change( @@ -65,9 +68,13 @@ pub trait Container { carried_item: &mut Option, slot: usize, mouse_click: MouseClick, - ); + ) { + handle_item_take(carried_item, self.all_slots()[slot], mouse_click) + } fn all_slots(&mut self) -> Vec<&mut Option>; + + fn all_slots_ref(&self) -> Vec>; } pub fn handle_item_take( @@ -180,6 +187,10 @@ impl Container for ContainerStruct { fn all_slots(&mut self) -> Vec<&mut Option> { self.slots.iter_mut().collect() } + + fn all_slots_ref(&self) -> Vec> { + self.slots.iter().map(|slot| slot.as_ref()).collect() + } } pub struct OptionallyCombinedContainer<'a> { @@ -217,11 +228,13 @@ impl<'a> Container for OptionallyCombinedContainer<'a> { slots } - fn handle_item_change( - &mut self, - carried_item: &mut Option, - slot: usize, - mouse_click: MouseClick, - ) { + fn all_slots_ref(&self) -> Vec> { + let mut slots = if let Some(container) = &self.container { + container.all_slots_ref() + } else { + vec![] + }; + slots.extend(self.inventory.all_slots_ref()); + slots } } diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs new file mode 100644 index 000000000..35a24b9a5 --- /dev/null +++ b/pumpkin-inventory/src/open_container.rs @@ -0,0 +1,46 @@ +use crate::container_click::MouseClick; +use crate::{handle_item_change, Container, WindowType}; +use pumpkin_world::item::ItemStack; +use std::sync::{Mutex, MutexGuard}; + +pub struct OpenContainer { + players: Vec, + container: Mutex>, +} + +impl OpenContainer { + pub fn try_open(&self, player_id: i32) -> Option>> { + if !self.players.contains(&player_id) { + return None; + } + Some(self.container.lock().unwrap()) + } + + pub fn empty(player_id: i32) -> Self { + Self { + players: vec![player_id], + container: Mutex::new(Box::new(Chest::new())), + } + } +} + +struct Chest([Option; 27]); + +impl Chest { + pub fn new() -> Self { + Self([None; 27]) + } +} +impl Container for Chest { + fn window_type(&self) -> &WindowType { + &WindowType::Generic9x3 + } + + fn all_slots(&mut self) -> Vec<&mut Option> { + self.0.iter_mut().collect() + } + + fn all_slots_ref(&self) -> Vec> { + self.0.iter().map(|slot| slot.as_ref()).collect() + } +} diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 4da9ac73c..ca7150920 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -148,4 +148,8 @@ impl Container for PlayerInventory { fn all_slots(&mut self) -> Vec<&mut Option> { self.slots_mut() } + + fn all_slots_ref(&self) -> Vec> { + self.slots() + } } diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index a32d9b26f..02cb84893 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -15,11 +15,12 @@ use pumpkin_protocol::slot::Slot; use pumpkin_world::item::ItemStack; use crate::entity::player::Player; +use crate::server::Server; impl Player { pub fn open_container( &mut self, - window_type: WindowType, + window_type: &WindowType, minecraft_menu_id: &str, window_title: Option<&str>, items: Option>>, @@ -45,7 +46,7 @@ impl Player { pub fn set_container_content<'a>( &mut self, - window_type: WindowType, + window_type: &WindowType, items: Option>>, carried_item: Option<&'a ItemStack>, ) { @@ -75,7 +76,7 @@ impl Player { } }; let packet = - CSetContainerContent::new(window_type as u8 + 1, 0.into(), &slots, &carried_item); + CSetContainerContent::new(*window_type as u8 + 1, 0.into(), &slots, &carried_item); self.client.send_packet(&packet); } @@ -109,8 +110,15 @@ impl Player { .send_packet(&CSetContainerProperty::new(window_type as u8, id, value)); } - pub fn handle_click_container(&mut self, packet: SClickContainer) { + pub fn handle_click_container(&mut self, server: &mut Server, packet: SClickContainer) { use container_click::*; + let mut opened_container = if let Some(id) = self.open_container { + server.try_get_container(self.entity_id(), self.open_container.unwrap()) + } else { + None + }; + let opened_container = opened_container.as_deref_mut(); + let click = Click { state_id: packet.state_id.0.try_into().unwrap(), changed_items: packet @@ -137,15 +145,19 @@ impl Player { packet.slot, ), }; - match click.mode.click_type { - ClickType::MouseClick(mouse_click) => { - self.mouse_click(mouse_click, click.window_type, click.mode.slot) + ClickType::MouseClick(mouse_click) => self.mouse_click( + opened_container, + mouse_click, + click.window_type, + click.mode.slot, + ), + ClickType::ShiftClick => { + self.shift_mouse_click(opened_container, click.window_type, click.mode.slot) } - ClickType::ShiftClick => self.shift_mouse_click(click.window_type, click.mode.slot), ClickType::KeyClick(key_click) => match click.mode.slot { container_click::Slot::Normal(slot) => { - self.number_button_pressed(click.window_type, key_click, slot) + self.number_button_pressed(opened_container, click.window_type, key_click, slot) } container_click::Slot::OutsideInventory => { unimplemented!("This is not a valid state") @@ -153,7 +165,7 @@ impl Player { }, ClickType::CreativePickItem => { if let container_click::Slot::Normal(slot) = click.mode.slot { - self.creative_pick_item(slot) + self.creative_pick_item(opened_container, slot) } } ClickType::DoubleClick => {} @@ -178,11 +190,12 @@ impl Player { fn mouse_click( &mut self, + opened_container: Option<&mut Box>, mouse_click: MouseClick, window_type: WindowType, slot: container_click::Slot, ) { - let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); + let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); if container.window_type() != &window_type { return; } @@ -195,8 +208,13 @@ impl Player { }; } - fn shift_mouse_click(&mut self, window_type: WindowType, slot: container_click::Slot) { - let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); + fn shift_mouse_click( + &mut self, + opened_container: Option<&mut Box>, + window_type: WindowType, + slot: container_click::Slot, + ) { + let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); if container.window_type() != &window_type { return; } @@ -235,13 +253,19 @@ impl Player { }; } - fn number_button_pressed(&mut self, window_type: WindowType, key_click: KeyClick, slot: usize) { + fn number_button_pressed( + &mut self, + opened_container: Option<&mut Box>, + window_type: WindowType, + key_click: KeyClick, + slot: usize, + ) { let changing_slot = match key_click { KeyClick::Slot(slot) => slot, KeyClick::Offhand => 45, }; let mut changing_item_slot = self.inventory.get_slot(changing_slot as usize).to_owned(); - let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); + let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); if container.window_type() != &window_type { return; } @@ -253,34 +277,16 @@ impl Player { } } - fn creative_pick_item(&mut self, slot: usize) { - let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); - + fn creative_pick_item( + &mut self, + mut opened_container: Option<&mut Box>, + slot: usize, + ) { + let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); if let Some(Some(item)) = container.all_slots().get_mut(slot) { self.carried_item = Some(item.to_owned()) } } fn double_click(&mut self, slot: usize) {} - - fn get_container_type(&mut self) -> &WindowType { - match &self.open_container { - Some(container) => container.window_type(), - // This is the one the inventory uses for some stupid reason - None => &WindowType::Generic9x1, - } - } -} - -/*impl ContainerStruct { - pub fn opened_by_players(&mut self, server: Server) -> Vec<&Player> { - - } -}*/ - -fn get_full_container<'a>( - inventory: &'a mut PlayerInventory, - open_container: Option<&'a mut Box>, -) -> OptionallyCombinedContainer<'a> { - OptionallyCombinedContainer::new(inventory, open_container) } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 9fd2e0dad..b734ff3a8 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -15,10 +15,10 @@ use pumpkin_protocol::{ }, position::WorldPosition, server::play::{ - SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, - SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, - SUseItemOn, + SChatCommand, SChatMessage, SClickContainer, SClientInformationPlay, SConfirmTeleport, + SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, + SPlayerPositionRotation, SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, + SSwingArm, SUseItem, SUseItemOn, }, ConnectionState, RawPacket, ServerPacket, VarInt, }; @@ -26,11 +26,7 @@ use pumpkin_world::item::ItemStack; use pumpkin_world::vector3::Vector3; use serde::{Deserialize, Serialize}; -use crate::{ - client::{authentication::GameProfile, Client}, - server::Server, - util::boundingbox::BoundingBox, -}; +use crate::{client::authentication::GameProfile, util::boundingbox::BoundingBox}; pub struct PlayerAbilities { pub invulnerable: bool, @@ -65,7 +61,7 @@ pub struct Player { pub food: i32, pub food_saturation: f32, pub inventory: PlayerInventory, - pub open_container: Option>, + pub open_container: Option, pub carried_item: Option, /// send `send_abilties_update` when changed @@ -406,9 +402,13 @@ impl Player { Ok(()) } SClickContainer::PACKET_ID => { - self.handle_click_container(SClickContainer::read(bytebuf).unwrap()) + self.handle_click_container(server, SClickContainer::read(bytebuf)?); + Ok(()) + } + _ => { + log::error!("Failed to handle player packet id {:#04x}", packet.id.0); + Ok(()) } - _ => log::error!("Failed to handle player packet id {:#04x}", packet.id.0), } } } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index c673cbc9c..6d5b008f8 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -29,16 +29,16 @@ use pumpkin_protocol::{ }; use pumpkin_world::{dimension::Dimension, radial_chunk_iterator::RadialIterator, World}; -use pumpkin_registry::Registry; -use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; -use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; - use crate::{ client::Client, config::{AdvancedConfiguration, BasicConfiguration}, entity::player::{GameMode, Player}, }; +use pumpkin_inventory::{Container, OpenContainer}; +use pumpkin_registry::Registry; +use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; +use serde::{Deserialize, Serialize}; +use tokio::sync::mpsc; pub const CURRENT_MC_VERSION: &str = "1.21.1"; @@ -63,6 +63,7 @@ pub struct Server { // TODO: place this into every world pub current_players: HashMap, Arc>>, + pub open_containers: HashMap, entity_id: AtomicI32, pub base_config: BasicConfiguration, @@ -120,6 +121,7 @@ impl Server { status_response_json, public_key_der, current_players: HashMap::new(), + open_containers: HashMap::new(), base_config: config.0, auth_client, advanced_config: config.1, @@ -315,6 +317,14 @@ impl Server { None } + pub fn try_get_container( + &self, + player_id: EntityId, + container_id: u64, + ) -> Option>> { + self.open_containers.get(&container_id)?.try_open(player_id) + } + /// Sends a Packet to all Players pub fn broadcast_packet

(&self, from: &mut Player, packet: &P) where