Support dropping items out of Inventory

This commit is contained in:
Alexander Medvedev 2025-02-20 21:50:59 +01:00
parent 1fb2be46d4
commit 1f414f347c
6 changed files with 72 additions and 30 deletions

View File

@ -99,6 +99,7 @@ impl Click {
}
}
#[derive(Debug)]
pub enum ClickType {
MouseClick(MouseClick),
ShiftClick,
@ -114,6 +115,7 @@ pub enum MouseClick {
Right,
}
#[derive(Debug)]
pub enum KeyClick {
Slot(u8),
Offhand,
@ -124,6 +126,7 @@ pub enum Slot {
OutsideInventory,
}
#[derive(Debug)]
pub enum DropType {
SingleItem,
FullStack,
@ -134,7 +137,7 @@ pub enum MouseDragType {
Right,
Middle,
}
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum MouseDragState {
Start(MouseDragType),
AddSlot(usize),

View File

@ -40,7 +40,7 @@ impl ClientPacket for CSetEquipment {
equipment
.1
.serialize(&mut serializer)
.expect("Could not serialize packet");
.expect("Could not serialize Equipment Slot");
bytebuf.put(serializer.output);
}
}

View File

@ -1,6 +1,6 @@
use std::sync::{
Arc,
atomic::{AtomicI8, AtomicU8},
atomic::{AtomicI8, AtomicU8, AtomicU32},
};
use async_trait::async_trait;
@ -16,6 +16,7 @@ pub struct ItemEntity {
entity: Entity,
item: ItemStack,
count: AtomicU8,
item_age: AtomicU32,
pickup_delay: AtomicI8,
}
@ -25,6 +26,7 @@ impl ItemEntity {
entity,
item: stack,
count: AtomicU8::new(stack.item_count),
item_age: AtomicU32::new(0),
pickup_delay: AtomicI8::new(10), // Vanilla
}
}
@ -43,6 +45,13 @@ impl EntityBase for ItemEntity {
self.pickup_delay
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
}
let age = self
.item_age
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if age >= 6000 {
self.entity.remove().await;
}
}
async fn on_player_collision(&self, player: Arc<Player>) {
if self.pickup_delay.load(std::sync::atomic::Ordering::Relaxed) == 0 {

View File

@ -982,22 +982,23 @@ impl Player {
.await;
}
pub async fn drop_item(&self, server: &Server, drop_stack: bool) {
pub async fn drop_item(&self, server: &Server, stack: ItemStack) {
let entity = server.add_entity(
self.living_entity.entity.pos.load(),
EntityType::ITEM,
&self.world().await,
);
let item_entity = Arc::new(ItemEntity::new(entity, stack));
self.world().await.spawn_entity(item_entity.clone()).await;
item_entity.send_meta_packet().await;
}
pub async fn drop_held_item(&self, server: &Server, drop_stack: bool) {
let mut inv = self.inventory.lock().await;
if let Some(item) = inv.held_item_mut() {
let drop_amount = if drop_stack { item.item_count } else { 1 };
let entity = server.add_entity(
self.living_entity.entity.pos.load(),
EntityType::ITEM,
&self.world().await,
);
let item_entity = Arc::new(ItemEntity::new(
entity,
ItemStack::new(drop_amount, item.item),
));
self.world().await.spawn_entity(item_entity.clone()).await;
item_entity.send_meta_packet().await;
// decrase item in hotbar
self.drop_item(server, ItemStack::new(drop_amount, item.item))
.await;
inv.decrease_current_stack(drop_amount);
}
}
@ -1243,7 +1244,7 @@ impl Player {
.await;
}
SSetCreativeSlot::PACKET_ID => {
self.handle_set_creative_slot(SSetCreativeSlot::read(bytebuf)?)
self.handle_set_creative_slot(server, SSetCreativeSlot::read(bytebuf)?)
.await?;
}
SSwingArm::PACKET_ID => {

View File

@ -4,7 +4,7 @@ use pumpkin_data::item::Item;
use pumpkin_data::screen::WindowType;
use pumpkin_inventory::Container;
use pumpkin_inventory::container_click::{
Click, ClickType, KeyClick, MouseClick, MouseDragState, MouseDragType,
Click, ClickType, DropType, KeyClick, MouseClick, MouseDragState, MouseDragType,
};
use pumpkin_inventory::drag_handler::DragHandler;
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
@ -157,6 +157,7 @@ impl Player {
let click_slot = click.slot;
self.match_click_behaviour(
server,
opened_container.as_deref_mut(),
click,
drag_handler,
@ -219,6 +220,7 @@ impl Player {
async fn match_click_behaviour(
&self,
server: &Server,
opened_container: Option<&mut Box<dyn Container>>,
click: Click,
drag_handler: &DragHandler,
@ -228,6 +230,7 @@ impl Player {
match click.click_type {
ClickType::MouseClick(mouse_click) => {
self.mouse_click(
server,
opened_container,
mouse_click,
click.slot,
@ -273,8 +276,18 @@ impl Player {
self.mouse_drag(drag_handler, opened_container, drag_state)
.await
}
ClickType::DropType(_drop_type) => {
log::debug!("todo");
ClickType::DropType(drop_type) => {
let carried_item = self.carried_item.load();
if let Some(item) = carried_item {
match drop_type {
DropType::FullStack => self.drop_item(server, item).await,
DropType::SingleItem => {
let mut item = item;
item.item_count = 1;
self.drop_item(server, item).await;
}
};
}
Ok(())
}
}
@ -282,6 +295,7 @@ impl Player {
async fn mouse_click(
&self,
server: &Server,
opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick,
slot: container_click::Slot,
@ -289,9 +303,9 @@ impl Player {
) -> Result<(), InventoryError> {
let mut inventory = self.inventory().lock().await;
let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container);
let mut carried_item = self.carried_item.load();
match slot {
container_click::Slot::Normal(slot) => {
let mut carried_item = self.carried_item.load();
let res = container.handle_item_change(
&mut carried_item,
slot,
@ -301,7 +315,19 @@ impl Player {
self.carried_item.store(carried_item);
res
}
container_click::Slot::OutsideInventory => Ok(()),
container_click::Slot::OutsideInventory => {
if let Some(item) = carried_item {
match mouse_click {
MouseClick::Left => self.drop_item(server, item).await,
MouseClick::Right => {
let mut item = item;
item.item_count = 1;
self.drop_item(server, item).await;
}
};
}
Ok(())
}
}
}

View File

@ -928,10 +928,10 @@ impl Player {
self.update_sequence(player_action.sequence.0);
}
Status::DropItem => {
self.drop_item(server, false).await;
self.drop_held_item(server, false).await;
}
Status::DropItemStack => {
self.drop_item(server, true).await;
self.drop_held_item(server, true).await;
}
Status::ShootArrowOrFinishEating | Status::SwapItem => {
log::debug!("todo");
@ -1149,20 +1149,23 @@ impl Player {
pub async fn handle_set_creative_slot(
&self,
server: &Server,
packet: SSetCreativeSlot,
) -> Result<(), InventoryError> {
if self.gamemode.load() != GameMode::Creative {
return Err(InventoryError::PermissionError);
}
let valid_slot = packet.slot >= 0 && packet.slot <= 45;
let item_stack = packet.clicked_item.to_item();
if valid_slot {
self.inventory().lock().await.set_slot(
packet.slot as usize,
packet.clicked_item.to_item(),
true,
)?;
self.inventory()
.lock()
.await
.set_slot(packet.slot as usize, item_stack, true)?;
} else {
// Item drop
self.drop_item(server, item_stack.unwrap()).await;
};
// TODO: The Item was dropped per drag and drop,
Ok(())
}