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.
This commit is contained in:
parent
ada8adc636
commit
53c3a013c2
@ -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<ItemStack>,
|
||||
slot: usize,
|
||||
mouse_click: MouseClick,
|
||||
);
|
||||
) {
|
||||
handle_item_take(carried_item, self.all_slots()[slot], mouse_click)
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>>;
|
||||
|
||||
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>;
|
||||
}
|
||||
|
||||
pub fn handle_item_take(
|
||||
@ -180,6 +187,10 @@ impl<const T: usize> Container for ContainerStruct<T> {
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
||||
self.slots.iter_mut().collect()
|
||||
}
|
||||
|
||||
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
|
||||
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<ItemStack>,
|
||||
slot: usize,
|
||||
mouse_click: MouseClick,
|
||||
) {
|
||||
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
|
||||
let mut slots = if let Some(container) = &self.container {
|
||||
container.all_slots_ref()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
slots.extend(self.inventory.all_slots_ref());
|
||||
slots
|
||||
}
|
||||
}
|
||||
|
||||
46
pumpkin-inventory/src/open_container.rs
Normal file
46
pumpkin-inventory/src/open_container.rs
Normal file
@ -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<i32>,
|
||||
container: Mutex<Box<dyn Container>>,
|
||||
}
|
||||
|
||||
impl OpenContainer {
|
||||
pub fn try_open(&self, player_id: i32) -> Option<MutexGuard<Box<dyn Container>>> {
|
||||
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<ItemStack>; 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<ItemStack>> {
|
||||
self.0.iter_mut().collect()
|
||||
}
|
||||
|
||||
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
|
||||
self.0.iter().map(|slot| slot.as_ref()).collect()
|
||||
}
|
||||
}
|
||||
@ -148,4 +148,8 @@ impl Container for PlayerInventory {
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
||||
self.slots_mut()
|
||||
}
|
||||
|
||||
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
|
||||
self.slots()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Vec<Option<&ItemStack>>>,
|
||||
@ -45,7 +46,7 @@ impl Player {
|
||||
|
||||
pub fn set_container_content<'a>(
|
||||
&mut self,
|
||||
window_type: WindowType,
|
||||
window_type: &WindowType,
|
||||
items: Option<Vec<Option<&'a ItemStack>>>,
|
||||
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<dyn Container>>,
|
||||
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<dyn Container>>,
|
||||
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<dyn Container>>,
|
||||
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<dyn Container>>,
|
||||
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<const SLOTS: usize> ContainerStruct<SLOTS> {
|
||||
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<dyn Container>>,
|
||||
) -> OptionallyCombinedContainer<'a> {
|
||||
OptionallyCombinedContainer::new(inventory, open_container)
|
||||
}
|
||||
|
||||
@ -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<Box<dyn Container>>,
|
||||
pub open_container: Option<u64>,
|
||||
pub carried_item: Option<ItemStack>,
|
||||
|
||||
/// 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Token>, Arc<Mutex<Player>>>,
|
||||
pub open_containers: HashMap<u64, OpenContainer>,
|
||||
|
||||
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<MutexGuard<Box<dyn Container>>> {
|
||||
self.open_containers.get(&container_id)?.try_open(player_id)
|
||||
}
|
||||
|
||||
/// Sends a Packet to all Players
|
||||
pub fn broadcast_packet<P>(&self, from: &mut Player, packet: &P)
|
||||
where
|
||||
|
||||
Loading…
Reference in New Issue
Block a user