diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index 100790a76..e1e554fae 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -52,20 +52,14 @@ pub enum WindowType { CartographyTable, Stonecutter, } +pub struct ContainerStruct([Option; SLOTS]); -impl WindowType { - pub const fn default_title(&self) -> &'static str { - // TODO: Add titles here: - /*match self { - _ => "WINDOW TITLE", - }*/ - "WINDOW TITLE" - } -} // Container needs Sync + Send to be able to be in async Server pub trait Container: Sync + Send { fn window_type(&self) -> &'static WindowType; + fn window_name(&self) -> &'static str; + fn handle_item_change( &mut self, carried_item: &mut Option, @@ -211,6 +205,13 @@ impl<'a> Container for OptionallyCombinedContainer<'a, 'a> { } } + fn window_name(&self) -> &'static str { + self.container + .as_ref() + .map(|container| container.window_name()) + .unwrap_or(self.inventory.window_name()) + } + fn all_slots(&mut self) -> Vec<&mut Option> { let slots = match &mut self.container { Some(container) => { diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 3ca446e25..df052755f 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -59,6 +59,9 @@ impl Container for Chest { &WindowType::Generic9x3 } + fn window_name(&self) -> &'static str { + "Chest" + } fn all_slots(&mut self) -> Vec<&mut Option> { self.0.iter_mut().collect() } diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index a3611229c..d8e127b78 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -36,7 +36,6 @@ impl PlayerInventory { total_opened_containers: 2, } } - /// Set the contents of an item in a slot /// /// ## Slot @@ -48,48 +47,44 @@ impl PlayerInventory { /// ## Item allowed override /// An override, which when enabled, makes it so that invalid items, can be placed in slots they normally can't. /// Useful functionality for plugins in the future. - pub fn set_slot(&mut self, slot: usize, item: Option, item_allowed_override: bool) { - match slot { - 0 => { - // TODO: Add crafting check here - self.crafting_output = item + pub fn set_slot( + &mut self, + slot: usize, + item: Option, + item_allowed_override: bool, + ) -> Result<(), InventoryError> { + if item_allowed_override { + if !(0..=45).contains(&slot) { + Err(InventoryError::InvalidSlot)? } - 1..=4 => self.crafting[slot - 1] = item, - 5..=8 => { - match item { - None => self.armor[slot - 5] = None, - Some(item) => { - // TODO: Replace asserts with error handling - match slot - 5 { - 0 => { - assert!(item.is_helmet() || item_allowed_override); - self.armor[0] = Some(item); - } - 1 => { - assert!(item.is_chestplate() || item_allowed_override); - self.armor[1] = Some(item) - } - 2 => { - assert!(item.is_leggings() || item_allowed_override); - self.armor[2] = Some(item); - } - 3 => { - assert!(item.is_boots() || item_allowed_override); - self.armor[3] = Some(item) - } - _ => unreachable!(), - } - } - } - } - 9..=44 => { - self.items[slot - 9] = item; - } - 45 => { - self.offhand = item; - } - _ => unreachable!(), + *self.all_slots()[slot] = item; + return Ok(()); } + let slot_condition = self.slot_condition(slot)?; + if let Some(item) = item { + if slot_condition(&item) { + *self.all_slots()[slot] = Some(item); + } + } + Ok(()) + } + #[allow(clippy::type_complexity)] + pub fn slot_condition( + &self, + slot: usize, + ) -> Result bool>, InventoryError> { + if !(0..=45).contains(&slot) { + return Err(InventoryError::InvalidSlot); + } + + Ok(Box::new(match slot { + 0..=4 | 9..=45 => |_| true, + 5 => |item: &ItemStack| item.is_helmet(), + 6 => |item: &ItemStack| item.is_chestplate(), + 7 => |item: &ItemStack| item.is_leggings(), + 8 => |item: &ItemStack| item.is_boots(), + _ => unreachable!(), + })) } pub fn get_slot(&mut self, slot: usize) -> Result<&mut Option, InventoryError> { match slot { @@ -138,15 +133,26 @@ impl Container for PlayerInventory { &WindowType::Generic9x1 } + fn window_name(&self) -> &'static str { + // We never send an OpenContainer with inventory, so it has no name. + "" + } + fn handle_item_change( &mut self, carried_slot: &mut Option, slot: usize, mouse_click: MouseClick, ) -> Result<(), InventoryError> { + let slot_condition = self.slot_condition(slot)?; let item_slot = self.get_slot(slot)?; - - handle_item_change(carried_slot, item_slot, mouse_click); + if let Some(item) = carried_slot { + if slot_condition(item) { + handle_item_change(carried_slot, item_slot, mouse_click); + } + } else { + handle_item_change(carried_slot, item_slot, mouse_click) + } Ok(()) } diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 954b9ba44..69c1c5dd1 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -8,8 +8,8 @@ use pumpkin_inventory::container_click::{ }; use pumpkin_inventory::drag_handler::DragHandler; use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait}; +use pumpkin_inventory::Container; use pumpkin_inventory::{container_click, InventoryError, OptionallyCombinedContainer}; -use pumpkin_inventory::{Container, WindowType}; use pumpkin_protocol::client::play::{ CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, }; @@ -19,12 +19,7 @@ use pumpkin_world::item::ItemStack; use std::sync::{Arc, Mutex}; impl Player { - pub fn open_container( - &mut self, - server: &mut Server, - minecraft_menu_id: &str, - window_title: Option<&str>, - ) { + pub fn open_container(&mut self, server: &mut Server, minecraft_menu_id: &str) { self.inventory.state_id = 0; let total_opened_containers = self.inventory.total_opened_containers; let mut container = self @@ -39,12 +34,11 @@ impl Player { .get("protocol_id") .unwrap()) .into(); - let window_type = match &container { - Some(container) => container.window_type(), - None => &WindowType::Generic9x1, - } - .to_owned(); - let title = TextComponent::text(window_title.unwrap_or(window_type.default_title())); + let window_title = container + .as_ref() + .map(|container| container.window_name()) + .unwrap_or(self.inventory.window_name()); + let title = TextComponent::text(window_title); self.client.send_packet(&COpenScreen::new( total_opened_containers.into(), diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 41262b733..034e8d1b7 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -14,7 +14,7 @@ use pumpkin_core::{ GameMode, }; use pumpkin_entity::EntityId; -use pumpkin_inventory::WindowType; +use pumpkin_inventory::{InventoryError, WindowType}; use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem}; use pumpkin_protocol::{ client::play::{ @@ -517,15 +517,16 @@ impl Player { self.inventory.set_selected(slot as usize); } - pub fn handle_set_creative_slot(&mut self, _server: &mut Server, packet: SSetCreativeSlot) { + pub fn handle_set_creative_slot( + &mut self, + _server: &mut Server, + packet: SSetCreativeSlot, + ) -> Result<(), InventoryError> { if self.gamemode != GameMode::Creative { - self.kick(TextComponent::text( - "Invalid action, you can only do that if you are in creative", - )); - return; + return Err(InventoryError::PermissionError); } self.inventory - .set_slot(packet.slot as usize, packet.clicked_item.to_item(), false); + .set_slot(packet.slot as usize, packet.clicked_item.to_item(), false) } // TODO: diff --git a/pumpkin/src/commands/cmd_echest.rs b/pumpkin/src/commands/cmd_echest.rs index 594015cb6..d082ad1d3 100644 --- a/pumpkin/src/commands/cmd_echest.rs +++ b/pumpkin/src/commands/cmd_echest.rs @@ -21,7 +21,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { server.open_containers.insert(0, open_container); } } - player.open_container(server, "minecraft:generic_9x3", Some("Ender Chest")); + player.open_container(server, "minecraft:generic_9x3"); } Ok(()) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 3c7b05ee3..73c4b6585 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -438,7 +438,8 @@ impl Player { Ok(()) } SSetCreativeSlot::PACKET_ID => { - self.handle_set_creative_slot(server, SSetCreativeSlot::read(bytebuf)?); + self.handle_set_creative_slot(server, SSetCreativeSlot::read(bytebuf)?) + .unwrap(); Ok(()) } SPlayPingRequest::PACKET_ID => {