add working container state and change to fix with latest current branch

This commit is contained in:
Edvin Bryntesson 2024-09-02 15:05:56 +02:00
parent c98c0ec6bf
commit d1c4e59955
No known key found for this signature in database
11 changed files with 244 additions and 367 deletions

5
Cargo.lock generated
View File

@ -1901,6 +1901,7 @@ dependencies = [
"digest 0.11.0-pre.9", "digest 0.11.0-pre.9",
"hmac", "hmac",
"image", "image",
"itertools 0.13.0",
"log", "log",
"mio", "mio",
"num-bigint", "num-bigint",
@ -2806,9 +2807,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.39.2" version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",

View File

@ -1,4 +1,3 @@
use crate::WindowType;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
pub struct ClickMode { pub struct ClickMode {
@ -8,8 +7,6 @@ pub struct ClickMode {
pub struct Click { pub struct Click {
pub mode: ClickMode, pub mode: ClickMode,
pub state_id: u32,
pub window_type: WindowType,
pub changed_items: Vec<ItemChange>, pub changed_items: Vec<ItemChange>,
pub carried_item: Option<ItemStack>, pub carried_item: Option<ItemStack>,
} }
@ -25,7 +22,7 @@ impl ClickMode {
click_type: ClickType::CreativePickItem, click_type: ClickType::CreativePickItem,
slot: Slot::Normal(slot.try_into().unwrap()), slot: Slot::Normal(slot.try_into().unwrap()),
}, },
4 => Self::new_drop_item(button, slot), 4 => Self::new_drop_item(button),
5 => Self::new_drag_item(button, slot), 5 => Self::new_drag_item(button, slot),
6 => Self { 6 => Self {
click_type: ClickType::DoubleClick, click_type: ClickType::DoubleClick,
@ -78,7 +75,7 @@ impl ClickMode {
} }
} }
fn new_drop_item(button: i8, slot: i16) -> Self { fn new_drop_item(button: i8) -> Self {
let drop_type = match button { let drop_type = match button {
0 => DropType::SingleItem, 0 => DropType::SingleItem,
1 => DropType::FullStack, 1 => DropType::FullStack,
@ -87,7 +84,7 @@ impl ClickMode {
}; };
Self { Self {
click_type: ClickType::DropType(drop_type), click_type: ClickType::DropType(drop_type),
slot: Slot::Normal(slot.try_into().unwrap()), slot: Slot::OutsideInventory,
} }
} }
@ -108,7 +105,6 @@ impl ClickMode {
MouseDragState::End, MouseDragState::End,
Slot::OutsideInventory, Slot::OutsideInventory,
), ),
4 => ( 4 => (
MouseDragType::Right, MouseDragType::Right,
MouseDragState::Start, MouseDragState::Start,

View File

@ -76,7 +76,28 @@ pub trait Container: Sync + Send {
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>; fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>;
fn state_id(&mut self) -> i32; fn all_combinable_slots(&self) -> Vec<Option<&ItemStack>> {
self.all_slots_ref()
}
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.all_slots()
}
fn advance_state_id(&mut self) -> i32;
fn reset_state_id(&mut self);
fn state_id(&self) -> i32;
fn print_all_contents(&self) {
self.all_slots_ref()
.into_iter()
.enumerate()
.filter_map(|(slot, item)| item.map(|item| (slot, item)))
.for_each(|(slot, item)| {
dbg!(slot, item);
});
}
} }
pub fn handle_item_take( pub fn handle_item_take(
@ -106,12 +127,6 @@ pub fn handle_item_change(
current_slot: &mut Option<ItemStack>, current_slot: &mut Option<ItemStack>,
mouse_click: MouseClick, mouse_click: MouseClick,
) { ) {
if let Some(carried) = carried_slot.as_ref() {
dbg!(carried);
}
if let Some(current) = current_slot.as_ref() {
dbg!(current);
}
match (current_slot.as_mut(), carried_slot.as_mut()) { match (current_slot.as_mut(), carried_slot.as_mut()) {
// Swap or combine current and carried // Swap or combine current and carried
(Some(current), Some(carried)) => { (Some(current), Some(carried)) => {
@ -170,25 +185,23 @@ pub fn combine_stacks(
} }
} }
pub struct OptionallyCombinedContainer<'a> { pub struct OptionallyCombinedContainer<'a, 'b> {
container: Option<&'a mut Box<dyn Container>>, container: Option<&'a mut Box<dyn Container>>,
inventory: &'a mut PlayerInventory, inventory: &'b mut PlayerInventory,
state_id: i32,
} }
impl<'a> OptionallyCombinedContainer<'a> { impl<'a, 'b> OptionallyCombinedContainer<'a, 'b> {
pub fn new( pub fn new(
player_inventory: &'a mut PlayerInventory, player_inventory: &'b mut PlayerInventory,
container: Option<&'a mut Box<dyn Container>>, container: Option<&'a mut Box<dyn Container>>,
) -> Self { ) -> Self {
Self { Self {
inventory: player_inventory, inventory: player_inventory,
container, container,
state_id: 0,
} }
} }
} }
impl<'a> Container for OptionallyCombinedContainer<'a> { impl<'a> Container for OptionallyCombinedContainer<'a, 'a> {
fn window_type(&self) -> &'static WindowType { fn window_type(&self) -> &'static WindowType {
if let Some(container) = &self.container { if let Some(container) = &self.container {
container.window_type() container.window_type()
@ -198,27 +211,38 @@ impl<'a> Container for OptionallyCombinedContainer<'a> {
} }
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> { fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
let mut slots = if let Some(container) = &mut self.container { let slots = match &mut self.container {
container.all_slots() Some(container) => {
} else { let mut slots = container.all_slots();
vec![] slots.extend(self.inventory.all_combinable_slots_mut());
slots
}
None => self.inventory.all_slots(),
}; };
slots.extend(self.inventory.all_slots()); dbg!(slots.len());
slots slots
} }
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> { fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
let mut slots = if let Some(container) = &self.container { match &self.container {
container.all_slots_ref() Some(container) => {
} else { let mut slots = container.all_slots_ref();
vec![] slots.extend(self.inventory.all_combinable_slots());
}; slots
slots.extend(self.inventory.all_slots_ref()); }
slots None => self.inventory.all_slots_ref(),
}
} }
fn state_id(&mut self) -> i32 { fn advance_state_id(&mut self) -> i32 {
self.state_id += 1; self.inventory.advance_state_id()
self.state_id - 1 }
fn reset_state_id(&mut self) {
self.inventory.reset_state_id()
}
fn state_id(&self) -> i32 {
self.inventory.state_id()
} }
} }

View File

@ -10,6 +10,7 @@ pub struct OpenContainer {
impl 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<MutexGuard<Box<dyn Container>>> {
if !self.players.contains(&player_id) { if !self.players.contains(&player_id) {
dbg!("couldn't open container");
return None; return None;
} }
let container = self.container.lock().unwrap(); let container = self.container.lock().unwrap();
@ -31,6 +32,18 @@ impl OpenContainer {
} }
} }
pub fn remove_player(&mut self, player_id: i32) {
if let Some(index) = self.players.iter().enumerate().find_map(|(index, id)| {
if *id == player_id {
Some(index)
} else {
None
}
}) {
self.players.remove(index);
}
}
pub fn empty(player_id: i32) -> Self { pub fn empty(player_id: i32) -> Self {
Self { Self {
players: vec![player_id], players: vec![player_id],
@ -58,6 +71,7 @@ impl Container for Chest {
} }
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> { fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
dbg!(self.slots.iter().len());
self.slots.iter_mut().collect() self.slots.iter_mut().collect()
} }
@ -65,8 +79,16 @@ impl Container for Chest {
self.slots.iter().map(|slot| slot.as_ref()).collect() self.slots.iter().map(|slot| slot.as_ref()).collect()
} }
fn state_id(&mut self) -> i32 { fn advance_state_id(&mut self) -> i32 {
self.state_id += 1; self.state_id += 1;
self.state_id - 1 self.state_id - 1
} }
fn reset_state_id(&mut self) {
self.state_id = 0;
}
fn state_id(&self) -> i32 {
self.state_id
}
} }

View File

@ -14,6 +14,7 @@ pub struct PlayerInventory {
// current selected slot in hotbar // current selected slot in hotbar
selected: usize, selected: usize,
state_id: i32, state_id: i32,
pub total_opened_containers: u8,
} }
impl Default for PlayerInventory { impl Default for PlayerInventory {
@ -33,6 +34,7 @@ impl PlayerInventory {
// TODO: What when player spawns in with an different index ? // TODO: What when player spawns in with an different index ?
selected: 0, selected: 0,
state_id: 0, state_id: 0,
total_opened_containers: 2,
} }
} }
@ -155,8 +157,23 @@ impl Container for PlayerInventory {
self.slots() self.slots()
} }
fn state_id(&mut self) -> i32 { fn all_combinable_slots(&self) -> Vec<Option<&ItemStack>> {
self.items.iter().map(|item| item.as_ref()).collect()
}
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.items.iter_mut().collect()
}
fn advance_state_id(&mut self) -> i32 {
self.state_id += 1; self.state_id += 1;
self.state_id - 1 self.state_id
}
fn reset_state_id(&mut self) {
self.state_id = 0;
}
fn state_id(&self) -> i32 {
self.state_id
} }
} }

View File

@ -64,3 +64,4 @@ crossbeam-channel = "0.5.13"
uuid.workspace = true uuid.workspace = true
tokio.workspace = true tokio.workspace = true
rayon.workspace = true rayon.workspace = true
itertools = "0.13.0"

View File

@ -1,8 +1,8 @@
use num_traits::FromPrimitive; use itertools::Itertools;
use pumpkin_core::text::TextComponent; use pumpkin_core::text::TextComponent;
use pumpkin_inventory::container_click::{KeyClick, MouseClick}; use pumpkin_inventory::container_click::{KeyClick, MouseClick};
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait}; use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
use pumpkin_inventory::{container_click, handle_item_change, OptionallyCombinedContainer}; use pumpkin_inventory::{container_click, OptionallyCombinedContainer};
use pumpkin_inventory::{Container, WindowType}; use pumpkin_inventory::{Container, WindowType};
use pumpkin_protocol::client::play::{ use pumpkin_protocol::client::play::{
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
@ -10,6 +10,7 @@ use pumpkin_protocol::client::play::{
use pumpkin_protocol::server::play::SClickContainer; use pumpkin_protocol::server::play::SClickContainer;
use pumpkin_protocol::slot::Slot; use pumpkin_protocol::slot::Slot;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use std::sync::MutexGuard;
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::server::Server; use crate::server::Server;
@ -17,12 +18,12 @@ use crate::server::Server;
impl Player { impl Player {
pub fn open_container( pub fn open_container(
&mut self, &mut self,
container: &Box<dyn Container>, server: &mut Server,
minecraft_menu_id: &str, minecraft_menu_id: &str,
window_title: Option<&str>, window_title: Option<&str>,
carried_item: Option<&ItemStack>,
state_id: i32,
) { ) {
let mut container = self.get_open_container(server);
let container = container.as_deref_mut();
let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY
.get("minecraft:menu") .get("minecraft:menu")
.unwrap() .unwrap()
@ -32,56 +33,48 @@ impl Player {
.get("protocol_id") .get("protocol_id")
.unwrap()) .unwrap())
.into(); .into();
let window_type = container.window_type(); 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 title = TextComponent::text(window_title.unwrap_or(window_type.default_title()));
self.inventory.reset_state_id();
self.client.send_packet(&COpenScreen::new( self.client.send_packet(&COpenScreen::new(
(*window_type as u8 + 1).into(), self.inventory.total_opened_containers.into(),
menu_protocol_id, menu_protocol_id,
title, title,
)); ));
self.set_container_content(container);
self.set_container_content(Some(container), carried_item, state_id);
} }
pub fn set_container_content<'a>( pub fn set_container_content(&mut self, container: Option<&mut Box<dyn Container>>) {
&mut self, let total_opened_containers = self.inventory.total_opened_containers;
container: Option<&Box<dyn Container>>, let container = OptionallyCombinedContainer::new(&mut self.inventory, container);
carried_item: Option<&'a ItemStack>,
state_id: i32, let slots = container
) { .all_slots_ref()
let slots: Vec<Slot> = {
let items = container.map(|container| container.all_slots_ref());
if let Some(mut items) = items {
items.extend(self.inventory.all_slots_ref());
items
} else {
self.inventory.all_slots_ref()
}
.into_iter() .into_iter()
.map(|item| { .map(Slot::from)
if let Some(item) = item { .collect_vec();
Slot::from(item)
} else {
Slot::empty()
}
})
.collect()
};
let carried_item = { let carried_item = {
if let Some(item) = carried_item { if let Some(item) = self.carried_item.as_ref() {
item.into() item.into()
} else { } else {
Slot::empty() Slot::empty()
} }
}; };
let window_type = match container { let packet = CSetContainerContent::new(
Some(container) => *container.window_type() as u8, total_opened_containers,
None => 0, self.inventory.state_id().into(),
}; &slots,
&carried_item,
let packet = CSetContainerContent::new(window_type, state_id.into(), &slots, &carried_item); );
self.inventory.advance_state_id();
self.client.send_packet(&packet); self.client.send_packet(&packet);
} }
@ -101,32 +94,54 @@ impl Player {
} }
/// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent /// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent
pub fn close_container(&mut self, window_type: WindowType) { pub fn close_container(&mut self) {
self.client self.inventory.total_opened_containers += 1;
.send_packet(&CCloseContainer::new(window_type as u8)) self.client.send_packet(&CCloseContainer::new(
self.inventory.total_opened_containers,
))
} }
pub fn set_container_property<T: WindowPropertyTrait>( pub fn set_container_property<T: WindowPropertyTrait>(
&mut self, &mut self,
window_type: WindowType,
window_property: WindowProperty<T>, window_property: WindowProperty<T>,
) { ) {
let (id, value) = window_property.into_tuple(); let (id, value) = window_property.into_tuple();
self.client self.client.send_packet(&CSetContainerProperty::new(
.send_packet(&CSetContainerProperty::new(window_type as u8, id, value)); self.inventory.total_opened_containers,
id,
value,
));
} }
pub fn handle_click_container(&mut self, server: &mut Server, 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 { let mut opened_container = self.get_open_container(server);
server.try_get_container(self.entity_id(), id)
} else {
None
};
let opened_container = opened_container.as_deref_mut(); let opened_container = opened_container.as_deref_mut();
let current_state_id = if let Some(container) = opened_container.as_ref() {
container.state_id()
} else {
self.inventory.state_id()
};
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;
}
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;
}
} else if packet.window_id != 0 {
dbg!("weird");
return;
}
let click = Click { let click = Click {
state_id: packet.state_id.0.try_into().unwrap(),
changed_items: packet changed_items: packet
.array_of_changed_slots .array_of_changed_slots
.into_iter() .into_iter()
@ -139,12 +154,6 @@ impl Player {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
window_type: WindowType::from_u8(if packet.window_id == 0 {
0
} else {
packet.window_id - 1
})
.unwrap(),
carried_item: packet.carried_item.to_item(), carried_item: packet.carried_item.to_item(),
mode: ClickMode::new( mode: ClickMode::new(
packet packet
@ -157,18 +166,13 @@ impl Player {
), ),
}; };
match click.mode.click_type { match click.mode.click_type {
ClickType::MouseClick(mouse_click) => self.mouse_click( ClickType::MouseClick(mouse_click) => {
opened_container, self.mouse_click(opened_container, mouse_click, click.mode.slot)
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(opened_container, 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(opened_container, click.window_type, key_click, slot) self.number_button_pressed(opened_container, 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")
@ -189,20 +193,16 @@ impl Player {
drag_type: _, drag_type: _,
} => (), } => (),
ClickType::DropType(_drop_type) => (), ClickType::DropType(_drop_type) => (),
} };
} }
fn mouse_click( fn mouse_click(
&mut self, &mut self,
opened_container: Option<&mut Box<dyn Container>>, opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick, mouse_click: MouseClick,
window_type: WindowType,
slot: container_click::Slot, slot: container_click::Slot,
) { ) {
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
match slot { match slot {
container_click::Slot::Normal(slot) => { container_click::Slot::Normal(slot) => {
@ -215,41 +215,42 @@ impl Player {
fn shift_mouse_click( fn shift_mouse_click(
&mut self, &mut self,
opened_container: Option<&mut Box<dyn Container>>, opened_container: Option<&mut Box<dyn Container>>,
window_type: WindowType,
slot: container_click::Slot, slot: container_click::Slot,
) { ) {
let container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
match slot { match slot {
container_click::Slot::Normal(slot) => { container_click::Slot::Normal(slot) => {
if let Some(item_in_pressed_slot) = self.inventory.slots()[slot] { let all_slots = container.all_slots();
let slots = self.inventory.slots().into_iter().enumerate(); if let Some(item_in_pressed_slot) = all_slots[slot].to_owned() {
let slots = all_slots.into_iter().enumerate();
// Hotbar // Hotbar
let find_condition = |(_, slot): &(usize, Option<&ItemStack>)| { let find_condition = |(slot_number, slot): (usize, &mut Option<ItemStack>)| {
slot.is_none_or(|item| item.item_id == item_in_pressed_slot.item_id) // TODO: Check for max item count here
match slot {
Some(item) => {
if item.item_id == item_in_pressed_slot.item_id
&& item.item_count != 64
{
Some(slot_number)
} else {
None
}
}
None => Some(slot_number),
}
}; };
let slots = if slot > 35 { let slots = if slot > 35 {
slots slots.skip(9).find_map(find_condition)
.skip(9)
.find(find_condition)
.map(|(slot_num, _)| slot_num)
} else { } else {
slots slots.skip(36).rev().find_map(find_condition)
.skip(36)
.rev()
.find(find_condition)
.map(|(slot_num, _)| slot_num)
}; };
if let Some(slot) = slots { if let Some(slot) = slots {
let mut item_slot = self.inventory.slots()[slot].map(|i| i.to_owned()); let mut item_slot = container.all_slots()[slot].map(|i| i.to_owned());
self.inventory container.handle_item_change(&mut item_slot, slot, MouseClick::Left);
.handle_item_change(&mut item_slot, slot, MouseClick::Left); *container.all_slots()[slot] = item_slot;
*self.inventory.slots_mut()[slot] = item_slot;
} }
} }
} }
@ -260,7 +261,6 @@ impl Player {
fn number_button_pressed( fn number_button_pressed(
&mut self, &mut self,
opened_container: Option<&mut Box<dyn Container>>, opened_container: Option<&mut Box<dyn Container>>,
window_type: WindowType,
key_click: KeyClick, key_click: KeyClick,
slot: usize, slot: usize,
) { ) {
@ -270,15 +270,9 @@ impl Player {
}; };
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); let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
let item_slot = &mut container.all_slots()[slot]; container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left);
if item_slot.is_some() { *self.inventory.get_slot(changing_slot as usize) = changing_item_slot
handle_item_change(&mut changing_item_slot, item_slot, MouseClick::Left);
*self.inventory.get_slot(changing_slot as usize) = changing_item_slot
}
} }
fn creative_pick_item( fn creative_pick_item(
@ -293,4 +287,15 @@ impl Player {
} }
fn double_click(&mut self, _slot: usize) {} fn double_click(&mut self, _slot: usize) {}
pub fn get_open_container<'a>(
&self,
server: &'a Server,
) -> Option<MutexGuard<'a, Box<dyn Container>>> {
if let Some(id) = self.open_container {
server.try_get_container(self.entity_id(), id)
} else {
None
}
}
} }

View File

@ -537,8 +537,14 @@ impl Player {
// TODO: // TODO:
// This function will in the future be used to keep track of if the client is in a valid state. // This function will in the future be used to keep track of if the client is in a valid state.
// But this is not possible yet // But this is not possible yet
pub fn handle_close_container(&mut self, _server: &mut Server, packet: SCloseContainer) { pub fn handle_close_container(&mut self, server: &mut Server, packet: SCloseContainer) {
// window_id 0 represents both 9x1 Generic AND inventory here // window_id 0 represents both 9x1 Generic AND inventory here
if let Some(id) = self.open_container {
if let Some(container) = server.open_containers.get_mut(&id) {
container.remove_player(self.entity_id())
}
self.open_container = None;
}
let Some(_window_type) = WindowType::from_u8(packet.window_id) else { let Some(_window_type) = WindowType::from_u8(packet.window_id) else {
self.kick(TextComponent::text("Invalid window ID")); self.kick(TextComponent::text("Invalid window ID"));
return; return;

View File

@ -10,34 +10,18 @@ const DESCRIPTION: &str =
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| { CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| {
if let Some(player) = sender.as_mut_player() { if let Some(player) = sender.as_mut_player() {
let entity_id = player.entity_id().clone(); let entity_id = player.entity_id();
player.open_container = Some(0); player.open_container = Some(0);
let mut container = match server.open_containers.get_mut(&0) { match server.open_containers.get_mut(&0) {
Some(ender_chest) => { Some(ender_chest) => {
if ender_chest.try_open(entity_id).is_none() { ender_chest.add_player(entity_id);
ender_chest.add_player(entity_id);
}
ender_chest.try_open(entity_id).unwrap()
} }
None => { None => {
let open_container = OpenContainer::empty(entity_id); let open_container = OpenContainer::empty(entity_id);
server.open_containers.insert(0, open_container); server.open_containers.insert(0, open_container);
server
.open_containers
.get_mut(&0)
.unwrap()
.try_open(entity_id)
.unwrap()
} }
}; }
let state_id = container.state_id(); player.open_container(server, "minecraft:generic_9x3", Some("Ender Chest"));
player.open_container(
&container,
"minecraft:generic_9x3",
Some("Ender Chest"),
None,
state_id,
);
} }
Ok(()) Ok(())

View File

@ -1,8 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use num_derive::FromPrimitive;
use crate::{client::Client, server::Server};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use pumpkin_core::{ use pumpkin_core::{
math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3}, math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3},
@ -26,16 +24,13 @@ use pumpkin_protocol::{
ConnectionState, RawPacket, ServerPacket, VarInt, ConnectionState, RawPacket, ServerPacket, VarInt,
}; };
use pumpkin_protocol::server::play::SCloseContainer;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use pumpkin_world::vector3::Vector3;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
client::{authentication::GameProfile, Client, PlayerConfig}, client::{authentication::GameProfile, Client, PlayerConfig},
server::Server, server::Server,
world::World, world::World,
util::boundingbox::BoundingBox
}; };
pub struct PlayerAbilities { pub struct PlayerAbilities {
@ -460,6 +455,10 @@ impl Player {
self.handle_click_container(server, SClickContainer::read(bytebuf)?); self.handle_click_container(server, SClickContainer::read(bytebuf)?);
Ok(()) Ok(())
} }
SCloseContainer::PACKET_ID => {
self.handle_close_container(server, SCloseContainer::read(bytebuf)?);
Ok(())
}
_ => { _ => {
log::error!("Failed to handle player packet id {:#04x}", packet.id.0); log::error!("Failed to handle player packet id {:#04x}", packet.id.0);
Ok(()) Ok(())

View File

@ -1,13 +1,3 @@
use std::{
io::Cursor,
path::Path,
sync::{
atomic::{AtomicI32, Ordering},
Arc, Mutex,
},
time::Duration,
};
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use image::GenericImageView; use image::GenericImageView;
use mio::Token; use mio::Token;
@ -20,19 +10,21 @@ use pumpkin_protocol::{
CURRENT_MC_PROTOCOL, CURRENT_MC_PROTOCOL,
}; };
use pumpkin_world::dimension::Dimension; use pumpkin_world::dimension::Dimension;
use std::collections::HashMap;
use crate::{ use std::{
client::Client, io::Cursor,
config::{AdvancedConfiguration, BasicConfiguration}, path::Path,
entity::player::{GameMode, Player}, sync::{
world::World, atomic::{AtomicI32, Ordering},
Arc, Mutex, MutexGuard,
},
time::Duration,
}; };
use crate::{client::Client, entity::player::Player, world::World};
use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_inventory::{Container, OpenContainer};
use pumpkin_registry::Registry; use pumpkin_registry::Registry;
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; 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";
@ -55,7 +47,6 @@ pub struct Server {
/// Cache the registry so we don't have to parse it every time a player joins /// Cache the registry so we don't have to parse it every time a player joins
pub cached_registry: Vec<Registry>, pub cached_registry: Vec<Registry>,
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
pub open_containers: HashMap<u64, OpenContainer>, pub open_containers: HashMap<u64, OpenContainer>,
entity_id: AtomicI32, entity_id: AtomicI32,
@ -102,6 +93,7 @@ impl Server {
Self { Self {
plugin_loader, plugin_loader,
cached_registry: Registry::get_static(), cached_registry: Registry::get_static(),
open_containers: HashMap::new(),
// 0 is invalid // 0 is invalid
entity_id: 2.into(), entity_id: 2.into(),
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))], worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
@ -111,8 +103,6 @@ impl Server {
status_response, status_response,
status_response_json, status_response_json,
public_key_der, public_key_der,
open_containers: HashMap::new(),
base_config: config.0,
auth_client, auth_client,
} }
} }
@ -134,188 +124,20 @@ impl Server {
client, client,
world.clone(), world.clone(),
entity_id, entity_id,
self.base_config.hardcore, gamemode,
&["minecraft:overworld"], )));
self.base_config.max_players.into(), world.lock().await.add_player(token, player.clone());
self.base_config.view_distance.into(), // TODO: view distance (player, world)
self.base_config.simulation_distance.into(), // TODO: sim view dinstance
false,
false,
false,
0.into(),
"minecraft:overworld",
0, // seed
gamemode.to_u8().unwrap(),
self.base_config.default_gamemode.to_i8().unwrap(),
false,
false,
None,
0.into(),
false,
));
dbg!("sending abilities");
// player abilities
player
.client
.send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1));
// teleport
let x = 10.0;
let y = 120.0;
let z = 10.0;
let yaw = 10.0;
let pitch = 10.0;
player.teleport(x, y, z, 10.0, 10.0);
let gameprofile = &player.gameprofile;
// first send info update to our new player, So he can see his Skin
// also send his info to everyone else
self.broadcast_packet(
player,
&CPlayerInfoUpdate::new(
0x01 | 0x08,
&[pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![
PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
},
PlayerAction::UpdateListed(true),
],
}],
),
);
// here we send all the infos of already joined players
let mut entries = Vec::new();
for (_, playerr) in self
.current_players
.iter()
.filter(|c| c.0 != &player.client.token)
{
let playerr = playerr.as_ref().lock().unwrap();
let gameprofile = &playerr.gameprofile;
entries.push(pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![
PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
},
PlayerAction::UpdateListed(true),
],
})
}
player
.client
.send_packet(&CPlayerInfoUpdate::new(0x01 | 0x08, &entries));
// Start waiting for level chunks
player.client.send_packet(&CGameEvent::new(13, 0.0));
let gameprofile = &player.gameprofile;
// spawn player for every client
self.broadcast_packet_except(
&[&player.client.token],
// TODO: add velo
&CSpawnEntity::new(
entity_id.into(),
UUID(gameprofile.id),
(EntityType::Player as i32).into(),
x,
y,
z,
pitch,
yaw,
yaw,
0.into(),
0.0,
0.0,
0.0,
),
);
// spawn players for our client
let token = player.client.token.clone();
for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) {
let existing_player = existing_player.as_ref().lock().unwrap();
let entity = &existing_player.entity;
let gameprofile = &existing_player.gameprofile;
player.client.send_packet(&CSpawnEntity::new(
existing_player.entity_id().into(),
UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(),
entity.x,
entity.y,
entity.z,
entity.yaw,
entity.pitch,
entity.pitch,
0.into(),
0.0,
0.0,
0.0,
))
}
// entity meta data
if let Some(config) = player.client.config.as_ref() {
self.broadcast_packet(
player,
&CSetEntityMetadata::new(
entity_id.into(),
Metadata::new(17, VarInt(0), config.skin_parts),
),
)
}
self.spawn_test_chunk(player, self.base_config.view_distance as u32)
.await;
}
/// TODO: This definitly should be in world
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<MutexGuard<Player>> {
for (_, player) in self
.current_players
.iter()
.filter(|c| c.0 != &from.client.token)
{
let player = player.lock().unwrap();
if player.entity_id() == id {
return Some(player);
}
}
None
} }
pub fn try_get_container( pub fn try_get_container(
&self, &self,
player_id: EntityId, player_id: EntityId,
container_id: u64, container_id: u64,
) -> Option<MutexGuard<Box<dyn Container>>> { ) -> Option<MutexGuard<'_, Box<dyn Container>>> {
self.open_containers.get(&container_id)?.try_open(player_id) 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
P: ClientPacket,
{
// we can't borrow twice at same time
from.client.send_packet(packet);
for (_, player) in self
.current_players
.iter()
.filter(|c| c.0 != &from.client.token)
{
let mut player = player.lock().unwrap();
player.client.send_packet(packet);
}
gamemode,
)));
world.lock().await.add_player(token, player.clone());
(player, world)
}
/// Sends a Packet to all Players in all worlds /// Sends a Packet to all Players in all worlds
pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], packet: &P) pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], packet: &P)
where where