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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ use pumpkin_data::item::Item;
use pumpkin_data::screen::WindowType; use pumpkin_data::screen::WindowType;
use pumpkin_inventory::Container; use pumpkin_inventory::Container;
use pumpkin_inventory::container_click::{ 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::drag_handler::DragHandler;
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait}; use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
@ -157,6 +157,7 @@ impl Player {
let click_slot = click.slot; let click_slot = click.slot;
self.match_click_behaviour( self.match_click_behaviour(
server,
opened_container.as_deref_mut(), opened_container.as_deref_mut(),
click, click,
drag_handler, drag_handler,
@ -219,6 +220,7 @@ impl Player {
async fn match_click_behaviour( async fn match_click_behaviour(
&self, &self,
server: &Server,
opened_container: Option<&mut Box<dyn Container>>, opened_container: Option<&mut Box<dyn Container>>,
click: Click, click: Click,
drag_handler: &DragHandler, drag_handler: &DragHandler,
@ -228,6 +230,7 @@ impl Player {
match click.click_type { match click.click_type {
ClickType::MouseClick(mouse_click) => { ClickType::MouseClick(mouse_click) => {
self.mouse_click( self.mouse_click(
server,
opened_container, opened_container,
mouse_click, mouse_click,
click.slot, click.slot,
@ -273,8 +276,18 @@ impl Player {
self.mouse_drag(drag_handler, opened_container, drag_state) self.mouse_drag(drag_handler, opened_container, drag_state)
.await .await
} }
ClickType::DropType(_drop_type) => { ClickType::DropType(drop_type) => {
log::debug!("todo"); 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(()) Ok(())
} }
} }
@ -282,6 +295,7 @@ impl Player {
async fn mouse_click( async fn mouse_click(
&self, &self,
server: &Server,
opened_container: Option<&mut Box<dyn Container>>, opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick, mouse_click: MouseClick,
slot: container_click::Slot, slot: container_click::Slot,
@ -289,9 +303,9 @@ impl Player {
) -> Result<(), InventoryError> { ) -> Result<(), InventoryError> {
let mut inventory = self.inventory().lock().await; let mut inventory = self.inventory().lock().await;
let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container);
let mut carried_item = self.carried_item.load();
match slot { match slot {
container_click::Slot::Normal(slot) => { container_click::Slot::Normal(slot) => {
let mut carried_item = self.carried_item.load();
let res = container.handle_item_change( let res = container.handle_item_change(
&mut carried_item, &mut carried_item,
slot, slot,
@ -301,7 +315,19 @@ impl Player {
self.carried_item.store(carried_item); self.carried_item.store(carried_item);
res 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); self.update_sequence(player_action.sequence.0);
} }
Status::DropItem => { Status::DropItem => {
self.drop_item(server, false).await; self.drop_held_item(server, false).await;
} }
Status::DropItemStack => { Status::DropItemStack => {
self.drop_item(server, true).await; self.drop_held_item(server, true).await;
} }
Status::ShootArrowOrFinishEating | Status::SwapItem => { Status::ShootArrowOrFinishEating | Status::SwapItem => {
log::debug!("todo"); log::debug!("todo");
@ -1149,20 +1149,23 @@ impl Player {
pub async fn handle_set_creative_slot( pub async fn handle_set_creative_slot(
&self, &self,
server: &Server,
packet: SSetCreativeSlot, packet: SSetCreativeSlot,
) -> Result<(), InventoryError> { ) -> Result<(), InventoryError> {
if self.gamemode.load() != GameMode::Creative { if self.gamemode.load() != GameMode::Creative {
return Err(InventoryError::PermissionError); return Err(InventoryError::PermissionError);
} }
let valid_slot = packet.slot >= 0 && packet.slot <= 45; let valid_slot = packet.slot >= 0 && packet.slot <= 45;
let item_stack = packet.clicked_item.to_item();
if valid_slot { if valid_slot {
self.inventory().lock().await.set_slot( self.inventory()
packet.slot as usize, .lock()
packet.clicked_item.to_item(), .await
true, .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(()) Ok(())
} }