diff --git a/pumpkin-protocol/src/bedrock/server/mod.rs b/pumpkin-protocol/src/bedrock/server/mod.rs index a50a94549..9909732a4 100644 --- a/pumpkin-protocol/src/bedrock/server/mod.rs +++ b/pumpkin-protocol/src/bedrock/server/mod.rs @@ -4,6 +4,7 @@ pub mod container_close; pub mod interaction; pub mod loading_screen; pub mod login; +pub mod player_action; pub mod player_auth_input; pub mod raknet; pub mod request_chunk_radius; diff --git a/pumpkin-protocol/src/bedrock/server/player_action.rs b/pumpkin-protocol/src/bedrock/server/player_action.rs new file mode 100644 index 000000000..92fc0c85b --- /dev/null +++ b/pumpkin-protocol/src/bedrock/server/player_action.rs @@ -0,0 +1,104 @@ +use std::io::{Error, Read}; + +use pumpkin_macros::packet; +use pumpkin_util::math::position::BlockPos; + +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(Debug, PacketRead)] +#[packet(36)] +pub struct SPlayerAction { + pub runtime_id: VarInt, + pub action: Action, + pub block_pos: BlockPos, + pub result_pos: BlockPos, + pub face: VarInt, +} + +#[derive(Debug)] +#[repr(i32)] +pub enum Action { + StartBreak = 0, + AbortBreak = 1, + StopBreak = 2, + GetUpdatedBlock = 3, + DropItem = 4, + StartSleeping = 5, + StopSleeping = 6, + Respawn = 7, + Jump = 8, + StartSprint = 9, + StopSprint = 10, + StartSneak = 11, + StopSneak = 12, + CreativePlayerDestroyBlock = 13, + DimensionChangeAck = 14, + StartGlide = 15, + StopGlide = 16, + BuildDenied = 17, + CrackBreak = 18, + ChangeSkin = 19, + SetEnchantmentSeed = 20, + Swimming = 21, + StopSwimming = 22, + StartSpinAttack = 23, + StopSpinAttack = 24, + InteractBlock = 25, + PredictDestroyBlock = 26, + ContinueDestroyBlock = 27, + StartItemUseOn = 28, + StopItemUseOn = 29, + HandledTeleport = 30, + MissedSwing = 31, + StartCrawling = 32, + StopCrawling = 33, + StartFlying = 34, + StopFlying = 35, + ClientAckServerData = 36, +} + +impl PacketRead for Action { + fn read(reader: &mut R) -> Result { + let action = VarInt::read(reader)?; + match action.0 { + 0 => Ok(Self::StartBreak), + 1 => Ok(Self::AbortBreak), + 2 => Ok(Self::StopBreak), + 3 => Ok(Self::GetUpdatedBlock), + 4 => Ok(Self::DropItem), + 5 => Ok(Self::StartSleeping), + 6 => Ok(Self::StopSleeping), + 7 => Ok(Self::Respawn), + 8 => Ok(Self::Jump), + 9 => Ok(Self::StartSprint), + 10 => Ok(Self::StopSprint), + 11 => Ok(Self::StartSneak), + 12 => Ok(Self::StopSneak), + 13 => Ok(Self::CreativePlayerDestroyBlock), + 14 => Ok(Self::DimensionChangeAck), + 15 => Ok(Self::StartGlide), + 16 => Ok(Self::StopGlide), + 17 => Ok(Self::BuildDenied), + 18 => Ok(Self::CrackBreak), + 19 => Ok(Self::ChangeSkin), + 20 => Ok(Self::SetEnchantmentSeed), + 21 => Ok(Self::Swimming), + 22 => Ok(Self::StopSwimming), + 23 => Ok(Self::StartSpinAttack), + 24 => Ok(Self::StopSpinAttack), + 25 => Ok(Self::InteractBlock), + 26 => Ok(Self::PredictDestroyBlock), + 27 => Ok(Self::ContinueDestroyBlock), + 28 => Ok(Self::StartItemUseOn), + 29 => Ok(Self::StopItemUseOn), + 30 => Ok(Self::HandledTeleport), + 31 => Ok(Self::MissedSwing), + 32 => Ok(Self::StartCrawling), + 33 => Ok(Self::StopCrawling), + 34 => Ok(Self::StartFlying), + 35 => Ok(Self::StopFlying), + 36 => Ok(Self::ClientAckServerData), + _ => Err(Error::other(format!("Invalid action ID: {}", action.0))), + } + } +} diff --git a/pumpkin-protocol/src/serial/deserializer.rs b/pumpkin-protocol/src/serial/deserializer.rs index f4169da8d..01e584e80 100644 --- a/pumpkin-protocol/src/serial/deserializer.rs +++ b/pumpkin-protocol/src/serial/deserializer.rs @@ -3,10 +3,13 @@ use std::{ net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, }; -use pumpkin_util::math::{vector2::Vector2, vector3::Vector3}; +use pumpkin_util::math::{position::BlockPos, vector2::Vector2, vector3::Vector3}; use uuid::Uuid; -use crate::{codec::var_uint::VarUInt, serial::PacketRead}; +use crate::{ + codec::{var_int::VarInt, var_uint::VarUInt}, + serial::PacketRead, +}; impl PacketRead for bool { fn read(reader: &mut R) -> Result { @@ -187,6 +190,16 @@ impl PacketRead for Vector2 { } } +impl PacketRead for BlockPos { + fn read(reader: &mut R) -> Result { + Ok(Self(Vector3 { + x: VarInt::read(reader)?.0, + y: VarInt::read(reader)?.0, + z: VarInt::read(reader)?.0, + })) + } +} + impl PacketRead for SocketAddr { fn read(reader: &mut R) -> Result { match u8::read(reader)? { diff --git a/pumpkin/src/net/bedrock/mod.rs b/pumpkin/src/net/bedrock/mod.rs index 2cff88d91..c84a39a73 100644 --- a/pumpkin/src/net/bedrock/mod.rs +++ b/pumpkin/src/net/bedrock/mod.rs @@ -28,6 +28,7 @@ use pumpkin_protocol::{ interaction::SInteraction, loading_screen::SLoadingScreen, login::SLogin, + player_action::SPlayerAction, player_auth_input::SPlayerAuthInput, raknet::{ connection::{ @@ -586,6 +587,10 @@ impl BedrockClient { self.handle_chat_command(player, server, SCommandRequest::read(reader)?) .await; } + SPlayerAction::PACKET_ID => { + self.handle_player_action(player, server, SPlayerAction::read(reader)?) + .await; + } _ => { warn!("Bedrock: Received Unknown Game packet: {}", packet.id); } diff --git a/pumpkin/src/net/bedrock/play.rs b/pumpkin/src/net/bedrock/play.rs index d028bf5b5..490665019 100644 --- a/pumpkin/src/net/bedrock/play.rs +++ b/pumpkin/src/net/bedrock/play.rs @@ -11,6 +11,7 @@ use pumpkin_protocol::{ command_request::SCommandRequest, container_close::SContainerClose, interaction::{Action, SInteraction}, + player_action::{Action as PlayerAction, SPlayerAction}, player_auth_input::{InputData, SPlayerAuthInput}, request_chunk_radius::SRequestChunkRadius, text::SText, @@ -19,7 +20,9 @@ use pumpkin_protocol::{ codec::{var_int::VarInt, var_long::VarLong}, java::client::play::CSystemChatMessage, }; -use pumpkin_util::{math::position::BlockPos, text::TextComponent}; +use pumpkin_util::{GameMode, math::position::BlockPos, text::TextComponent}; + +use pumpkin_world::world::BlockFlags; use crate::{ entity::{EntityBase, player::Player}, @@ -210,6 +213,69 @@ impl BedrockClient { }} } + pub async fn handle_player_action( + &self, + player: &Arc, + server: &Server, + packet: SPlayerAction, + ) { + if !player.has_client_loaded() { + return; + } + + match packet.action { + PlayerAction::StartBreak | PlayerAction::CreativePlayerDestroyBlock => { + let location = packet.block_pos; + if !player.can_interact_with_block_at(&location, 1.0) { + return; + } + + let entity = &player.living_entity.entity; + let world = entity.world.load_full(); + let (block, state) = world.get_block_and_state(&location).await; + + if player.gamemode.load() == GameMode::Creative { + let new_state = world + .break_block( + &location, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS | BlockFlags::SKIP_DROPS, + ) + .await; + if new_state.is_some() { + server + .block_registry + .broken(&world, block, player, &location, server, state) + .await; + } + } else if !state.is_air() { + let speed = crate::block::calc_block_breaking(player, state, block).await; + if speed >= 1.0 { + let broken_state = world.get_block_state(&location).await; + let new_state = world + .break_block( + &location, + Some(player.clone()), + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + if new_state.is_some() { + server + .block_registry + .broken(&world, block, player, &location, server, broken_state) + .await; + player.apply_tool_damage_for_block_break(broken_state).await; + } + } else { + // TODO: Survival progressive breaking + } + } + } + // TODO + _ => {} + } + } + pub async fn handle_chat_command( &self, player: &Arc,