add working container state and change to fix with latest current branch
This commit is contained in:
parent
c98c0ec6bf
commit
d1c4e59955
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -1901,6 +1901,7 @@ dependencies = [
|
||||
"digest 0.11.0-pre.9",
|
||||
"hmac",
|
||||
"image",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"mio",
|
||||
"num-bigint",
|
||||
@ -2806,9 +2807,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.39.2"
|
||||
version = "1.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use crate::WindowType;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
|
||||
pub struct ClickMode {
|
||||
@ -8,8 +7,6 @@ pub struct ClickMode {
|
||||
|
||||
pub struct Click {
|
||||
pub mode: ClickMode,
|
||||
pub state_id: u32,
|
||||
pub window_type: WindowType,
|
||||
pub changed_items: Vec<ItemChange>,
|
||||
pub carried_item: Option<ItemStack>,
|
||||
}
|
||||
@ -25,7 +22,7 @@ impl ClickMode {
|
||||
click_type: ClickType::CreativePickItem,
|
||||
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),
|
||||
6 => Self {
|
||||
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 {
|
||||
0 => DropType::SingleItem,
|
||||
1 => DropType::FullStack,
|
||||
@ -87,7 +84,7 @@ impl ClickMode {
|
||||
};
|
||||
Self {
|
||||
click_type: ClickType::DropType(drop_type),
|
||||
slot: Slot::Normal(slot.try_into().unwrap()),
|
||||
slot: Slot::OutsideInventory,
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +105,6 @@ impl ClickMode {
|
||||
MouseDragState::End,
|
||||
Slot::OutsideInventory,
|
||||
),
|
||||
|
||||
4 => (
|
||||
MouseDragType::Right,
|
||||
MouseDragState::Start,
|
||||
|
||||
@ -76,7 +76,28 @@ pub trait Container: Sync + Send {
|
||||
|
||||
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(
|
||||
@ -106,12 +127,6 @@ pub fn handle_item_change(
|
||||
current_slot: &mut Option<ItemStack>,
|
||||
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()) {
|
||||
// Swap or combine current and 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>>,
|
||||
inventory: &'a mut PlayerInventory,
|
||||
state_id: i32,
|
||||
inventory: &'b mut PlayerInventory,
|
||||
}
|
||||
impl<'a> OptionallyCombinedContainer<'a> {
|
||||
impl<'a, 'b> OptionallyCombinedContainer<'a, 'b> {
|
||||
pub fn new(
|
||||
player_inventory: &'a mut PlayerInventory,
|
||||
player_inventory: &'b mut PlayerInventory,
|
||||
container: Option<&'a mut Box<dyn Container>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inventory: player_inventory,
|
||||
container,
|
||||
state_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Container for OptionallyCombinedContainer<'a> {
|
||||
impl<'a> Container for OptionallyCombinedContainer<'a, 'a> {
|
||||
fn window_type(&self) -> &'static WindowType {
|
||||
if let Some(container) = &self.container {
|
||||
container.window_type()
|
||||
@ -198,27 +211,38 @@ impl<'a> Container for OptionallyCombinedContainer<'a> {
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
||||
let mut slots = if let Some(container) = &mut self.container {
|
||||
container.all_slots()
|
||||
} else {
|
||||
vec![]
|
||||
let slots = match &mut self.container {
|
||||
Some(container) => {
|
||||
let mut slots = container.all_slots();
|
||||
slots.extend(self.inventory.all_combinable_slots_mut());
|
||||
slots
|
||||
}
|
||||
None => self.inventory.all_slots(),
|
||||
};
|
||||
slots.extend(self.inventory.all_slots());
|
||||
dbg!(slots.len());
|
||||
slots
|
||||
}
|
||||
|
||||
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());
|
||||
match &self.container {
|
||||
Some(container) => {
|
||||
let mut slots = container.all_slots_ref();
|
||||
slots.extend(self.inventory.all_combinable_slots());
|
||||
slots
|
||||
}
|
||||
None => self.inventory.all_slots_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
fn state_id(&mut self) -> i32 {
|
||||
self.state_id += 1;
|
||||
self.state_id - 1
|
||||
fn advance_state_id(&mut self) -> i32 {
|
||||
self.inventory.advance_state_id()
|
||||
}
|
||||
|
||||
fn reset_state_id(&mut self) {
|
||||
self.inventory.reset_state_id()
|
||||
}
|
||||
|
||||
fn state_id(&self) -> i32 {
|
||||
self.inventory.state_id()
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ pub struct OpenContainer {
|
||||
impl OpenContainer {
|
||||
pub fn try_open(&self, player_id: i32) -> Option<MutexGuard<Box<dyn Container>>> {
|
||||
if !self.players.contains(&player_id) {
|
||||
dbg!("couldn't open container");
|
||||
return None;
|
||||
}
|
||||
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 {
|
||||
Self {
|
||||
players: vec![player_id],
|
||||
@ -58,6 +71,7 @@ impl Container for Chest {
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
||||
dbg!(self.slots.iter().len());
|
||||
self.slots.iter_mut().collect()
|
||||
}
|
||||
|
||||
@ -65,8 +79,16 @@ impl Container for Chest {
|
||||
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
|
||||
}
|
||||
|
||||
fn reset_state_id(&mut self) {
|
||||
self.state_id = 0;
|
||||
}
|
||||
|
||||
fn state_id(&self) -> i32 {
|
||||
self.state_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ pub struct PlayerInventory {
|
||||
// current selected slot in hotbar
|
||||
selected: usize,
|
||||
state_id: i32,
|
||||
pub total_opened_containers: u8,
|
||||
}
|
||||
|
||||
impl Default for PlayerInventory {
|
||||
@ -33,6 +34,7 @@ impl PlayerInventory {
|
||||
// TODO: What when player spawns in with an different index ?
|
||||
selected: 0,
|
||||
state_id: 0,
|
||||
total_opened_containers: 2,
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,8 +157,23 @@ impl Container for PlayerInventory {
|
||||
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
|
||||
}
|
||||
|
||||
fn reset_state_id(&mut self) {
|
||||
self.state_id = 0;
|
||||
}
|
||||
fn state_id(&self) -> i32 {
|
||||
self.state_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,3 +64,4 @@ crossbeam-channel = "0.5.13"
|
||||
uuid.workspace = true
|
||||
tokio.workspace = true
|
||||
rayon.workspace = true
|
||||
itertools = "0.13.0"
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use num_traits::FromPrimitive;
|
||||
use itertools::Itertools;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_inventory::container_click::{KeyClick, MouseClick};
|
||||
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_protocol::client::play::{
|
||||
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||
@ -10,6 +10,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;
|
||||
@ -17,12 +18,12 @@ use crate::server::Server;
|
||||
impl Player {
|
||||
pub fn open_container(
|
||||
&mut self,
|
||||
container: &Box<dyn Container>,
|
||||
server: &mut Server,
|
||||
minecraft_menu_id: &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
|
||||
.get("minecraft:menu")
|
||||
.unwrap()
|
||||
@ -32,56 +33,48 @@ impl Player {
|
||||
.get("protocol_id")
|
||||
.unwrap())
|
||||
.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()));
|
||||
|
||||
self.inventory.reset_state_id();
|
||||
|
||||
self.client.send_packet(&COpenScreen::new(
|
||||
(*window_type as u8 + 1).into(),
|
||||
self.inventory.total_opened_containers.into(),
|
||||
menu_protocol_id,
|
||||
title,
|
||||
));
|
||||
|
||||
self.set_container_content(Some(container), carried_item, state_id);
|
||||
self.set_container_content(container);
|
||||
}
|
||||
|
||||
pub fn set_container_content<'a>(
|
||||
&mut self,
|
||||
container: Option<&Box<dyn Container>>,
|
||||
carried_item: Option<&'a ItemStack>,
|
||||
state_id: i32,
|
||||
) {
|
||||
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()
|
||||
}
|
||||
pub fn set_container_content(&mut self, container: Option<&mut Box<dyn Container>>) {
|
||||
let total_opened_containers = self.inventory.total_opened_containers;
|
||||
let container = OptionallyCombinedContainer::new(&mut self.inventory, container);
|
||||
|
||||
let slots = container
|
||||
.all_slots_ref()
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
if let Some(item) = item {
|
||||
Slot::from(item)
|
||||
} else {
|
||||
Slot::empty()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
.map(Slot::from)
|
||||
.collect_vec();
|
||||
|
||||
let carried_item = {
|
||||
if let Some(item) = carried_item {
|
||||
if let Some(item) = self.carried_item.as_ref() {
|
||||
item.into()
|
||||
} else {
|
||||
Slot::empty()
|
||||
}
|
||||
};
|
||||
|
||||
let window_type = match container {
|
||||
Some(container) => *container.window_type() as u8,
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let packet = CSetContainerContent::new(window_type, state_id.into(), &slots, &carried_item);
|
||||
let packet = CSetContainerContent::new(
|
||||
total_opened_containers,
|
||||
self.inventory.state_id().into(),
|
||||
&slots,
|
||||
&carried_item,
|
||||
);
|
||||
self.inventory.advance_state_id();
|
||||
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
|
||||
pub fn close_container(&mut self, window_type: WindowType) {
|
||||
self.client
|
||||
.send_packet(&CCloseContainer::new(window_type as u8))
|
||||
pub fn close_container(&mut self) {
|
||||
self.inventory.total_opened_containers += 1;
|
||||
self.client.send_packet(&CCloseContainer::new(
|
||||
self.inventory.total_opened_containers,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_container_property<T: WindowPropertyTrait>(
|
||||
&mut self,
|
||||
window_type: WindowType,
|
||||
window_property: WindowProperty<T>,
|
||||
) {
|
||||
let (id, value) = window_property.into_tuple();
|
||||
self.client
|
||||
.send_packet(&CSetContainerProperty::new(window_type as u8, id, value));
|
||||
self.client.send_packet(&CSetContainerProperty::new(
|
||||
self.inventory.total_opened_containers,
|
||||
id,
|
||||
value,
|
||||
));
|
||||
}
|
||||
|
||||
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(), id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut opened_container = self.get_open_container(server);
|
||||
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 {
|
||||
state_id: packet.state_id.0.try_into().unwrap(),
|
||||
changed_items: packet
|
||||
.array_of_changed_slots
|
||||
.into_iter()
|
||||
@ -139,12 +154,6 @@ impl Player {
|
||||
}
|
||||
})
|
||||
.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(),
|
||||
mode: ClickMode::new(
|
||||
packet
|
||||
@ -157,18 +166,13 @@ impl Player {
|
||||
),
|
||||
};
|
||||
match click.mode.click_type {
|
||||
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::MouseClick(mouse_click) => {
|
||||
self.mouse_click(opened_container, mouse_click, click.mode.slot)
|
||||
}
|
||||
ClickType::ShiftClick => self.shift_mouse_click(opened_container, click.mode.slot),
|
||||
ClickType::KeyClick(key_click) => match click.mode.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 => {
|
||||
unimplemented!("This is not a valid state")
|
||||
@ -189,20 +193,16 @@ impl Player {
|
||||
drag_type: _,
|
||||
} => (),
|
||||
ClickType::DropType(_drop_type) => (),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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 = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
if container.window_type() != &window_type {
|
||||
return;
|
||||
}
|
||||
|
||||
match slot {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
@ -215,41 +215,42 @@ impl Player {
|
||||
fn shift_mouse_click(
|
||||
&mut self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
window_type: WindowType,
|
||||
slot: container_click::Slot,
|
||||
) {
|
||||
let container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
if container.window_type() != &window_type {
|
||||
return;
|
||||
}
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
|
||||
match slot {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
if let Some(item_in_pressed_slot) = self.inventory.slots()[slot] {
|
||||
let slots = self.inventory.slots().into_iter().enumerate();
|
||||
let all_slots = container.all_slots();
|
||||
if let Some(item_in_pressed_slot) = all_slots[slot].to_owned() {
|
||||
let slots = all_slots.into_iter().enumerate();
|
||||
// Hotbar
|
||||
let find_condition = |(_, slot): &(usize, Option<&ItemStack>)| {
|
||||
slot.is_none_or(|item| item.item_id == item_in_pressed_slot.item_id)
|
||||
let find_condition = |(slot_number, slot): (usize, &mut Option<ItemStack>)| {
|
||||
// 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 {
|
||||
slots
|
||||
.skip(9)
|
||||
.find(find_condition)
|
||||
.map(|(slot_num, _)| slot_num)
|
||||
slots.skip(9).find_map(find_condition)
|
||||
} else {
|
||||
slots
|
||||
.skip(36)
|
||||
.rev()
|
||||
.find(find_condition)
|
||||
.map(|(slot_num, _)| slot_num)
|
||||
slots.skip(36).rev().find_map(find_condition)
|
||||
};
|
||||
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
|
||||
.handle_item_change(&mut item_slot, slot, MouseClick::Left);
|
||||
*self.inventory.slots_mut()[slot] = item_slot;
|
||||
container.handle_item_change(&mut item_slot, slot, MouseClick::Left);
|
||||
*container.all_slots()[slot] = item_slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -260,7 +261,6 @@ impl Player {
|
||||
fn number_button_pressed(
|
||||
&mut self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
window_type: WindowType,
|
||||
key_click: KeyClick,
|
||||
slot: usize,
|
||||
) {
|
||||
@ -270,16 +270,10 @@ impl Player {
|
||||
};
|
||||
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);
|
||||
if container.window_type() != &window_type {
|
||||
return;
|
||||
}
|
||||
|
||||
let item_slot = &mut container.all_slots()[slot];
|
||||
if item_slot.is_some() {
|
||||
handle_item_change(&mut changing_item_slot, item_slot, MouseClick::Left);
|
||||
container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left);
|
||||
*self.inventory.get_slot(changing_slot as usize) = changing_item_slot
|
||||
}
|
||||
}
|
||||
|
||||
fn creative_pick_item(
|
||||
&mut self,
|
||||
@ -293,4 +287,15 @@ impl Player {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,8 +537,14 @@ impl Player {
|
||||
// TODO:
|
||||
// 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
|
||||
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
|
||||
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 {
|
||||
self.kick(TextComponent::text("Invalid window ID"));
|
||||
return;
|
||||
|
||||
@ -10,34 +10,18 @@ const DESCRIPTION: &str =
|
||||
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| {
|
||||
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);
|
||||
let mut container = match server.open_containers.get_mut(&0) {
|
||||
match server.open_containers.get_mut(&0) {
|
||||
Some(ender_chest) => {
|
||||
if ender_chest.try_open(entity_id).is_none() {
|
||||
ender_chest.add_player(entity_id);
|
||||
}
|
||||
ender_chest.try_open(entity_id).unwrap()
|
||||
}
|
||||
None => {
|
||||
let open_container = OpenContainer::empty(entity_id);
|
||||
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(
|
||||
&container,
|
||||
"minecraft:generic_9x3",
|
||||
Some("Ender Chest"),
|
||||
None,
|
||||
state_id,
|
||||
);
|
||||
}
|
||||
player.open_container(server, "minecraft:generic_9x3", Some("Ender Chest"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
use crate::{client::Client, server::Server};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_core::{
|
||||
math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3},
|
||||
@ -26,16 +24,13 @@ use pumpkin_protocol::{
|
||||
ConnectionState, RawPacket, ServerPacket, VarInt,
|
||||
};
|
||||
|
||||
use pumpkin_protocol::server::play::SCloseContainer;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use pumpkin_world::vector3::Vector3;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
use crate::{
|
||||
client::{authentication::GameProfile, Client, PlayerConfig},
|
||||
server::Server,
|
||||
world::World,
|
||||
util::boundingbox::BoundingBox
|
||||
};
|
||||
|
||||
pub struct PlayerAbilities {
|
||||
@ -460,6 +455,10 @@ impl Player {
|
||||
self.handle_click_container(server, SClickContainer::read(bytebuf)?);
|
||||
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);
|
||||
Ok(())
|
||||
|
||||
@ -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 image::GenericImageView;
|
||||
use mio::Token;
|
||||
@ -20,19 +10,21 @@ use pumpkin_protocol::{
|
||||
CURRENT_MC_PROTOCOL,
|
||||
};
|
||||
use pumpkin_world::dimension::Dimension;
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
config::{AdvancedConfiguration, BasicConfiguration},
|
||||
entity::player::{GameMode, Player},
|
||||
world::World,
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
io::Cursor,
|
||||
path::Path,
|
||||
sync::{
|
||||
atomic::{AtomicI32, Ordering},
|
||||
Arc, Mutex, MutexGuard,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{client::Client, entity::player::Player, world::World};
|
||||
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";
|
||||
|
||||
@ -55,7 +47,6 @@ pub struct Server {
|
||||
/// Cache the registry so we don't have to parse it every time a player joins
|
||||
pub cached_registry: Vec<Registry>,
|
||||
|
||||
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
|
||||
pub open_containers: HashMap<u64, OpenContainer>,
|
||||
entity_id: AtomicI32,
|
||||
|
||||
@ -102,6 +93,7 @@ impl Server {
|
||||
Self {
|
||||
plugin_loader,
|
||||
cached_registry: Registry::get_static(),
|
||||
open_containers: HashMap::new(),
|
||||
// 0 is invalid
|
||||
entity_id: 2.into(),
|
||||
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
|
||||
@ -111,8 +103,6 @@ impl Server {
|
||||
status_response,
|
||||
status_response_json,
|
||||
public_key_der,
|
||||
open_containers: HashMap::new(),
|
||||
base_config: config.0,
|
||||
auth_client,
|
||||
}
|
||||
}
|
||||
@ -134,188 +124,20 @@ impl Server {
|
||||
client,
|
||||
world.clone(),
|
||||
entity_id,
|
||||
self.base_config.hardcore,
|
||||
&["minecraft:overworld"],
|
||||
self.base_config.max_players.into(),
|
||||
self.base_config.view_distance.into(), // TODO: view distance
|
||||
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
|
||||
gamemode,
|
||||
)));
|
||||
world.lock().await.add_player(token, player.clone());
|
||||
(player, world)
|
||||
}
|
||||
|
||||
pub fn try_get_container(
|
||||
&self,
|
||||
player_id: EntityId,
|
||||
container_id: u64,
|
||||
) -> Option<MutexGuard<Box<dyn Container>>> {
|
||||
) -> 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
|
||||
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
|
||||
pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], packet: &P)
|
||||
where
|
||||
|
||||
Loading…
Reference in New Issue
Block a user