a bit of refactor, and actually enforce player inventory condition rules on handle_item_change
This commit is contained in:
parent
3b6fc48a0f
commit
59a950c606
@ -52,20 +52,14 @@ pub enum WindowType {
|
||||
CartographyTable,
|
||||
Stonecutter,
|
||||
}
|
||||
pub struct ContainerStruct<const SLOTS: usize>([Option<ItemStack>; 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<ItemStack>,
|
||||
@ -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<ItemStack>> {
|
||||
let slots = match &mut self.container {
|
||||
Some(container) => {
|
||||
|
||||
@ -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<ItemStack>> {
|
||||
self.0.iter_mut().collect()
|
||||
}
|
||||
|
||||
@ -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<ItemStack>, 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<ItemStack>,
|
||||
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<Box<dyn Fn(&ItemStack) -> 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<ItemStack>, 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<ItemStack>,
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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 => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user