From f396310ac247c3e3c98d03748288fd189c7c166a Mon Sep 17 00:00:00 2001 From: Bafran Date: Fri, 20 Dec 2024 22:09:32 -0800 Subject: [PATCH 01/12] Pick Item Changes --- pumpkin-inventory/src/player.rs | 19 ++++++++++ pumpkin-protocol/src/server/play/mod.rs | 2 + .../src/server/play/s_pick_item.rs | 10 +++++ pumpkin/src/entity/player.rs | 5 ++- pumpkin/src/net/packet/play.rs | 38 ++++++++++++++++++- 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 pumpkin-protocol/src/server/play/s_pick_item.rs diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 3a230385c..817288881 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -116,6 +116,25 @@ impl PlayerInventory { &mut self.items[self.selected + 36 - 9] } + pub fn set_held_item(&mut self, item: Option) { + debug_assert!((0..9).contains(&self.selected)); + self.items[self.selected + 36 - 9] = item; + } + + pub fn get_hotbar_slot(&mut self) -> Option { + // // first searches the player's hotbar for an empty slot, starting from the current slot and looping around to the slot before it. + // for i in 0..9 { + // let slot = (self.selected + i) % 9; + // if self.items[slot + 36].is_none() { + // return Some(slot + 36); + // } + // } + + // If there still are no slots that meet that criteria, then the server uses the currently selected slot + Some(self.selected) + } + + pub fn slots(&self) -> Vec> { let mut slots = vec![self.crafting_output.as_ref()]; slots.extend(self.crafting.iter().map(|c| c.as_ref())); diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 3fa607d6c..8c854dd2d 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -15,6 +15,7 @@ mod s_player_abilities; mod s_player_action; mod s_player_command; mod s_player_ground; +mod s_pick_item; mod s_player_input; mod s_player_position; mod s_player_position_rotation; @@ -42,6 +43,7 @@ pub use s_player_abilities::*; pub use s_player_action::*; pub use s_player_command::*; pub use s_player_ground::*; +pub use s_pick_item::*; pub use s_player_input::*; pub use s_player_position::*; pub use s_player_position_rotation::*; diff --git a/pumpkin-protocol/src/server/play/s_pick_item.rs b/pumpkin-protocol/src/server/play/s_pick_item.rs new file mode 100644 index 000000000..56811d91b --- /dev/null +++ b/pumpkin-protocol/src/server/play/s_pick_item.rs @@ -0,0 +1,10 @@ +use pumpkin_macros::server_packet; +use serde::Deserialize; + +use crate::VarInt; + +#[derive(Deserialize)] +#[server_packet("play:pick_item_from_block")] +pub struct SPickItem { + pub slot: VarInt, +} diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index c197f58f9..593ca2ca0 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -39,7 +39,7 @@ use pumpkin_protocol::{ SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, SCommandSuggestion, SConfirmTeleport, SInteract, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, + SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, SPickItem, }, RawPacket, ServerPacket, SoundCategory, }; @@ -726,6 +726,9 @@ impl Player { SSetPlayerGround::PACKET_ID => { self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?); } + SPickItem::PACKET_ID => { + self.handle_pick_item(SPickItem::read(bytebuf)?).await; + } SPlayerAbilities::PACKET_ID => { self.handle_player_abilities(SPlayerAbilities::read(bytebuf)?) .await; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index baf904659..72ee22519 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -20,7 +20,9 @@ use pumpkin_core::{ }; use pumpkin_inventory::InventoryError; use pumpkin_protocol::codec::var_int::VarInt; +use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem}; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; +use pumpkin_protocol::slot::Slot; use pumpkin_protocol::{ client::play::CCommandSuggestions, server::play::{SCloseContainer, SCommandSuggestion, SKeepAlive, SSetPlayerGround, SUseItem}, @@ -34,7 +36,7 @@ use pumpkin_protocol::{ Action, ActionType, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, + SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, SPickItem, }, }; use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace}; @@ -310,6 +312,40 @@ impl Player { .store(ground.on_ground, std::sync::atomic::Ordering::Relaxed); } + pub async fn handle_pick_item(&self, pick_item: SPickItem) { + let source_slot = pick_item.slot.0 as usize; + let mut inventory = self.inventory().lock().await; + + let dest_slot = inventory.get_hotbar_slot().unwrap(); + let dest_item = inventory.get_slot(source_slot).unwrap().as_ref(); + + log::warn!("Picking item from slot {} to slot {}", source_slot, dest_slot); + + // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. + let slot = Slot::from(dest_item); + let packet = CSetContainerSlot::new( + -2, + inventory.state_id as i32, + dest_slot, + &slot + ); + self.client.send_packet(&packet).await; + + // Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. + let slot = Slot::from(None); + let packet = CSetContainerSlot::new( + -2, + inventory.state_id as i32, + source_slot, + &slot + ); + self.client.send_packet(&packet).await; + + // Set Held Item, switching to the newly chosen slot. + let packet = CSetHeldItem::new(dest_slot as i8); + self.client.send_packet(&packet).await; + } + pub async fn handle_player_command(&self, command: SPlayerCommand) { if command.entity_id != self.entity_id().into() { return; From 11d787ae9f5c5092438d6b4fd17cf8e4729f69ee Mon Sep 17 00:00:00 2001 From: Bafran Date: Thu, 26 Dec 2024 10:59:55 -0800 Subject: [PATCH 02/12] Changed some logging --- pumpkin/src/net/packet/play.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 72ee22519..b0f80d50a 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -316,7 +316,7 @@ impl Player { let source_slot = pick_item.slot.0 as usize; let mut inventory = self.inventory().lock().await; - let dest_slot = inventory.get_hotbar_slot().unwrap(); + let dest_slot = inventory.get_hotbar_slot().unwrap() + 36; let dest_item = inventory.get_slot(source_slot).unwrap().as_ref(); log::warn!("Picking item from slot {} to slot {}", source_slot, dest_slot); @@ -324,8 +324,8 @@ impl Player { // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. let slot = Slot::from(dest_item); let packet = CSetContainerSlot::new( - -2, - inventory.state_id as i32, + (-2) as i8, + (inventory.state_id) as i32, dest_slot, &slot ); @@ -334,8 +334,8 @@ impl Player { // Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. let slot = Slot::from(None); let packet = CSetContainerSlot::new( - -2, - inventory.state_id as i32, + (-2) as i8, + (inventory.state_id) as i32, source_slot, &slot ); From 41bc0e468fc27742aff181aa00dd07757e686312 Mon Sep 17 00:00:00 2001 From: Bafran Date: Fri, 27 Dec 2024 14:34:00 -0800 Subject: [PATCH 03/12] Pick Item working for hotbar slots --- pumpkin-inventory/src/player.rs | 18 ++-- .../src/server/play/s_pick_item.rs | 8 +- pumpkin/src/entity/player.rs | 6 +- pumpkin/src/net/packet/play.rs | 91 ++++++++++++------- 4 files changed, 75 insertions(+), 48 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 817288881..d3d37c971 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -121,17 +121,15 @@ impl PlayerInventory { self.items[self.selected + 36 - 9] = item; } - pub fn get_hotbar_slot(&mut self) -> Option { - // // first searches the player's hotbar for an empty slot, starting from the current slot and looping around to the slot before it. - // for i in 0..9 { - // let slot = (self.selected + i) % 9; - // if self.items[slot + 36].is_none() { - // return Some(slot + 36); - // } - // } + pub fn get_pick_item_hotbar_slot(&mut self, item_id: u16) -> usize { + for slot in 35..=44 { + match &self.items[slot - 9] { + Some(item) if item.item_id == item_id => return slot, + _ => continue, + } + } - // If there still are no slots that meet that criteria, then the server uses the currently selected slot - Some(self.selected) + self.selected + 36 } diff --git a/pumpkin-protocol/src/server/play/s_pick_item.rs b/pumpkin-protocol/src/server/play/s_pick_item.rs index 56811d91b..f337bbb9a 100644 --- a/pumpkin-protocol/src/server/play/s_pick_item.rs +++ b/pumpkin-protocol/src/server/play/s_pick_item.rs @@ -1,10 +1,10 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::server_packet; use serde::Deserialize; -use crate::VarInt; - #[derive(Deserialize)] #[server_packet("play:pick_item_from_block")] -pub struct SPickItem { - pub slot: VarInt, +pub struct SPickItemFromBlock { + pub pos: WorldPosition, + pub include_data: bool, } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 593ca2ca0..eaa0b3361 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -39,7 +39,7 @@ use pumpkin_protocol::{ SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, SCommandSuggestion, SConfirmTeleport, SInteract, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, SPickItem, + SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, SPickItemFromBlock, }, RawPacket, ServerPacket, SoundCategory, }; @@ -726,8 +726,8 @@ impl Player { SSetPlayerGround::PACKET_ID => { self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?); } - SPickItem::PACKET_ID => { - self.handle_pick_item(SPickItem::read(bytebuf)?).await; + SPickItemFromBlock::PACKET_ID => { + self.handle_pick_item_from_block(SPickItemFromBlock::read(bytebuf)?).await; } SPlayerAbilities::PACKET_ID => { self.handle_player_abilities(SPlayerAbilities::read(bytebuf)?) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index b0f80d50a..2a4945344 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -36,11 +36,12 @@ use pumpkin_protocol::{ Action, ActionType, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, SPickItem, + SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, SPickItemFromBlock, }, }; use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace}; use pumpkin_world::item::item_registry::get_item_by_id; +use pumpkin_world::item::ItemStack; use thiserror::Error; fn modulus(a: f32, b: f32) -> f32 { @@ -312,38 +313,66 @@ impl Player { .store(ground.on_ground, std::sync::atomic::Ordering::Relaxed); } - pub async fn handle_pick_item(&self, pick_item: SPickItem) { - let source_slot = pick_item.slot.0 as usize; - let mut inventory = self.inventory().lock().await; - - let dest_slot = inventory.get_hotbar_slot().unwrap() + 36; - let dest_item = inventory.get_slot(source_slot).unwrap().as_ref(); + pub async fn handle_pick_item_from_block(&self, pick_item: SPickItemFromBlock) { + let pos = pick_item.pos; + // If player is in creative mode && include_data, then pick block with additional data + let include_data = pick_item.include_data; + + if self.can_interact_with_block_at(&pos, 1.0) { + let world = self.world(); + let block = world.get_block(pos).await; + if let Ok(block) = block { + let item_id = block.item_id; + let mut inventory = self.inventory().lock().await; + + log::warn!("Item id: {}", item_id); + + let dest_slot = inventory.get_pick_item_hotbar_slot(item_id); + log::warn!("Dest slot: {}", dest_slot); + // let picked_item = ItemStack::new(1, item_id); + // let slot = Slot::from(&picked_item); + // // Returns previous value + // inventory.state_id += 1; + // // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. + // let packet = CSetContainerSlot::new( + // (-2) as i8, + // (inventory.state_id) as i32, + // dest_slot, + // &slot + // ); + // self.client.send_packet(&packet).await; + + // Set held item + let packet = CSetHeldItem::new((dest_slot - 36) as i8); + self.client.send_packet(&packet).await; + } + } else { + log::warn!("Player cannot interact with block at {}", pos); + } - log::warn!("Picking item from slot {} to slot {}", source_slot, dest_slot); - - // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. - let slot = Slot::from(dest_item); - let packet = CSetContainerSlot::new( - (-2) as i8, - (inventory.state_id) as i32, - dest_slot, - &slot - ); - self.client.send_packet(&packet).await; - - // Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. - let slot = Slot::from(None); - let packet = CSetContainerSlot::new( - (-2) as i8, - (inventory.state_id) as i32, - source_slot, - &slot - ); - self.client.send_packet(&packet).await; - // Set Held Item, switching to the newly chosen slot. - let packet = CSetHeldItem::new(dest_slot as i8); - self.client.send_packet(&packet).await; + // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. + // let packet = CSetContainerSlot::new( + // (-2) as i8, + // (inventory.state_id) as i32, + // dest_slot, + // &slot + // ); + // self.client.send_packet(&packet).await; + + // // Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. + // let slot = Slot::from(None); + // let packet = CSetContainerSlot::new( + // (-2) as i8, + // (inventory.state_id) as i32, + // source_slot, + // &slot + // ); + // self.client.send_packet(&packet).await; + + // // Set Held Item, switching to the newly chosen slot. + // let packet = CSetHeldItem::new(dest_slot as i8); + // self.client.send_packet(&packet).await; } pub async fn handle_player_command(&self, command: SPlayerCommand) { From b9b0e1fe765a5bac3764455998db32137a5ace4a Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 11:46:46 -0800 Subject: [PATCH 04/12] Basic functionality, changes needed to adhere to expected minecraft behaviour --- pumpkin-inventory/src/player.rs | 21 +++++-- pumpkin/src/net/packet/play.rs | 107 ++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index d3d37c971..d065a506e 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -121,17 +121,30 @@ impl PlayerInventory { self.items[self.selected + 36 - 9] = item; } - pub fn get_pick_item_hotbar_slot(&mut self, item_id: u16) -> usize { - for slot in 35..=44 { + pub fn get_slot_with_item(&self, item_id: u16) -> Option { + for slot in 9..=44 { match &self.items[slot - 9] { - Some(item) if item.item_id == item_id => return slot, + Some(item) if item.item_id == item_id => return Some(slot), _ => continue, } } - self.selected + 36 + None } + + pub fn get_pick_item_hotbar_slot(&self) -> usize { + if self.items[self.selected + 36 - 9].is_none() { + return self.selected + } + + for slot in 0..9 { + if self.items[slot + 36 - 9].is_none() { + return slot; + } + } + self.selected + } pub fn slots(&self) -> Vec> { let mut slots = vec![self.crafting_output.as_ref()]; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 2a4945344..d709f31bc 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -41,7 +41,7 @@ use pumpkin_protocol::{ }; use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace}; use pumpkin_world::item::item_registry::get_item_by_id; -use pumpkin_world::item::ItemStack; +use pumpkin_world::item::{self, ItemStack}; use thiserror::Error; fn modulus(a: f32, b: f32) -> f32 { @@ -315,8 +315,8 @@ impl Player { pub async fn handle_pick_item_from_block(&self, pick_item: SPickItemFromBlock) { let pos = pick_item.pos; - // If player is in creative mode && include_data, then pick block with additional data - let include_data = pick_item.include_data; + // TODO: Include block data + let _include_data = pick_item.include_data; if self.can_interact_with_block_at(&pos, 1.0) { let world = self.world(); @@ -325,54 +325,69 @@ impl Player { let item_id = block.item_id; let mut inventory = self.inventory().lock().await; - log::warn!("Item id: {}", item_id); - - let dest_slot = inventory.get_pick_item_hotbar_slot(item_id); - log::warn!("Dest slot: {}", dest_slot); - // let picked_item = ItemStack::new(1, item_id); - // let slot = Slot::from(&picked_item); - // // Returns previous value - // inventory.state_id += 1; - // // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. - // let packet = CSetContainerSlot::new( - // (-2) as i8, - // (inventory.state_id) as i32, - // dest_slot, - // &slot - // ); - // self.client.send_packet(&packet).await; + let source_slot = inventory.get_slot_with_item(item_id); + let mut dest_slot = inventory.get_pick_item_hotbar_slot(); + + let slot_index = if let Some(source_slot) = source_slot { + source_slot + } else { + // Cases if we DONT have the item in our inventory + if self.gamemode.load() == GameMode::Creative { + // If player is in creative mode, create the item and place it in an empty slot + let item_stack = ItemStack::new(1, item_id); + let slot = Slot::from(&item_stack); + inventory.state_id += 1; + let packet = CSetContainerSlot::new( + 0, + (inventory.state_id) as i32, + dest_slot + 36, // dest_slot is the hotbar slot, so we need to add 36 to get the correct slot index + &slot, + ); + self.client.send_packet(&packet).await; + // Let slot_index be 0 to bypass the if statement below + 0 + } else { + // Handle survival mode case + return; + } + }; + + if !(slot_index > 36 && slot_index <= 44) { + let item_stack = match inventory.get_slot(slot_index) { + Ok(Some(stack)) => stack, + Ok(None) => return, // or handle empty slot + Err(_) => return, // or handle error case + }; + + // Set dest hotbar slot to picked item + let slot = Slot::from(&*item_stack); + inventory.state_id += 1; + let packet = CSetContainerSlot::new( + 0, + (inventory.state_id) as i32, + dest_slot + 36, // dest_slot is the hotbar slot, so we need to add 36 to get the correct slot index + &slot, + ); + self.client.send_packet(&packet).await; + // Clear source slot + let slot = Slot::from(None); + inventory.state_id += 1; + let packet = CSetContainerSlot::new( + 0, + (inventory.state_id) as i32, + slot_index, + &slot, + ); + self.client.send_packet(&packet).await; + } else { + dest_slot = slot_index - 36; + } // Set held item - let packet = CSetHeldItem::new((dest_slot - 36) as i8); + let packet = CSetHeldItem::new(dest_slot as i8); self.client.send_packet(&packet).await; } - } else { - log::warn!("Player cannot interact with block at {}", pos); } - - - // Set Container Slot with window ID set to -2, updating the chosen hotbar slot. - // let packet = CSetContainerSlot::new( - // (-2) as i8, - // (inventory.state_id) as i32, - // dest_slot, - // &slot - // ); - // self.client.send_packet(&packet).await; - - // // Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. - // let slot = Slot::from(None); - // let packet = CSetContainerSlot::new( - // (-2) as i8, - // (inventory.state_id) as i32, - // source_slot, - // &slot - // ); - // self.client.send_packet(&packet).await; - - // // Set Held Item, switching to the newly chosen slot. - // let packet = CSetHeldItem::new(dest_slot as i8); - // self.client.send_packet(&packet).await; } pub async fn handle_player_command(&self, command: SPlayerCommand) { From 13fa16bf30451973220cf2b69828ceee9ef6b446 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 12:26:40 -0800 Subject: [PATCH 05/12] Clean up code --- pumpkin-inventory/src/player.rs | 6 +- pumpkin-protocol/src/server/play/mod.rs | 4 +- pumpkin/src/entity/player.rs | 11 +- pumpkin/src/net/packet/play.rs | 154 +++++++++++++----------- 4 files changed, 93 insertions(+), 82 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index d065a506e..948f53b44 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -128,13 +128,13 @@ impl PlayerInventory { _ => continue, } } - + None } pub fn get_pick_item_hotbar_slot(&self) -> usize { if self.items[self.selected + 36 - 9].is_none() { - return self.selected + return self.selected; } for slot in 0..9 { @@ -142,7 +142,7 @@ impl PlayerInventory { return slot; } } - + self.selected } diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 8c854dd2d..3995f3326 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -10,12 +10,12 @@ mod s_confirm_teleport; mod s_cookie_response; mod s_interact; mod s_keep_alive; +mod s_pick_item; mod s_ping_request; mod s_player_abilities; mod s_player_action; mod s_player_command; mod s_player_ground; -mod s_pick_item; mod s_player_input; mod s_player_position; mod s_player_position_rotation; @@ -38,12 +38,12 @@ pub use s_confirm_teleport::*; pub use s_cookie_response::*; pub use s_interact::*; pub use s_keep_alive::*; +pub use s_pick_item::*; pub use s_ping_request::*; pub use s_player_abilities::*; pub use s_player_action::*; pub use s_player_command::*; pub use s_player_ground::*; -pub use s_pick_item::*; pub use s_player_input::*; pub use s_player_position::*; pub use s_player_position_rotation::*; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index eaa0b3361..5d58a7d71 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -37,9 +37,10 @@ use pumpkin_protocol::{ }, server::play::{ SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, - SCommandSuggestion, SConfirmTeleport, SInteract, SPlayerAbilities, SPlayerAction, - SPlayerCommand, SPlayerInput, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, SPickItemFromBlock, + SCommandSuggestion, SConfirmTeleport, SInteract, SPickItemFromBlock, SPlayerAbilities, + SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, + SUseItemOn, }, RawPacket, ServerPacket, SoundCategory, }; @@ -675,6 +676,7 @@ impl Player { } } + #[allow(clippy::too_many_lines)] pub async fn handle_play_packet( self: &Arc, server: &Arc, @@ -727,7 +729,8 @@ impl Player { self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?); } SPickItemFromBlock::PACKET_ID => { - self.handle_pick_item_from_block(SPickItemFromBlock::read(bytebuf)?).await; + self.handle_pick_item_from_block(SPickItemFromBlock::read(bytebuf)?) + .await; } SPlayerAbilities::PACKET_ID => { self.handle_player_abilities(SPlayerAbilities::read(bytebuf)?) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index d709f31bc..b018eae62 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -34,14 +34,14 @@ use pumpkin_protocol::{ }, server::play::{ Action, ActionType, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, - SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAbilities, SPlayerAction, - SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, SPickItemFromBlock, + SConfirmTeleport, SInteract, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities, + SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, + SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, }, }; use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace}; use pumpkin_world::item::item_registry::get_item_by_id; -use pumpkin_world::item::{self, ItemStack}; +use pumpkin_world::item::ItemStack; use thiserror::Error; fn modulus(a: f32, b: f32) -> f32 { @@ -314,80 +314,88 @@ impl Player { } pub async fn handle_pick_item_from_block(&self, pick_item: SPickItemFromBlock) { - let pos = pick_item.pos; - // TODO: Include block data - let _include_data = pick_item.include_data; - - if self.can_interact_with_block_at(&pos, 1.0) { - let world = self.world(); - let block = world.get_block(pos).await; - if let Ok(block) = block { - let item_id = block.item_id; - let mut inventory = self.inventory().lock().await; - - let source_slot = inventory.get_slot_with_item(item_id); - let mut dest_slot = inventory.get_pick_item_hotbar_slot(); - - let slot_index = if let Some(source_slot) = source_slot { - source_slot - } else { - // Cases if we DONT have the item in our inventory - if self.gamemode.load() == GameMode::Creative { - // If player is in creative mode, create the item and place it in an empty slot - let item_stack = ItemStack::new(1, item_id); - let slot = Slot::from(&item_stack); - inventory.state_id += 1; - let packet = CSetContainerSlot::new( - 0, - (inventory.state_id) as i32, - dest_slot + 36, // dest_slot is the hotbar slot, so we need to add 36 to get the correct slot index - &slot, - ); - self.client.send_packet(&packet).await; - // Let slot_index be 0 to bypass the if statement below - 0 - } else { - // Handle survival mode case - return; - } - }; - - if !(slot_index > 36 && slot_index <= 44) { - let item_stack = match inventory.get_slot(slot_index) { - Ok(Some(stack)) => stack, - Ok(None) => return, // or handle empty slot - Err(_) => return, // or handle error case - }; - - // Set dest hotbar slot to picked item - let slot = Slot::from(&*item_stack); - inventory.state_id += 1; - let packet = CSetContainerSlot::new( - 0, - (inventory.state_id) as i32, - dest_slot + 36, // dest_slot is the hotbar slot, so we need to add 36 to get the correct slot index - &slot, - ); - self.client.send_packet(&packet).await; - // Clear source slot - let slot = Slot::from(None); - inventory.state_id += 1; - let packet = CSetContainerSlot::new( - 0, - (inventory.state_id) as i32, - slot_index, - &slot, - ); - self.client.send_packet(&packet).await; - } else { - dest_slot = slot_index - 36; + if !self.can_interact_with_block_at(&pick_item.pos, 1.0) { + return; + } + + let Ok(block) = self.world().get_block(pick_item.pos).await else { + return; + }; + + let mut inventory = self.inventory().lock().await; + let source_slot = inventory.get_slot_with_item(block.item_id); + let mut dest_slot = inventory.get_pick_item_hotbar_slot(); + + // Early return if no source slot and not in creative mode + if source_slot.is_none() && self.gamemode.load() != GameMode::Creative { + return; + } + + match source_slot { + Some(slot_index) if !(slot_index > 36 && slot_index <= 44) => { + // Handle existing item case + let item_stack = match inventory.get_slot(slot_index) { + Ok(Some(stack)) => *stack, + _ => return, + }; + + // Update destination slot + let slot_data = Slot::from(&item_stack); + inventory.state_id += 1; + let dest_packet = CSetContainerSlot::new( + 0, + inventory.state_id as i32, + dest_slot + 36, + &slot_data, + ); + self.client.send_packet(&dest_packet).await; + + if inventory.set_slot(dest_slot + 36, Some(item_stack), false).is_err() { + log::error!("Pick item set slot error!"); + return; } - // Set held item - let packet = CSetHeldItem::new(dest_slot as i8); + // Clear source slot + let slot_data = Slot::from(None); + inventory.state_id += 1; + let source_packet = + CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &slot_data); + self.client.send_packet(&source_packet).await; + + if inventory.set_slot(slot_index, None, false).is_err() { + log::error!("Failed to clear source slot!"); + return; + } + } + Some(slot_index) => { + dest_slot = slot_index - 36; + } + None if self.gamemode.load() == GameMode::Creative => { + // Handle creative mode case + let item_stack = ItemStack::new(1, block.item_id); + let slot_data = Slot::from(&item_stack); + inventory.state_id += 1; + let packet = CSetContainerSlot::new( + 0, + inventory.state_id as i32, + dest_slot + 36, + &slot_data, + ); self.client.send_packet(&packet).await; + + if inventory.set_slot(dest_slot + 36, Some(item_stack), false).is_err() { + log::error!("Pick item set slot error!"); + return; + } } + _ => return, } + + // Update held item + inventory.set_selected(dest_slot); + self.client + .send_packet(&CSetHeldItem::new(dest_slot as i8)) + .await; } pub async fn handle_player_command(&self, command: SPlayerCommand) { From d08cbff18861b0263c63d143f2896f2022ae3367 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 12:47:07 -0800 Subject: [PATCH 06/12] Added todo for pick from entity --- .../src/server/play/s_pick_item.rs | 8 ++++++++ pumpkin/src/net/packet/play.rs | 20 ++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pumpkin-protocol/src/server/play/s_pick_item.rs b/pumpkin-protocol/src/server/play/s_pick_item.rs index f337bbb9a..33d432a2f 100644 --- a/pumpkin-protocol/src/server/play/s_pick_item.rs +++ b/pumpkin-protocol/src/server/play/s_pick_item.rs @@ -8,3 +8,11 @@ pub struct SPickItemFromBlock { pub pos: WorldPosition, pub include_data: bool, } + +// TODO: Handler for this packet +#[derive(Deserialize)] +#[server_packet("play:pick_item_from_entity")] +pub struct SPickItemFromEntity { + pub id: i32, + pub include_data: bool, +} diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index b018eae62..7ce6fc125 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -34,9 +34,9 @@ use pumpkin_protocol::{ }, server::play::{ Action, ActionType, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, - SConfirmTeleport, SInteract, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities, - SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, - SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, + SConfirmTeleport, SInteract, SPickItemFromBlock, SPickItemFromEntity, SPlayPingRequest, + SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, Status, }, }; use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace}; @@ -350,7 +350,10 @@ impl Player { ); self.client.send_packet(&dest_packet).await; - if inventory.set_slot(dest_slot + 36, Some(item_stack), false).is_err() { + if inventory + .set_slot(dest_slot + 36, Some(item_stack), false) + .is_err() + { log::error!("Pick item set slot error!"); return; } @@ -383,7 +386,10 @@ impl Player { ); self.client.send_packet(&packet).await; - if inventory.set_slot(dest_slot + 36, Some(item_stack), false).is_err() { + if inventory + .set_slot(dest_slot + 36, Some(item_stack), false) + .is_err() + { log::error!("Pick item set slot error!"); return; } @@ -398,6 +404,10 @@ impl Player { .await; } + pub fn handle_pick_item_from_entity(&self, _pick_item: SPickItemFromEntity) { + // TODO: Implement and merge any redundant code with pick_item_from_block + } + pub async fn handle_player_command(&self, command: SPlayerCommand) { if command.entity_id != self.entity_id().into() { return; From a3fa9c223faa5824e1c8fc45d26d6fcafad02f68 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 14:12:20 -0800 Subject: [PATCH 07/12] Added item swapping --- pumpkin-inventory/src/player.rs | 10 +++++++ pumpkin/src/net/packet/play.rs | 47 +++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 948f53b44..2c0862e7d 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -146,6 +146,16 @@ impl PlayerInventory { self.selected } + pub fn get_empty_slot(&self) -> Option { + for slot in 9..=44 { + if self.items[slot - 9].is_none() { + return Some(slot); + } + } + + None + } + pub fn slots(&self) -> Vec> { let mut slots = vec![self.crafting_output.as_ref()]; slots.extend(self.crafting.iter().map(|c| c.as_ref())); diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 7ce6fc125..fdb0823ce 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -323,6 +323,7 @@ impl Player { }; let mut inventory = self.inventory().lock().await; + let source_slot = inventory.get_slot_with_item(block.item_id); let mut dest_slot = inventory.get_pick_item_hotbar_slot(); @@ -333,48 +334,59 @@ impl Player { match source_slot { Some(slot_index) if !(slot_index > 36 && slot_index <= 44) => { - // Handle existing item case - let item_stack = match inventory.get_slot(slot_index) { - Ok(Some(stack)) => *stack, + // Case where item is in inventory + + // Save items + let source_slot_data = match inventory.get_slot(slot_index) { + Ok(Some(stack)) => Slot::from(&*stack), _ => return, }; + let dest_slot_data = match inventory.get_slot(dest_slot + 36) { + Ok(Some(stack)) => Slot::from(&*stack), + _ => Slot::from(None), + }; // Update destination slot - let slot_data = Slot::from(&item_stack); inventory.state_id += 1; let dest_packet = CSetContainerSlot::new( 0, inventory.state_id as i32, dest_slot + 36, - &slot_data, + &source_slot_data, ); self.client.send_packet(&dest_packet).await; if inventory - .set_slot(dest_slot + 36, Some(item_stack), false) + .set_slot(dest_slot + 36, source_slot_data.to_item(), false) .is_err() { log::error!("Pick item set slot error!"); return; } - // Clear source slot - let slot_data = Slot::from(None); + // Update source slot inventory.state_id += 1; let source_packet = - CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &slot_data); + CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &dest_slot_data); self.client.send_packet(&source_packet).await; - if inventory.set_slot(slot_index, None, false).is_err() { + if inventory.set_slot(slot_index, dest_slot_data.to_item(), false).is_err() { log::error!("Failed to clear source slot!"); return; } } Some(slot_index) => { + // Case where item is in hotbar dest_slot = slot_index - 36; } None if self.gamemode.load() == GameMode::Creative => { - // Handle creative mode case + // Save dest slot data + let dest_slot_data = match inventory.get_slot(dest_slot + 36) { + Ok(Some(stack)) => Slot::from(&*stack), + _ => Slot::from(None), + }; + + // Case where item is not present, if in creative mode create the item let item_stack = ItemStack::new(1, block.item_id); let slot_data = Slot::from(&item_stack); inventory.state_id += 1; @@ -393,6 +405,19 @@ impl Player { log::error!("Pick item set slot error!"); return; } + + // Check if there is any empty slot in the player inventory + if let Some(slot_index) = inventory.get_empty_slot() { + inventory.state_id += 1; + let source_packet = CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &dest_slot_data); + self.client.send_packet(&source_packet).await; + + if inventory.set_slot(slot_index, dest_slot_data.to_item(), false).is_err() { + log::error!("Failed to clear source slot!"); + return; + } + } + } _ => return, } From 69d60a323dbfc87eed35d118ac88bc7b9673f699 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 14:35:40 -0800 Subject: [PATCH 08/12] Swapping and cleanup --- pumpkin-inventory/src/player.rs | 8 +-- pumpkin/src/net/packet/play.rs | 97 ++++++++++++--------------------- 2 files changed, 35 insertions(+), 70 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 2c0862e7d..f6bccbb69 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -147,13 +147,7 @@ impl PlayerInventory { } pub fn get_empty_slot(&self) -> Option { - for slot in 9..=44 { - if self.items[slot - 9].is_none() { - return Some(slot); - } - } - - None + (9..=44).find(|&slot| self.items[slot - 9].is_none()) } pub fn slots(&self) -> Vec> { diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index fdb0823ce..d6480b947 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -18,6 +18,7 @@ use pumpkin_core::{ text::TextComponent, GameMode, }; +use pumpkin_inventory::player::PlayerInventory; use pumpkin_inventory::InventoryError; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem}; @@ -313,6 +314,24 @@ impl Player { .store(ground.on_ground, std::sync::atomic::Ordering::Relaxed); } + async fn update_single_slot( + &self, + inventory: &mut tokio::sync::MutexGuard<'_, PlayerInventory>, + slot: usize, + slot_data: Slot, + ) { + inventory.state_id += 1; + let dest_packet = CSetContainerSlot::new(0, inventory.state_id as i32, slot, &slot_data); + self.client.send_packet(&dest_packet).await; + + if inventory + .set_slot(slot, slot_data.to_item(), false) + .is_err() + { + log::error!("Pick item set slot error!"); + } + } + pub async fn handle_pick_item_from_block(&self, pick_item: SPickItemFromBlock) { if !self.can_interact_with_block_at(&pick_item.pos, 1.0) { return; @@ -327,6 +346,11 @@ impl Player { let source_slot = inventory.get_slot_with_item(block.item_id); let mut dest_slot = inventory.get_pick_item_hotbar_slot(); + let dest_slot_data = match inventory.get_slot(dest_slot + 36) { + Ok(Some(stack)) => Slot::from(&*stack), + _ => Slot::from(None), + }; + // Early return if no source slot and not in creative mode if source_slot.is_none() && self.gamemode.load() != GameMode::Creative { return; @@ -335,89 +359,36 @@ impl Player { match source_slot { Some(slot_index) if !(slot_index > 36 && slot_index <= 44) => { // Case where item is in inventory - - // Save items + + // Update destination slot let source_slot_data = match inventory.get_slot(slot_index) { Ok(Some(stack)) => Slot::from(&*stack), _ => return, }; - let dest_slot_data = match inventory.get_slot(dest_slot + 36) { - Ok(Some(stack)) => Slot::from(&*stack), - _ => Slot::from(None), - }; - - // Update destination slot - inventory.state_id += 1; - let dest_packet = CSetContainerSlot::new( - 0, - inventory.state_id as i32, - dest_slot + 36, - &source_slot_data, - ); - self.client.send_packet(&dest_packet).await; - - if inventory - .set_slot(dest_slot + 36, source_slot_data.to_item(), false) - .is_err() - { - log::error!("Pick item set slot error!"); - return; - } + self.update_single_slot(&mut inventory, dest_slot + 36, source_slot_data) + .await; // Update source slot - inventory.state_id += 1; - let source_packet = - CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &dest_slot_data); - self.client.send_packet(&source_packet).await; - - if inventory.set_slot(slot_index, dest_slot_data.to_item(), false).is_err() { - log::error!("Failed to clear source slot!"); - return; - } + self.update_single_slot(&mut inventory, slot_index, dest_slot_data) + .await; } Some(slot_index) => { // Case where item is in hotbar dest_slot = slot_index - 36; } None if self.gamemode.load() == GameMode::Creative => { - // Save dest slot data - let dest_slot_data = match inventory.get_slot(dest_slot + 36) { - Ok(Some(stack)) => Slot::from(&*stack), - _ => Slot::from(None), - }; - // Case where item is not present, if in creative mode create the item let item_stack = ItemStack::new(1, block.item_id); let slot_data = Slot::from(&item_stack); - inventory.state_id += 1; - let packet = CSetContainerSlot::new( - 0, - inventory.state_id as i32, - dest_slot + 36, - &slot_data, - ); - self.client.send_packet(&packet).await; - - if inventory - .set_slot(dest_slot + 36, Some(item_stack), false) - .is_err() - { - log::error!("Pick item set slot error!"); - return; - } + self.update_single_slot(&mut inventory, dest_slot + 36, slot_data) + .await; // Check if there is any empty slot in the player inventory if let Some(slot_index) = inventory.get_empty_slot() { inventory.state_id += 1; - let source_packet = CSetContainerSlot::new(0, inventory.state_id as i32, slot_index, &dest_slot_data); - self.client.send_packet(&source_packet).await; - - if inventory.set_slot(slot_index, dest_slot_data.to_item(), false).is_err() { - log::error!("Failed to clear source slot!"); - return; - } + self.update_single_slot(&mut inventory, slot_index, dest_slot_data) + .await; } - } _ => return, } From 98f887aebcc4eb678314464cef25bb603cfead33 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 14:49:45 -0800 Subject: [PATCH 09/12] Fix for rebase --- pumpkin/src/net/packet/play.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index d6480b947..618918f43 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -20,10 +20,10 @@ use pumpkin_core::{ }; use pumpkin_inventory::player::PlayerInventory; use pumpkin_inventory::InventoryError; -use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem}; +use pumpkin_protocol::codec::slot::Slot; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; -use pumpkin_protocol::slot::Slot; use pumpkin_protocol::{ client::play::CCommandSuggestions, server::play::{SCloseContainer, SCommandSuggestion, SKeepAlive, SSetPlayerGround, SUseItem}, From 98d0cc1a238de319e4ed007054b5ddd011a62fa0 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sat, 28 Dec 2024 20:18:26 -0800 Subject: [PATCH 10/12] Remove unused function --- pumpkin-inventory/src/player.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index f6bccbb69..f406ba5a6 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -116,11 +116,6 @@ impl PlayerInventory { &mut self.items[self.selected + 36 - 9] } - pub fn set_held_item(&mut self, item: Option) { - debug_assert!((0..9).contains(&self.selected)); - self.items[self.selected + 36 - 9] = item; - } - pub fn get_slot_with_item(&self, item_id: u16) -> Option { for slot in 9..=44 { match &self.items[slot - 9] { From e4f95ab937d2626f98467159800bd5f43b794651 Mon Sep 17 00:00:00 2001 From: Bafran Date: Sun, 29 Dec 2024 09:47:17 -0800 Subject: [PATCH 11/12] Bugfix --- pumpkin/src/net/packet/play.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 618918f43..d5caf9234 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -341,6 +341,11 @@ impl Player { return; }; + if block.item_id == 0 { + // Invalid block id (blocks such as tall seagrass) + return; + } + let mut inventory = self.inventory().lock().await; let source_slot = inventory.get_slot_with_item(block.item_id); @@ -357,7 +362,11 @@ impl Player { } match source_slot { - Some(slot_index) if !(slot_index > 36 && slot_index <= 44) => { + Some(slot_index) if (36..=44).contains(&slot_index) => { + // Case where item is in hotbar + dest_slot = slot_index - 36; + } + Some(slot_index) => { // Case where item is in inventory // Update destination slot @@ -372,10 +381,6 @@ impl Player { self.update_single_slot(&mut inventory, slot_index, dest_slot_data) .await; } - Some(slot_index) => { - // Case where item is in hotbar - dest_slot = slot_index - 36; - } None if self.gamemode.load() == GameMode::Creative => { // Case where item is not present, if in creative mode create the item let item_stack = ItemStack::new(1, block.item_id); From 43e8c45a8bbf29a1b27903ed08beb771922353fe Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sun, 29 Dec 2024 19:01:16 +0100 Subject: [PATCH 12/12] remove todo --- pumpkin-protocol/src/server/play/s_pick_item.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pumpkin-protocol/src/server/play/s_pick_item.rs b/pumpkin-protocol/src/server/play/s_pick_item.rs index 33d432a2f..8ff25b1d3 100644 --- a/pumpkin-protocol/src/server/play/s_pick_item.rs +++ b/pumpkin-protocol/src/server/play/s_pick_item.rs @@ -9,7 +9,6 @@ pub struct SPickItemFromBlock { pub include_data: bool, } -// TODO: Handler for this packet #[derive(Deserialize)] #[server_packet("play:pick_item_from_entity")] pub struct SPickItemFromEntity {