add drag and error handling for containers
This commit is contained in:
parent
28292439de
commit
bb6677dd19
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1967,9 +1967,11 @@ dependencies = [
|
||||
name = "pumpkin-inventory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-world",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -6,6 +6,7 @@ edition.workspace = true
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
|
||||
thiserror = "1.0.63"
|
||||
itertools = "0.13.0"
|
||||
# For items
|
||||
pumpkin-world = { path = "../pumpkin-world"}
|
||||
|
||||
@ -83,63 +83,21 @@ impl Click {
|
||||
}
|
||||
|
||||
fn new_drag_item(button: i8, slot: i16) -> Self {
|
||||
let (mouse_type, state, slot) = match button {
|
||||
0 => (
|
||||
MouseDragType::Left,
|
||||
MouseDragState::Start,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
1 => (
|
||||
MouseDragType::Left,
|
||||
MouseDragState::AddSlot,
|
||||
Slot::Normal(slot.try_into().unwrap()),
|
||||
),
|
||||
2 => (
|
||||
MouseDragType::Left,
|
||||
MouseDragState::End,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
4 => (
|
||||
MouseDragType::Right,
|
||||
MouseDragState::Start,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
5 => (
|
||||
MouseDragType::Right,
|
||||
MouseDragState::AddSlot,
|
||||
Slot::Normal(slot.try_into().unwrap()),
|
||||
),
|
||||
6 => (
|
||||
MouseDragType::Right,
|
||||
MouseDragState::End,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
|
||||
// ONLY FOR CREATIVE
|
||||
8 => (
|
||||
MouseDragType::Middle,
|
||||
MouseDragState::Start,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
9 => (
|
||||
MouseDragType::Middle,
|
||||
MouseDragState::AddSlot,
|
||||
Slot::Normal(slot.try_into().unwrap()),
|
||||
),
|
||||
10 => (
|
||||
MouseDragType::Middle,
|
||||
MouseDragState::End,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
let state = match button {
|
||||
0 => MouseDragState::Start(MouseDragType::Left),
|
||||
4 => MouseDragState::Start(MouseDragType::Right),
|
||||
8 => MouseDragState::Start(MouseDragType::Middle),
|
||||
1 | 5 | 9 => MouseDragState::AddSlot(slot.try_into().unwrap()),
|
||||
2 | 6 | 10 => MouseDragState::End,
|
||||
// TODO: Error handling
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Self {
|
||||
click_type: ClickType::MouseDrag {
|
||||
drag_state: state,
|
||||
drag_type: mouse_type,
|
||||
slot: match &state {
|
||||
MouseDragState::AddSlot(slot) => Slot::Normal(*slot),
|
||||
_ => Slot::OutsideInventory,
|
||||
},
|
||||
slot,
|
||||
click_type: ClickType::MouseDrag { drag_state: state },
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,10 +108,7 @@ pub enum ClickType {
|
||||
KeyClick(KeyClick),
|
||||
CreativePickItem,
|
||||
DropType(DropType),
|
||||
MouseDrag {
|
||||
drag_state: MouseDragState,
|
||||
drag_type: MouseDragType,
|
||||
},
|
||||
MouseDrag { drag_state: MouseDragState },
|
||||
DoubleClick,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@ -176,7 +131,7 @@ pub enum DropType {
|
||||
SingleItem,
|
||||
FullStack,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MouseDragType {
|
||||
Left,
|
||||
Right,
|
||||
@ -184,8 +139,8 @@ pub enum MouseDragType {
|
||||
}
|
||||
|
||||
pub enum MouseDragState {
|
||||
Start,
|
||||
AddSlot,
|
||||
Start(MouseDragType),
|
||||
AddSlot(usize),
|
||||
End,
|
||||
}
|
||||
|
||||
|
||||
178
pumpkin-inventory/src/drag_handler.rs
Normal file
178
pumpkin-inventory/src/drag_handler.rs
Normal file
@ -0,0 +1,178 @@
|
||||
use crate::container_click::MouseDragType;
|
||||
use crate::{Container, InventoryError};
|
||||
use itertools::Itertools;
|
||||
use num_traits::Euclid;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use std::collections::HashMap;
|
||||
use std::iter::{Enumerate, Filter, TakeWhile};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::vec::IntoIter;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DragHandler(RwLock<HashMap<u64, Arc<Mutex<Drag>>>>);
|
||||
|
||||
impl DragHandler {
|
||||
pub fn new() -> Self {
|
||||
Self(RwLock::new(HashMap::new()))
|
||||
}
|
||||
pub fn new_drag(
|
||||
&self,
|
||||
container_id: u64,
|
||||
player: i32,
|
||||
drag_type: MouseDragType,
|
||||
) -> Result<(), InventoryError> {
|
||||
let drag = Drag {
|
||||
player,
|
||||
drag_type,
|
||||
slots: vec![],
|
||||
};
|
||||
let mut drags = match self.0.write() {
|
||||
Ok(drags) => drags,
|
||||
Err(_) => Err(InventoryError::LockError)?,
|
||||
};
|
||||
drags.insert(container_id, Arc::new(Mutex::new(drag)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_slot(
|
||||
&self,
|
||||
container_id: u64,
|
||||
player: i32,
|
||||
slot: usize,
|
||||
) -> Result<(), InventoryError> {
|
||||
let drags = match self.0.read() {
|
||||
Ok(drags) => drags,
|
||||
Err(_) => Err(InventoryError::LockError)?,
|
||||
};
|
||||
match drags.get(&container_id) {
|
||||
Some(drag) => {
|
||||
let mut drag = drag.lock().unwrap();
|
||||
if drag.player != player {
|
||||
Err(InventoryError::MultiplePlayersDragging)?
|
||||
}
|
||||
if !drag.slots.contains(&slot) {
|
||||
drag.slots.push(slot);
|
||||
}
|
||||
}
|
||||
None => Err(InventoryError::OutOfOrderDragging)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_drag<T: Container>(
|
||||
&self,
|
||||
carried_item: &mut Option<ItemStack>,
|
||||
container: &mut T,
|
||||
container_id: &u64,
|
||||
player: i32,
|
||||
) -> Result<(), InventoryError> {
|
||||
// Minecraft client does still send dragging packets when not carrying an item!
|
||||
if carried_item.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Ok(mut drags) = self.0.write() else {
|
||||
Err(InventoryError::LockError)?
|
||||
};
|
||||
let Some((_, drag)) = drags.remove_entry(container_id) else {
|
||||
Err(InventoryError::OutOfOrderDragging)?
|
||||
};
|
||||
let mut drag = drag.lock().unwrap();
|
||||
|
||||
if player != drag.player {
|
||||
Err(InventoryError::MultiplePlayersDragging)?
|
||||
}
|
||||
let mut slots = container.all_slots();
|
||||
let slots_cloned = slots
|
||||
.iter()
|
||||
.map(|stack| stack.map(|item| item.to_owned()))
|
||||
.collect_vec();
|
||||
match drag.drag_type {
|
||||
// This is only valid in Creative GameMode.
|
||||
// Checked in any function that uses this function.
|
||||
MouseDragType::Middle => {
|
||||
for slot in &drag.slots {
|
||||
*slots[*slot] = carried_item.clone();
|
||||
}
|
||||
}
|
||||
MouseDragType::Right => {
|
||||
let amount_of_items = carried_item.unwrap().item_count as usize;
|
||||
let mut single_item = carried_item.clone().unwrap();
|
||||
single_item.item_count = 1;
|
||||
let changing_slots = drag.changing_slots(
|
||||
amount_of_items,
|
||||
&slots_cloned,
|
||||
carried_item.as_ref().unwrap(),
|
||||
);
|
||||
let mut amount_removed = 0;
|
||||
changing_slots.for_each(|slot| {
|
||||
amount_removed += 1;
|
||||
*slots[slot] = Some(single_item.clone())
|
||||
});
|
||||
|
||||
let mut remaining = carried_item.unwrap();
|
||||
if remaining.item_count == amount_removed {
|
||||
*carried_item = None
|
||||
} else {
|
||||
remaining.item_count -= amount_removed;
|
||||
*carried_item = Some(remaining)
|
||||
}
|
||||
}
|
||||
MouseDragType::Left => {
|
||||
let amount_of_items = carried_item.unwrap().item_count as usize;
|
||||
// TODO: Handle dragging a stack with greater amount than item allows as max unstackable
|
||||
// In that specific case, follow MouseDragType::Right behaviours instead!
|
||||
|
||||
let changing_slots = drag.changing_slots(
|
||||
amount_of_items,
|
||||
&slots_cloned,
|
||||
carried_item.as_ref().unwrap(),
|
||||
);
|
||||
let amount_of_slots = changing_slots.clone().count();
|
||||
let (amount_per_slot, remainder) =
|
||||
(carried_item.unwrap().item_count as usize).div_rem_euclid(&amount_of_slots);
|
||||
let mut item_in_each_slot = carried_item.unwrap().clone();
|
||||
item_in_each_slot.item_count = amount_per_slot as u8;
|
||||
changing_slots.for_each(|slot| *slots[slot] = Some(item_in_each_slot.clone()));
|
||||
|
||||
if remainder > 0 {
|
||||
let mut remaining = carried_item.unwrap().clone();
|
||||
remaining.item_count = remainder as u8;
|
||||
*carried_item = Some(remaining)
|
||||
} else {
|
||||
*carried_item = None
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Drag {
|
||||
player: i32,
|
||||
drag_type: MouseDragType,
|
||||
slots: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Drag {
|
||||
fn changing_slots<'a>(
|
||||
&'a self,
|
||||
amount_of_items: usize,
|
||||
slots: &'a [Option<ItemStack>],
|
||||
carried_item: &'a ItemStack,
|
||||
) -> impl Iterator<Item = usize> + 'a + Clone {
|
||||
self.slots
|
||||
.iter()
|
||||
.enumerate()
|
||||
.take_while(move |(slot_number, _)| *slot_number <= amount_of_items)
|
||||
.filter_map(move |(_, slot)| match &slots[*slot] {
|
||||
Some(item_slot) => {
|
||||
if *item_slot == *carried_item {
|
||||
Some(*slot)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => Some(*slot),
|
||||
})
|
||||
}
|
||||
}
|
||||
33
pumpkin-inventory/src/error.rs
Normal file
33
pumpkin-inventory/src/error.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum InventoryError {
|
||||
#[error("Unable to lock")]
|
||||
LockError,
|
||||
#[error("Invalid slot")]
|
||||
InvalidSlot,
|
||||
#[error("Player '{0}' tried to interact with a closed container")]
|
||||
ClosedContainerInteract(i32),
|
||||
#[error("Multiple players dragging in a container at once")]
|
||||
MultiplePlayersDragging,
|
||||
#[error("Out of order dragging")]
|
||||
OutOfOrderDragging,
|
||||
#[error("Invalid inventory packet")]
|
||||
InvalidPacket,
|
||||
#[error("Player does not have enough permissions")]
|
||||
PermissionError,
|
||||
}
|
||||
|
||||
impl InventoryError {
|
||||
pub fn should_kick(&self) -> bool {
|
||||
match self {
|
||||
InventoryError::InvalidSlot
|
||||
| InventoryError::ClosedContainerInteract(..)
|
||||
| InventoryError::InvalidPacket
|
||||
| InventoryError::PermissionError => true,
|
||||
InventoryError::LockError
|
||||
| InventoryError::OutOfOrderDragging
|
||||
| InventoryError::MultiplePlayersDragging => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
use crate::container_click::MouseClick;
|
||||
use crate::container_click::{MouseClick, MouseDragState, MouseDragType};
|
||||
use crate::player::PlayerInventory;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
|
||||
pub mod container_click;
|
||||
pub mod drag_handler;
|
||||
mod error;
|
||||
mod open_container;
|
||||
pub mod player;
|
||||
pub mod window_property;
|
||||
|
||||
pub use error::InventoryError;
|
||||
pub use open_container::OpenContainer;
|
||||
|
||||
/// https://wiki.vg/Inventory
|
||||
@ -68,8 +71,13 @@ pub trait Container: Sync + Send {
|
||||
carried_item: &mut Option<ItemStack>,
|
||||
slot: usize,
|
||||
mouse_click: MouseClick,
|
||||
) {
|
||||
handle_item_change(carried_item, self.all_slots()[slot], mouse_click)
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut all_slots = self.all_slots();
|
||||
if slot < all_slots.len() {
|
||||
Err(InventoryError::InvalidSlot)?
|
||||
}
|
||||
handle_item_change(carried_item, all_slots[slot], mouse_click);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>>;
|
||||
@ -98,6 +106,10 @@ pub trait Container: Sync + Send {
|
||||
dbg!(slot, item);
|
||||
});
|
||||
}
|
||||
|
||||
fn internal_pumpkin_id(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_item_take(
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::drag_handler::DragHandler;
|
||||
use crate::{Container, WindowType};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub struct OpenContainer {
|
||||
players: Vec<i32>,
|
||||
@ -8,21 +9,12 @@ pub struct OpenContainer {
|
||||
}
|
||||
|
||||
impl OpenContainer {
|
||||
pub fn try_open(&self, player_id: i32) -> Option<MutexGuard<Box<dyn Container>>> {
|
||||
pub fn try_open(&self, player_id: i32) -> Option<&Mutex<Box<dyn Container>>> {
|
||||
if !self.players.contains(&player_id) {
|
||||
dbg!("couldn't open container");
|
||||
return None;
|
||||
}
|
||||
let container = self.container.lock().unwrap();
|
||||
container
|
||||
.all_slots_ref()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(slot, item)| {
|
||||
if let Some(item) = item {
|
||||
dbg!(slot, item);
|
||||
}
|
||||
});
|
||||
let container = &self.container;
|
||||
Some(container)
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::container_click::MouseClick;
|
||||
use crate::{handle_item_change, Container, WindowType};
|
||||
use crate::drag_handler::DragHandler;
|
||||
use crate::{handle_item_change, Container, InventoryError, WindowType};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -92,17 +93,17 @@ impl PlayerInventory {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
pub fn get_slot(&mut self, slot: usize) -> &mut Option<ItemStack> {
|
||||
pub fn get_slot(&mut self, slot: usize) -> Result<&mut Option<ItemStack>, InventoryError> {
|
||||
match slot {
|
||||
0 => {
|
||||
// TODO: Add crafting check here
|
||||
&mut self.crafting_output
|
||||
Ok(&mut self.crafting_output)
|
||||
}
|
||||
1..=4 => &mut self.crafting[slot - 1],
|
||||
5..=8 => &mut self.armor[slot - 5],
|
||||
9..=44 => &mut self.items[slot - 9],
|
||||
45 => &mut self.offhand,
|
||||
_ => unreachable!(),
|
||||
1..=4 => Ok(&mut self.crafting[slot - 1]),
|
||||
5..=8 => Ok(&mut self.armor[slot - 5]),
|
||||
9..=44 => Ok(&mut self.items[slot - 9]),
|
||||
45 => Ok(&mut self.offhand),
|
||||
_ => Err(InventoryError::InvalidSlot),
|
||||
}
|
||||
}
|
||||
pub fn set_selected(&mut self, slot: usize) {
|
||||
@ -144,9 +145,11 @@ impl Container for PlayerInventory {
|
||||
carried_slot: &mut Option<ItemStack>,
|
||||
slot: usize,
|
||||
mouse_click: MouseClick,
|
||||
) {
|
||||
let item_slot = self.get_slot(slot);
|
||||
handle_item_change(carried_slot, item_slot, mouse_click)
|
||||
) -> Result<(), InventoryError> {
|
||||
let item_slot = self.get_slot(slot)?;
|
||||
|
||||
handle_item_change(carried_slot, item_slot, mouse_click);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
||||
|
||||
@ -18,3 +18,9 @@ pub struct ItemStack {
|
||||
pub item_id: u32,
|
||||
// TODO: Add Item Components
|
||||
}
|
||||
|
||||
impl PartialEq for ItemStack {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.item_id == other.item_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use itertools::Itertools;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_inventory::container_click::{KeyClick, MouseClick};
|
||||
use pumpkin_core::GameMode;
|
||||
use pumpkin_inventory::container_click::{KeyClick, MouseClick, MouseDragState, MouseDragType};
|
||||
use pumpkin_inventory::drag_handler::DragHandler;
|
||||
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
|
||||
use pumpkin_inventory::{container_click, OptionallyCombinedContainer};
|
||||
use pumpkin_inventory::{container_click, InventoryError, OptionallyCombinedContainer};
|
||||
use pumpkin_inventory::{Container, WindowType};
|
||||
use pumpkin_protocol::client::play::{
|
||||
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||
@ -10,10 +14,7 @@ use pumpkin_protocol::client::play::{
|
||||
use pumpkin_protocol::server::play::SClickContainer;
|
||||
use pumpkin_protocol::slot::Slot;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
impl Player {
|
||||
pub fn open_container(
|
||||
@ -22,8 +23,11 @@ impl Player {
|
||||
minecraft_menu_id: &str,
|
||||
window_title: Option<&str>,
|
||||
) {
|
||||
let mut container = self.get_open_container(server);
|
||||
let container = container.as_deref_mut();
|
||||
self.inventory.reset_state_id();
|
||||
let total_opened_containers = self.inventory.total_opened_containers;
|
||||
let mut container = self
|
||||
.get_open_container(server)
|
||||
.map(|container| container.lock().unwrap());
|
||||
let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY
|
||||
.get("minecraft:menu")
|
||||
.unwrap()
|
||||
@ -40,14 +44,12 @@ impl Player {
|
||||
.to_owned();
|
||||
let title = TextComponent::text(window_title.unwrap_or(window_type.default_title()));
|
||||
|
||||
self.inventory.reset_state_id();
|
||||
|
||||
self.client.send_packet(&COpenScreen::new(
|
||||
self.inventory.total_opened_containers.into(),
|
||||
total_opened_containers.into(),
|
||||
menu_protocol_id,
|
||||
title,
|
||||
));
|
||||
self.set_container_content(container);
|
||||
self.set_container_content(container.as_deref_mut());
|
||||
}
|
||||
|
||||
pub fn set_container_content(&mut self, container: Option<&mut Box<dyn Container>>) {
|
||||
@ -113,33 +115,35 @@ impl Player {
|
||||
));
|
||||
}
|
||||
|
||||
pub fn handle_click_container(&mut self, server: &mut Server, packet: SClickContainer) {
|
||||
pub fn handle_click_container(
|
||||
&mut self,
|
||||
server: &mut Server,
|
||||
packet: SClickContainer,
|
||||
) -> Result<(), InventoryError> {
|
||||
use container_click::*;
|
||||
let mut opened_container = self.get_open_container(server);
|
||||
let opened_container = opened_container.as_deref_mut();
|
||||
let mut opened_container = self
|
||||
.get_open_container(server)
|
||||
.map(|container| container.lock().unwrap());
|
||||
let mut opened_container = opened_container.as_deref_mut();
|
||||
let drag_handler = &server.drag_handler;
|
||||
|
||||
let current_state_id = if let Some(container) = opened_container.as_ref() {
|
||||
container.state_id()
|
||||
} else {
|
||||
self.inventory.state_id()
|
||||
};
|
||||
// This is just checking for regular desync, client hasn't done anything malicious
|
||||
if current_state_id != packet.state_id.0 {
|
||||
dbg!(current_state_id, packet.state_id.0);
|
||||
//self.set_container_content(opened_container.as_deref_mut());
|
||||
//return;
|
||||
self.set_container_content(opened_container.as_deref_mut());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if opened_container.is_some() {
|
||||
if packet.window_id != self.inventory.total_opened_containers {
|
||||
dbg!(
|
||||
self.inventory.total_opened_containers,
|
||||
opened_container.unwrap().window_type()
|
||||
);
|
||||
return;
|
||||
return Err(InventoryError::ClosedContainerInteract(self.entity_id()));
|
||||
}
|
||||
} else if packet.window_id != 0 {
|
||||
dbg!("weird");
|
||||
return;
|
||||
return Err(InventoryError::ClosedContainerInteract(self.entity_id()));
|
||||
}
|
||||
|
||||
let click = Click::new(
|
||||
@ -160,26 +164,27 @@ impl Player {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
self.number_button_pressed(opened_container, key_click, slot)
|
||||
}
|
||||
container_click::Slot::OutsideInventory => {
|
||||
unimplemented!("This is not a valid state")
|
||||
}
|
||||
container_click::Slot::OutsideInventory => Err(InventoryError::InvalidPacket),
|
||||
},
|
||||
ClickType::CreativePickItem => {
|
||||
if let container_click::Slot::Normal(slot) = click.slot {
|
||||
self.creative_pick_item(opened_container, slot)
|
||||
} else {
|
||||
Err(InventoryError::InvalidPacket)
|
||||
}
|
||||
}
|
||||
ClickType::DoubleClick => {
|
||||
if let container_click::Slot::Normal(slot) = click.slot {
|
||||
self.double_click(slot)
|
||||
} else {
|
||||
Err(InventoryError::InvalidPacket)
|
||||
}
|
||||
}
|
||||
ClickType::MouseDrag {
|
||||
drag_state: _,
|
||||
drag_type: _,
|
||||
} => (),
|
||||
ClickType::DropType(_drop_type) => (),
|
||||
};
|
||||
ClickType::MouseDrag { drag_state } => {
|
||||
self.mouse_drag(drag_handler, opened_container, drag_state)
|
||||
}
|
||||
ClickType::DropType(_drop_type) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_click(
|
||||
@ -187,22 +192,22 @@ impl Player {
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
mouse_click: MouseClick,
|
||||
slot: container_click::Slot,
|
||||
) {
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
|
||||
match slot {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
container.handle_item_change(&mut self.carried_item, slot, mouse_click)
|
||||
}
|
||||
container_click::Slot::OutsideInventory => (),
|
||||
};
|
||||
container_click::Slot::OutsideInventory => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn shift_mouse_click(
|
||||
&mut self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
slot: container_click::Slot,
|
||||
) {
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
|
||||
match slot {
|
||||
@ -235,13 +240,14 @@ impl Player {
|
||||
if let Some(slot) = slots {
|
||||
let mut item_slot = container.all_slots()[slot].map(|i| i.to_owned());
|
||||
|
||||
container.handle_item_change(&mut item_slot, slot, MouseClick::Left);
|
||||
container.handle_item_change(&mut item_slot, slot, MouseClick::Left)?;
|
||||
*container.all_slots()[slot] = item_slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
container_click::Slot::OutsideInventory => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn number_button_pressed(
|
||||
@ -249,35 +255,74 @@ impl Player {
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
key_click: KeyClick,
|
||||
slot: usize,
|
||||
) {
|
||||
) -> Result<(), InventoryError> {
|
||||
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 changing_item_slot = self.inventory.get_slot(changing_slot as usize)?.to_owned();
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
|
||||
container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left);
|
||||
*self.inventory.get_slot(changing_slot as usize) = changing_item_slot
|
||||
container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left)?;
|
||||
*self.inventory.get_slot(changing_slot as usize)? = changing_item_slot;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn creative_pick_item(
|
||||
&mut self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
slot: usize,
|
||||
) {
|
||||
) -> Result<(), InventoryError> {
|
||||
if self.gamemode != GameMode::Creative {
|
||||
return Err(InventoryError::PermissionError);
|
||||
}
|
||||
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())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn double_click(&mut self, _slot: usize) {}
|
||||
fn double_click(&mut self, _slot: usize) -> Result<(), InventoryError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mouse_drag(
|
||||
&mut self,
|
||||
drag_handler: &DragHandler,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
mouse_drag_state: MouseDragState,
|
||||
) -> Result<(), InventoryError> {
|
||||
let player_id = self.entity_id();
|
||||
let container_id = opened_container
|
||||
.as_ref()
|
||||
.map(|container| container.internal_pumpkin_id())
|
||||
.unwrap_or(player_id as u64);
|
||||
match mouse_drag_state {
|
||||
MouseDragState::Start(drag_type) => {
|
||||
drag_handler.new_drag(container_id, player_id, drag_type)?
|
||||
}
|
||||
MouseDragState::AddSlot(slot) => {
|
||||
drag_handler.add_slot(container_id, player_id, slot)?
|
||||
}
|
||||
MouseDragState::End => {
|
||||
let mut container =
|
||||
OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
drag_handler.apply_drag(
|
||||
&mut self.carried_item,
|
||||
&mut container,
|
||||
&container_id,
|
||||
player_id,
|
||||
)?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_open_container<'a>(
|
||||
&self,
|
||||
server: &'a Server,
|
||||
) -> Option<MutexGuard<'a, Box<dyn Container>>> {
|
||||
) -> Option<&'a Mutex<Box<dyn Container>>> {
|
||||
if let Some(id) = self.open_container {
|
||||
server.try_get_container(self.entity_id(), id)
|
||||
} else {
|
||||
|
||||
@ -22,6 +22,7 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{client::Client, entity::player::Player, world::World};
|
||||
use pumpkin_inventory::drag_handler::DragHandler;
|
||||
use pumpkin_inventory::{Container, OpenContainer};
|
||||
use pumpkin_registry::Registry;
|
||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
@ -48,6 +49,7 @@ pub struct Server {
|
||||
pub cached_registry: Vec<Registry>,
|
||||
|
||||
pub open_containers: HashMap<u64, OpenContainer>,
|
||||
pub drag_handler: DragHandler,
|
||||
entity_id: AtomicI32,
|
||||
|
||||
/// Used for Authentication, None is Online mode is disabled
|
||||
@ -94,6 +96,7 @@ impl Server {
|
||||
plugin_loader,
|
||||
cached_registry: Registry::get_static(),
|
||||
open_containers: HashMap::new(),
|
||||
drag_handler: DragHandler::new(),
|
||||
// 0 is invalid
|
||||
entity_id: 2.into(),
|
||||
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
|
||||
@ -134,8 +137,11 @@ impl Server {
|
||||
&self,
|
||||
player_id: EntityId,
|
||||
container_id: u64,
|
||||
) -> Option<MutexGuard<'_, Box<dyn Container>>> {
|
||||
self.open_containers.get(&container_id)?.try_open(player_id)
|
||||
) -> Option<&Mutex<Box<dyn Container>>> {
|
||||
self.open_containers
|
||||
.get(&container_id)?
|
||||
.try_open(player_id)
|
||||
.map(|container| container)
|
||||
}
|
||||
|
||||
/// Sends a Packet to all Players in all worlds
|
||||
|
||||
Loading…
Reference in New Issue
Block a user