Skip to content

Commit

Permalink
Support dropping items out of Inventory
Browse files Browse the repository at this point in the history
  • Loading branch information
Snowiiii committed Feb 20, 2025
1 parent 1fb2be4 commit 1f414f3
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 30 deletions.
5 changes: 4 additions & 1 deletion pumpkin-inventory/src/container_click.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl Click {
}
}

#[derive(Debug)]
pub enum ClickType {
MouseClick(MouseClick),
ShiftClick,
Expand All @@ -114,6 +115,7 @@ pub enum MouseClick {
Right,
}

#[derive(Debug)]
pub enum KeyClick {
Slot(u8),
Offhand,
Expand All @@ -124,6 +126,7 @@ pub enum Slot {
OutsideInventory,
}

#[derive(Debug)]
pub enum DropType {
SingleItem,
FullStack,
Expand All @@ -134,7 +137,7 @@ pub enum MouseDragType {
Right,
Middle,
}
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum MouseDragState {
Start(MouseDragType),
AddSlot(usize),
Expand Down
2 changes: 1 addition & 1 deletion pumpkin-protocol/src/client/play/set_equipment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
11 changes: 10 additions & 1 deletion pumpkin/src/entity/item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::{
Arc,
atomic::{AtomicI8, AtomicU8},
atomic::{AtomicI8, AtomicU8, AtomicU32},
};

use async_trait::async_trait;
Expand All @@ -16,6 +16,7 @@ pub struct ItemEntity {
entity: Entity,
item: ItemStack,
count: AtomicU8,
item_age: AtomicU32,
pickup_delay: AtomicI8,
}

Expand All @@ -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
}
}
Expand All @@ -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 {
Expand Down
29 changes: 15 additions & 14 deletions pumpkin/src/entity/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 => {
Expand Down
36 changes: 31 additions & 5 deletions pumpkin/src/net/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -157,6 +157,7 @@ impl Player {

let click_slot = click.slot;
self.match_click_behaviour(
server,
opened_container.as_deref_mut(),
click,
drag_handler,
Expand Down Expand Up @@ -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,
Expand All @@ -228,6 +230,7 @@ impl Player {
match click.click_type {
ClickType::MouseClick(mouse_click) => {
self.mouse_click(
server,
opened_container,
mouse_click,
click.slot,
Expand Down Expand Up @@ -273,25 +276,36 @@ 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(())
}
}
}

async fn mouse_click(
&self,
server: &Server,
opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick,
slot: container_click::Slot,
taking_crafted: bool,
) -> 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,
Expand All @@ -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(())
}
}
}

Expand Down
19 changes: 11 additions & 8 deletions pumpkin/src/net/packet/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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(())
}

Expand Down

0 comments on commit 1f414f3

Please sign in to comment.