feat(bedrock): add block breaking

This commit is contained in:
Alexander Medvedev 2026-04-30 22:57:52 +02:00
parent e035a5fb58
commit 4616289fbe
5 changed files with 192 additions and 3 deletions

View File

@ -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;

View File

@ -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<R: Read>(reader: &mut R) -> Result<Self, Error> {
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))),
}
}
}

View File

@ -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<R: Read>(reader: &mut R) -> Result<Self, Error> {
@ -187,6 +190,16 @@ impl<T: PacketRead> PacketRead for Vector2<T> {
}
}
impl PacketRead for BlockPos {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
Ok(Self(Vector3 {
x: VarInt::read(reader)?.0,
y: VarInt::read(reader)?.0,
z: VarInt::read(reader)?.0,
}))
}
}
impl PacketRead for SocketAddr {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
match u8::read(reader)? {

View File

@ -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);
}

View File

@ -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<Player>,
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<Player>,