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:
Edvin Bryntesson 2024-08-31 11:41:13 +02:00
parent ada8adc636
commit 53c3a013c2
No known key found for this signature in database
6 changed files with 145 additions and 66 deletions

View File

@ -4,11 +4,14 @@ use num_derive::{FromPrimitive, ToPrimitive};
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
pub mod container_click; pub mod container_click;
mod open_container;
pub mod player; pub mod player;
pub mod window_property; pub mod window_property;
pub use open_container::OpenContainer;
/// https://wiki.vg/Inventory /// https://wiki.vg/Inventory
#[derive(Debug, ToPrimitive, FromPrimitive, Clone, Eq, PartialEq)] #[derive(Debug, ToPrimitive, FromPrimitive, Clone, Copy, Eq, PartialEq)]
pub enum WindowType { pub enum WindowType {
// not used // not used
Generic9x1, Generic9x1,
@ -56,8 +59,8 @@ impl WindowType {
"WINDOW TITLE" "WINDOW TITLE"
} }
} }
// Container needs Sync + Send to be able to be in async Server
pub trait Container { pub trait Container: Sync + Send {
fn window_type(&self) -> &WindowType; fn window_type(&self) -> &WindowType;
fn handle_item_change( fn handle_item_change(
@ -65,9 +68,13 @@ pub trait Container {
carried_item: &mut Option<ItemStack>, carried_item: &mut Option<ItemStack>,
slot: usize, slot: usize,
mouse_click: MouseClick, 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(&mut self) -> Vec<&mut Option<ItemStack>>;
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>;
} }
pub fn handle_item_take( 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>> { fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
self.slots.iter_mut().collect() 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> { pub struct OptionallyCombinedContainer<'a> {
@ -217,11 +228,13 @@ impl<'a> Container for OptionallyCombinedContainer<'a> {
slots slots
} }
fn handle_item_change( fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
&mut self, let mut slots = if let Some(container) = &self.container {
carried_item: &mut Option<ItemStack>, container.all_slots_ref()
slot: usize, } else {
mouse_click: MouseClick, vec![]
) { };
slots.extend(self.inventory.all_slots_ref());
slots
} }
} }

View 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()
}
}

View File

@ -148,4 +148,8 @@ impl Container for PlayerInventory {
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> { fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
self.slots_mut() self.slots_mut()
} }
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
self.slots()
}
} }

View File

@ -15,11 +15,12 @@ use pumpkin_protocol::slot::Slot;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::server::Server;
impl Player { impl Player {
pub fn open_container( pub fn open_container(
&mut self, &mut self,
window_type: WindowType, window_type: &WindowType,
minecraft_menu_id: &str, minecraft_menu_id: &str,
window_title: Option<&str>, window_title: Option<&str>,
items: Option<Vec<Option<&ItemStack>>>, items: Option<Vec<Option<&ItemStack>>>,
@ -45,7 +46,7 @@ impl Player {
pub fn set_container_content<'a>( pub fn set_container_content<'a>(
&mut self, &mut self,
window_type: WindowType, window_type: &WindowType,
items: Option<Vec<Option<&'a ItemStack>>>, items: Option<Vec<Option<&'a ItemStack>>>,
carried_item: Option<&'a ItemStack>, carried_item: Option<&'a ItemStack>,
) { ) {
@ -75,7 +76,7 @@ impl Player {
} }
}; };
let packet = 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); self.client.send_packet(&packet);
} }
@ -109,8 +110,15 @@ impl Player {
.send_packet(&CSetContainerProperty::new(window_type as u8, id, value)); .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::*; 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 { let click = Click {
state_id: packet.state_id.0.try_into().unwrap(), state_id: packet.state_id.0.try_into().unwrap(),
changed_items: packet changed_items: packet
@ -137,15 +145,19 @@ impl Player {
packet.slot, packet.slot,
), ),
}; };
match click.mode.click_type { match click.mode.click_type {
ClickType::MouseClick(mouse_click) => { ClickType::MouseClick(mouse_click) => self.mouse_click(
self.mouse_click(mouse_click, click.window_type, click.mode.slot) 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 { ClickType::KeyClick(key_click) => match click.mode.slot {
container_click::Slot::Normal(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 => { container_click::Slot::OutsideInventory => {
unimplemented!("This is not a valid state") unimplemented!("This is not a valid state")
@ -153,7 +165,7 @@ impl Player {
}, },
ClickType::CreativePickItem => { ClickType::CreativePickItem => {
if let container_click::Slot::Normal(slot) = click.mode.slot { if let container_click::Slot::Normal(slot) = click.mode.slot {
self.creative_pick_item(slot) self.creative_pick_item(opened_container, slot)
} }
} }
ClickType::DoubleClick => {} ClickType::DoubleClick => {}
@ -178,11 +190,12 @@ impl Player {
fn mouse_click( fn mouse_click(
&mut self, &mut self,
opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick, mouse_click: MouseClick,
window_type: WindowType, window_type: WindowType,
slot: container_click::Slot, 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 { if container.window_type() != &window_type {
return; return;
} }
@ -195,8 +208,13 @@ impl Player {
}; };
} }
fn shift_mouse_click(&mut self, window_type: WindowType, slot: container_click::Slot) { fn shift_mouse_click(
let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); &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 { if container.window_type() != &window_type {
return; 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 { let changing_slot = match key_click {
KeyClick::Slot(slot) => slot, KeyClick::Slot(slot) => slot,
KeyClick::Offhand => 45, 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 = 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 { if container.window_type() != &window_type {
return; return;
} }
@ -253,34 +277,16 @@ impl Player {
} }
} }
fn creative_pick_item(&mut self, slot: usize) { fn creative_pick_item(
let mut container = get_full_container(&mut self.inventory, self.open_container.as_mut()); &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) { if let Some(Some(item)) = container.all_slots().get_mut(slot) {
self.carried_item = Some(item.to_owned()) self.carried_item = Some(item.to_owned())
} }
} }
fn double_click(&mut self, slot: usize) {} 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)
} }

View File

@ -15,10 +15,10 @@ use pumpkin_protocol::{
}, },
position::WorldPosition, position::WorldPosition,
server::play::{ server::play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SChatCommand, SChatMessage, SClickContainer, SClientInformationPlay, SConfirmTeleport,
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition,
SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SPlayerPositionRotation, SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround,
SUseItemOn, SSwingArm, SUseItem, SUseItemOn,
}, },
ConnectionState, RawPacket, ServerPacket, VarInt, ConnectionState, RawPacket, ServerPacket, VarInt,
}; };
@ -26,11 +26,7 @@ use pumpkin_world::item::ItemStack;
use pumpkin_world::vector3::Vector3; use pumpkin_world::vector3::Vector3;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{client::authentication::GameProfile, util::boundingbox::BoundingBox};
client::{authentication::GameProfile, Client},
server::Server,
util::boundingbox::BoundingBox,
};
pub struct PlayerAbilities { pub struct PlayerAbilities {
pub invulnerable: bool, pub invulnerable: bool,
@ -65,7 +61,7 @@ pub struct Player {
pub food: i32, pub food: i32,
pub food_saturation: f32, pub food_saturation: f32,
pub inventory: PlayerInventory, pub inventory: PlayerInventory,
pub open_container: Option<Box<dyn Container>>, pub open_container: Option<u64>,
pub carried_item: Option<ItemStack>, pub carried_item: Option<ItemStack>,
/// send `send_abilties_update` when changed /// send `send_abilties_update` when changed
@ -406,9 +402,13 @@ impl Player {
Ok(()) Ok(())
} }
SClickContainer::PACKET_ID => { 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),
} }
} }
} }

View File

@ -29,16 +29,16 @@ use pumpkin_protocol::{
}; };
use pumpkin_world::{dimension::Dimension, radial_chunk_iterator::RadialIterator, World}; 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::{ use crate::{
client::Client, client::Client,
config::{AdvancedConfiguration, BasicConfiguration}, config::{AdvancedConfiguration, BasicConfiguration},
entity::player::{GameMode, Player}, 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"; pub const CURRENT_MC_VERSION: &str = "1.21.1";
@ -63,6 +63,7 @@ pub struct Server {
// TODO: place this into every world // TODO: place this into every world
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>, pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
pub open_containers: HashMap<u64, OpenContainer>,
entity_id: AtomicI32, entity_id: AtomicI32,
pub base_config: BasicConfiguration, pub base_config: BasicConfiguration,
@ -120,6 +121,7 @@ impl Server {
status_response_json, status_response_json,
public_key_der, public_key_der,
current_players: HashMap::new(), current_players: HashMap::new(),
open_containers: HashMap::new(),
base_config: config.0, base_config: config.0,
auth_client, auth_client,
advanced_config: config.1, advanced_config: config.1,
@ -315,6 +317,14 @@ impl Server {
None 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 /// Sends a Packet to all Players
pub fn broadcast_packet<P>(&self, from: &mut Player, packet: &P) pub fn broadcast_packet<P>(&self, from: &mut Player, packet: &P)
where where