Calculate block breaking speed
Also broadcast current held item. The block progress is already correctly displayed and calculated for tools. But for some reason the destruction does not work, looking at vanilla code i have no idea what i'm doing wrong
This commit is contained in:
parent
94dc37a275
commit
5a77b21d22
27866
assets/blocks.json
27866
assets/blocks.json
File diff suppressed because it is too large
Load Diff
@ -115,6 +115,11 @@ impl PlayerInventory {
|
||||
self.items[self.selected as usize + 36 - 9].as_ref()
|
||||
}
|
||||
|
||||
pub async fn get_mining_speed(&self, block_name: &str) -> f32 {
|
||||
self.held_item()
|
||||
.map_or_else(|| 1.0, |e| e.get_speed(block_name))
|
||||
}
|
||||
|
||||
pub fn held_item_mut(&mut self) -> &mut Option<ItemStack> {
|
||||
debug_assert!((0..9).contains(&self.selected));
|
||||
&mut self.items[self.selected as usize + 36 - 9]
|
||||
|
||||
@ -12,7 +12,7 @@ use bytes::{Buf, BufMut};
|
||||
mod deserializer;
|
||||
use thiserror::Error;
|
||||
pub mod packet;
|
||||
mod serializer;
|
||||
pub mod serializer;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ReadingError {
|
||||
|
||||
@ -11,11 +11,11 @@ use crate::VarInt;
|
||||
pub struct CSetBlockDestroyStage {
|
||||
entity_id: VarInt,
|
||||
location: BlockPos,
|
||||
destroy_stage: u8,
|
||||
destroy_stage: i8,
|
||||
}
|
||||
|
||||
impl CSetBlockDestroyStage {
|
||||
pub fn new(entity_id: VarInt, location: BlockPos, destroy_stage: u8) -> Self {
|
||||
pub fn new(entity_id: VarInt, location: BlockPos, destroy_stage: i8) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
location,
|
||||
|
||||
@ -53,6 +53,7 @@ mod set_border_warning_distance;
|
||||
mod set_container_content;
|
||||
mod set_container_property;
|
||||
mod set_container_slot;
|
||||
mod set_equipment;
|
||||
mod set_experience;
|
||||
mod set_health;
|
||||
mod set_held_item;
|
||||
@ -129,6 +130,7 @@ pub use set_border_warning_distance::*;
|
||||
pub use set_container_content::*;
|
||||
pub use set_container_property::*;
|
||||
pub use set_container_slot::*;
|
||||
pub use set_equipment::*;
|
||||
pub use set_experience::*;
|
||||
pub use set_health::*;
|
||||
pub use set_held_item::*;
|
||||
|
||||
58
pumpkin-protocol/src/client/play/set_equipment.rs
Normal file
58
pumpkin-protocol/src/client/play/set_equipment.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::bytebuf::{serializer::Serializer, ByteBufMut};
|
||||
use bytes::BytesMut;
|
||||
use pumpkin_data::packet::clientbound::PLAY_SET_EQUIPMENT;
|
||||
use pumpkin_macros::client_packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
codec::{slot::Slot, var_int::VarInt},
|
||||
ClientPacket,
|
||||
};
|
||||
|
||||
#[client_packet(PLAY_SET_EQUIPMENT)]
|
||||
pub struct CSetEquipment {
|
||||
entity_id: VarInt,
|
||||
equipment: Vec<(EquipmentSlot, Slot)>,
|
||||
}
|
||||
|
||||
impl CSetEquipment {
|
||||
pub fn new(entity_id: VarInt, equipment: Vec<(EquipmentSlot, Slot)>) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
equipment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPacket for CSetEquipment {
|
||||
fn write(&self, bytebuf: &mut impl bytes::BufMut) {
|
||||
bytebuf.put_var_int(&self.entity_id);
|
||||
for i in 0..self.equipment.len() {
|
||||
let equipment = &self.equipment[i];
|
||||
let slot = &equipment.0;
|
||||
if i != self.equipment.len() - 1 {
|
||||
bytebuf.put_i8(-128);
|
||||
} else {
|
||||
bytebuf.put_i8(*slot as i8);
|
||||
}
|
||||
let buf = BytesMut::new();
|
||||
let mut serializer = Serializer::new(buf);
|
||||
equipment
|
||||
.1
|
||||
.serialize(&mut serializer)
|
||||
.expect("Could not serialize packet");
|
||||
bytebuf.put(serializer.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum EquipmentSlot {
|
||||
MainHand,
|
||||
OffHand,
|
||||
Feet,
|
||||
Legs,
|
||||
Chest,
|
||||
Head,
|
||||
Body,
|
||||
}
|
||||
@ -33,7 +33,7 @@ pub fn total_to_level_and_points(total_points: i32) -> (i32, i32) {
|
||||
/// Calculate progress (0.0 to 1.0) from points within a level
|
||||
pub fn progress_in_level(points: i32, level: i32) -> f32 {
|
||||
let max_points = points_in_level(level);
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
|
||||
let progress = (points as f32) / (max_points as f32);
|
||||
progress.clamp(0.0, 1.0)
|
||||
}
|
||||
|
||||
@ -135,6 +135,7 @@ pub struct State {
|
||||
pub luminance: u8,
|
||||
pub burnable: bool,
|
||||
pub tool_required: bool,
|
||||
pub hardness: f32,
|
||||
pub opacity: Option<u32>,
|
||||
pub replaceable: bool,
|
||||
pub collision_shapes: Vec<u16>,
|
||||
|
||||
@ -28,6 +28,19 @@ impl ItemStack {
|
||||
Self { item_count, item }
|
||||
}
|
||||
|
||||
pub fn get_speed(&self, block: &str) -> f32 {
|
||||
if let Some(tool) = self.item.components.tool {
|
||||
for rule in tool.rules {
|
||||
if rule.speed.is_none() || !rule.blocks.contains(&block) {
|
||||
continue;
|
||||
}
|
||||
return rule.speed.unwrap();
|
||||
}
|
||||
return tool.default_mining_speed.unwrap_or(1.0);
|
||||
}
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn is_correct_for_drops(&self, block: &str) -> bool {
|
||||
if let Some(tool) = self.item.components.tool {
|
||||
for rule in tool.rules {
|
||||
|
||||
@ -20,16 +20,16 @@ use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::block::registry::Block;
|
||||
use pumpkin_world::block::registry::{Block, State};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::block::blocks::crafting_table::CraftingTableBlock;
|
||||
use crate::block::blocks::jukebox::JukeboxBlock;
|
||||
use crate::block::registry::BlockRegistry;
|
||||
use crate::entity::item::ItemEntity;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
use crate::{block::blocks::crafting_table::CraftingTableBlock, entity::player::Player};
|
||||
use std::sync::Arc;
|
||||
|
||||
mod blocks;
|
||||
@ -68,6 +68,22 @@ pub async fn drop_loot(server: &Server, world: &Arc<World>, block: &Block, pos:
|
||||
item_entity.send_meta_packet().await;
|
||||
}
|
||||
|
||||
pub async fn calc_block_breaking(player: &Player, state: &State, block_name: &str) -> f32 {
|
||||
let hardness = state.hardness;
|
||||
#[expect(clippy::float_cmp)]
|
||||
if hardness == -1.0 {
|
||||
// unbreakable
|
||||
return 0.0;
|
||||
}
|
||||
let i = if player.can_harvest(state, block_name).await {
|
||||
30
|
||||
} else {
|
||||
100
|
||||
};
|
||||
|
||||
player.get_mining_speed(block_name).await / hardness / i as f32
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn default_block_properties_manager() -> Arc<BlockPropertiesManager> {
|
||||
let mut manager = BlockPropertiesManager::default();
|
||||
|
||||
@ -4,8 +4,14 @@ use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_data::{damage::DamageType, sound::Sound};
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_protocol::client::play::{CDamageEvent, CEntityStatus, MetaDataType, Metadata};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
CDamageEvent, CEntityStatus, CSetEquipment, EquipmentSlot, MetaDataType, Metadata,
|
||||
},
|
||||
codec::slot::Slot,
|
||||
};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
|
||||
use super::{Entity, EntityId, NBTStorage};
|
||||
|
||||
@ -49,6 +55,19 @@ impl LivingEntity {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_equipment_changes(&self, equipment: Vec<(EquipmentSlot, ItemStack)>) {
|
||||
let equipment: Vec<(EquipmentSlot, Slot)> = equipment
|
||||
.iter()
|
||||
.map(|(slot, stack)| (*slot, Slot::from(stack)))
|
||||
.collect();
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CSetEquipment::new(self.entity_id().into(), equipment))
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn set_pos(&self, position: Vector3<f64>) {
|
||||
self.last_pos.store(self.entity.pos.load());
|
||||
self.entity.set_pos(position);
|
||||
|
||||
@ -2,7 +2,7 @@ use pumpkin_world::block::registry::State;
|
||||
use std::{
|
||||
num::NonZeroU8,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32, AtomicU8, Ordering},
|
||||
atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
@ -23,10 +23,10 @@ use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::packet::Packet,
|
||||
client::play::{
|
||||
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
||||
CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
||||
CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage,
|
||||
CTitleText, CUnloadChunk, GameEvent, MetaDataType, PlayerAction,
|
||||
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus,
|
||||
CGameEvent, CHurtAnimation, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities,
|
||||
CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle,
|
||||
CSystemChatMessage, CTitleText, CUnloadChunk, GameEvent, MetaDataType, PlayerAction,
|
||||
},
|
||||
server::play::{
|
||||
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
||||
@ -70,6 +70,7 @@ use super::{
|
||||
Entity, EntityBase, EntityId, NBTStorage,
|
||||
};
|
||||
use crate::{
|
||||
block,
|
||||
command::{client_suggestions, dispatcher::CommandDispatcher},
|
||||
data::op_data::OPERATOR_CONFIG,
|
||||
net::{Client, PlayerConfig},
|
||||
@ -102,7 +103,6 @@ pub struct Player {
|
||||
pub open_container: AtomicCell<Option<u64>>,
|
||||
/// The item currently being held by the player.
|
||||
pub carried_item: AtomicCell<Option<ItemStack>>,
|
||||
|
||||
/// send `send_abilities_update` when changed
|
||||
/// The player's abilities and special powers.
|
||||
///
|
||||
@ -110,9 +110,14 @@ pub struct Player {
|
||||
///
|
||||
/// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes.
|
||||
pub abilities: Mutex<Abilities>,
|
||||
|
||||
/// The current stage of the block the player is breaking.
|
||||
pub current_block_destroy_stage: AtomicU8,
|
||||
pub current_block_destroy_stage: AtomicI32,
|
||||
/// Indicates if the player is currently mining a block.
|
||||
pub mining: AtomicBool,
|
||||
pub start_mining_time: AtomicI32,
|
||||
pub tick_counter: AtomicI32,
|
||||
pub packet_sequence: AtomicI32,
|
||||
pub mining_pos: Mutex<BlockPos>,
|
||||
/// A counter for teleport IDs used to track pending teleports.
|
||||
pub teleport_id_count: AtomicI32,
|
||||
/// The pending teleport information, including the teleport ID and target location.
|
||||
@ -189,10 +194,15 @@ impl Player {
|
||||
awaiting_teleport: Mutex::new(None),
|
||||
// TODO: Load this from previous instance
|
||||
hunger_manager: HungerManager::default(),
|
||||
current_block_destroy_stage: AtomicU8::new(0),
|
||||
current_block_destroy_stage: AtomicI32::new(-1),
|
||||
open_container: AtomicCell::new(None),
|
||||
tick_counter: AtomicI32::new(0),
|
||||
packet_sequence: AtomicI32::new(-1),
|
||||
start_mining_time: AtomicI32::new(0),
|
||||
carried_item: AtomicCell::new(None),
|
||||
teleport_id_count: AtomicI32::new(0),
|
||||
mining: AtomicBool::new(false),
|
||||
mining_pos: Mutex::new(BlockPos(Vector3::new(0, 0, 0))),
|
||||
abilities: Mutex::new(Abilities::default()),
|
||||
gamemode: AtomicCell::new(gamemode),
|
||||
// We want this to be an impossible watched section so that `player_chunker::update_position`
|
||||
@ -443,7 +453,39 @@ impl Player {
|
||||
{
|
||||
return;
|
||||
}
|
||||
let now = Instant::now();
|
||||
if self.packet_sequence.load(Ordering::Relaxed) > -1 {
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(
|
||||
self.packet_sequence.swap(-1, Ordering::Relaxed).into(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
self.tick_counter.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
if self.mining.load(Ordering::Relaxed) {
|
||||
let pos = self.mining_pos.lock().await;
|
||||
let world = self.world().await;
|
||||
let block = world.get_block(&pos).await.unwrap();
|
||||
let state = world.get_block_state(&pos).await.unwrap();
|
||||
// Is block broken ?
|
||||
if state.air {
|
||||
world.set_block_breaking(self.entity_id(), *pos, -1).await;
|
||||
self.current_block_destroy_stage
|
||||
.store(-1, Ordering::Relaxed);
|
||||
self.mining.store(false, Ordering::Relaxed);
|
||||
} else {
|
||||
self.continue_mining(
|
||||
*pos,
|
||||
&world,
|
||||
state,
|
||||
&block.name,
|
||||
self.start_mining_time.load(Ordering::Relaxed),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
self.last_attacked_ticks
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
@ -452,6 +494,8 @@ impl Player {
|
||||
|
||||
// timeout/keep alive handling
|
||||
self.tick_client_load_timeout();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_keep_alive_time.load()) >= Duration::from_secs(15) {
|
||||
// We never got a response from our last keep alive we send
|
||||
if self
|
||||
@ -472,6 +516,26 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
async fn continue_mining(
|
||||
&self,
|
||||
location: BlockPos,
|
||||
world: &World,
|
||||
state: &State,
|
||||
block_name: &str,
|
||||
starting_time: i32,
|
||||
) {
|
||||
let time = self.tick_counter.load(Ordering::Relaxed) - starting_time;
|
||||
let speed = block::calc_block_breaking(self, state, block_name).await * (time + 1) as f32;
|
||||
let progress = (speed * 10.0) as i32;
|
||||
if progress != self.current_block_destroy_stage.load(Ordering::Relaxed) {
|
||||
world
|
||||
.set_block_breaking(self.entity_id(), location, progress)
|
||||
.await;
|
||||
self.current_block_destroy_stage
|
||||
.store(progress, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn jump(&self) {
|
||||
if self.living_entity.entity.sprinting.load(Ordering::Relaxed) {
|
||||
self.add_exhaustion(0.2).await;
|
||||
@ -508,7 +572,6 @@ impl Player {
|
||||
}
|
||||
|
||||
pub fn get_attack_cooldown_progress(&self, base_time: f64, attack_speed: f64) -> f64 {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let x = f64::from(
|
||||
self.last_attacked_ticks
|
||||
.load(std::sync::atomic::Ordering::Acquire),
|
||||
@ -849,7 +912,6 @@ impl Player {
|
||||
))
|
||||
.await;
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
self.client
|
||||
.send_packet(&CGameEvent::new(
|
||||
GameEvent::ChangeGameMode,
|
||||
@ -885,6 +947,20 @@ impl Player {
|
||||
.map_or_else(|| false, |e| e.is_correct_for_drops(block_name))
|
||||
}
|
||||
|
||||
pub async fn get_mining_speed(&self, block_name: &str) -> f32 {
|
||||
let mut speed = self
|
||||
.inventory
|
||||
.lock()
|
||||
.await
|
||||
.get_mining_speed(block_name)
|
||||
.await;
|
||||
// TODO: Handle effects
|
||||
if !self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
|
||||
speed /= 5.0;
|
||||
}
|
||||
speed
|
||||
}
|
||||
|
||||
pub async fn send_message(
|
||||
&self,
|
||||
message: &TextComponent,
|
||||
@ -929,7 +1005,6 @@ impl Player {
|
||||
}
|
||||
|
||||
/// Sets the player's experience level and updates the client
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub async fn set_experience(&self, level: i32, progress: f32, points: i32) {
|
||||
self.experience_level.store(level, Ordering::Relaxed);
|
||||
self.experience_progress.store(progress.clamp(0.0, 1.0));
|
||||
@ -945,7 +1020,6 @@ impl Player {
|
||||
}
|
||||
|
||||
/// Sets the player's experience level directly
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub async fn set_experience_level(&self, new_level: i32, keep_progress: bool) {
|
||||
let progress = self.experience_progress.load();
|
||||
let mut points = self.experience_points.load(Ordering::Relaxed);
|
||||
@ -974,7 +1048,6 @@ impl Player {
|
||||
}
|
||||
|
||||
/// Set the player's experience points directly, Returns true if successful.
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub async fn set_experience_points(&self, new_points: i32) -> bool {
|
||||
let current_points = self.experience_points.load(Ordering::Relaxed);
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#![deny(clippy::print_stderr)]
|
||||
// REMOVE SOME WHEN RELEASE
|
||||
#![expect(clippy::cargo_common_metadata)]
|
||||
#![expect(clippy::cast_precision_loss)]
|
||||
#![expect(clippy::multiple_crate_versions)]
|
||||
#![expect(clippy::single_call_fn)]
|
||||
#![expect(clippy::cast_sign_loss)]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::num::NonZeroU8;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block;
|
||||
use crate::block::properties::Direction;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::entity::mob;
|
||||
@ -22,16 +23,15 @@ use pumpkin_inventory::player::PlayerInventory;
|
||||
use pumpkin_inventory::InventoryError;
|
||||
use pumpkin_macros::block_entity;
|
||||
use pumpkin_protocol::client::play::{
|
||||
CBlockEntityData, COpenSignEditor, CSetContainerSlot, CSetHeldItem,
|
||||
CBlockEntityData, COpenSignEditor, CSetContainerSlot, CSetHeldItem, EquipmentSlot,
|
||||
};
|
||||
use pumpkin_protocol::codec::slot::Slot;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::server::play::{SCookieResponse as SPCookieResponse, SUpdateSign};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
Animation, CAcknowledgeBlockChange, CCommandSuggestions, CEntityAnimation, CHeadRot,
|
||||
CPingResponse, CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot,
|
||||
FilterType,
|
||||
Animation, CCommandSuggestions, CEntityAnimation, CHeadRot, CPingResponse,
|
||||
CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, FilterType,
|
||||
},
|
||||
server::play::{
|
||||
Action, ActionType, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay,
|
||||
@ -481,6 +481,10 @@ impl Player {
|
||||
|
||||
// Update held item
|
||||
inventory.set_selected(dest_slot as u32);
|
||||
let empty = &ItemStack::new(0, Item::AIR);
|
||||
let stack = inventory.held_item().unwrap_or(empty);
|
||||
let equipment = vec![(EquipmentSlot::MainHand, *stack)];
|
||||
self.living_entity.send_equipment_changes(equipment).await;
|
||||
self.client
|
||||
.send_packet(&CSetHeldItem::new(dest_slot as i8))
|
||||
.await;
|
||||
@ -801,6 +805,7 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub async fn handle_player_action(
|
||||
self: Arc<Self>,
|
||||
player_action: SPlayerAction,
|
||||
@ -820,14 +825,16 @@ impl Player {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// TODO: do validation
|
||||
// TODO: Config
|
||||
if self.gamemode.load() == GameMode::Creative {
|
||||
let location = player_action.location;
|
||||
// Block break & block break sound
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
let block = world.get_block(&location).await;
|
||||
let state = world.get_block_state(&location).await.unwrap();
|
||||
|
||||
// TODO: do validation
|
||||
// TODO: Config
|
||||
if self.gamemode.load() == GameMode::Creative {
|
||||
// Block break & block break sound
|
||||
|
||||
world
|
||||
.break_block(server, &location, Some(self.clone()), false)
|
||||
@ -838,10 +845,38 @@ impl Player {
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
}
|
||||
return;
|
||||
}
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence))
|
||||
self.start_mining_time.store(
|
||||
self.tick_counter.load(std::sync::atomic::Ordering::Relaxed),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
if let Ok(block) = block {
|
||||
if !state.air {
|
||||
let speed = block::calc_block_breaking(&self, state, &block.name).await;
|
||||
// Instant break
|
||||
if speed >= 1.0 {
|
||||
world
|
||||
.break_block(server, &location, Some(self.clone()), true)
|
||||
.await;
|
||||
server
|
||||
.block_registry
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
} else {
|
||||
self.mining
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
*self.mining_pos.lock().await = location;
|
||||
let progress = (speed * 10.0) as i32;
|
||||
world
|
||||
.set_block_breaking(self.entity_id(), location, progress)
|
||||
.await;
|
||||
self.current_block_destroy_stage
|
||||
.store(progress, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.update_sequence(player_action.sequence.0);
|
||||
}
|
||||
Status::CancelledDigging => {
|
||||
if !self.can_interact_with_block_at(&player_action.location, 1.0) {
|
||||
@ -852,13 +887,17 @@ impl Player {
|
||||
);
|
||||
return;
|
||||
}
|
||||
self.current_block_destroy_stage
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence))
|
||||
self.mining
|
||||
.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
world
|
||||
.set_block_breaking(self.entity_id(), player_action.location, -1)
|
||||
.await;
|
||||
self.update_sequence(player_action.sequence.0);
|
||||
}
|
||||
Status::FinishedDigging => {
|
||||
dbg!("fuff");
|
||||
// TODO: do validation
|
||||
let location = player_action.location;
|
||||
if !self.can_interact_with_block_at(&location, 1.0) {
|
||||
@ -872,6 +911,11 @@ impl Player {
|
||||
// Block break & block break sound
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
self.mining
|
||||
.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
world
|
||||
.set_block_breaking(self.entity_id(), location, -1)
|
||||
.await;
|
||||
let block = world.get_block(&location).await;
|
||||
let state = world.get_block_state(&location).await;
|
||||
if let Ok(block) = block {
|
||||
@ -887,9 +931,7 @@ impl Player {
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
}
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence))
|
||||
.await;
|
||||
self.update_sequence(player_action.sequence.0);
|
||||
}
|
||||
Status::DropItemStack | Status::DropItem => {
|
||||
self.drop_item(server).await;
|
||||
@ -918,6 +960,18 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_sequence(&self, sequence: i32) {
|
||||
if sequence < 0 {
|
||||
log::error!("Expected packet sequence >= 0");
|
||||
}
|
||||
self.packet_sequence.store(
|
||||
self.packet_sequence
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
.max(sequence),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn handle_player_abilities(&self, player_abilities: SPlayerAbilities) {
|
||||
let mut abilities = self.abilities.lock().await;
|
||||
|
||||
@ -943,6 +997,7 @@ impl Player {
|
||||
if !self.has_client_loaded() {
|
||||
return Ok(());
|
||||
}
|
||||
self.update_sequence(use_item_on.sequence.0);
|
||||
|
||||
let location = use_item_on.location;
|
||||
let mut should_try_decrement = false;
|
||||
@ -986,9 +1041,6 @@ impl Player {
|
||||
.on_interact(block, block_state, &ItemStack::new(0, Item::AIR))
|
||||
.await;
|
||||
world.set_block_state(&location, new_state).await;
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||
.await;
|
||||
}
|
||||
return Ok(());
|
||||
};
|
||||
@ -1008,9 +1060,6 @@ impl Player {
|
||||
.on_interact(block, block_state, &stack)
|
||||
.await;
|
||||
world.set_block_state(&location, new_state).await;
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence.clone()))
|
||||
.await;
|
||||
match action_result {
|
||||
BlockActionResult::Continue => {}
|
||||
BlockActionResult::Consume => {
|
||||
@ -1093,7 +1142,12 @@ impl Player {
|
||||
self.kick(TextComponent::text("Invalid held slot")).await;
|
||||
return;
|
||||
}
|
||||
self.inventory().lock().await.set_selected(slot as u32);
|
||||
let mut inv = self.inventory().lock().await;
|
||||
inv.set_selected(slot as u32);
|
||||
let empty = &ItemStack::new(0, Item::AIR);
|
||||
let stack = inv.held_item().unwrap_or(empty);
|
||||
let equipment = vec![(EquipmentSlot::MainHand, *stack)];
|
||||
self.living_entity.send_equipment_changes(equipment).await;
|
||||
}
|
||||
|
||||
pub async fn handle_set_creative_slot(
|
||||
@ -1253,9 +1307,6 @@ impl Player {
|
||||
|
||||
// check block under the world
|
||||
if location.0.y + face.to_offset().y < WORLD_LOWEST_Y.into() {
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||
.await;
|
||||
return Err(BlockPlacingError::BlockOutOfWorld.into());
|
||||
}
|
||||
|
||||
@ -1270,17 +1321,11 @@ impl Player {
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||
.await;
|
||||
return Err(BlockPlacingError::BlockOutOfWorld.into());
|
||||
}
|
||||
|
||||
match self.gamemode.load() {
|
||||
GameMode::Spectator | GameMode::Adventure => {
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||
.await;
|
||||
return Err(BlockPlacingError::InvalidGamemode.into());
|
||||
}
|
||||
_ => {}
|
||||
@ -1356,10 +1401,6 @@ impl Player {
|
||||
.on_placed(&block, self, final_block_pos, server)
|
||||
.await;
|
||||
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||
.await;
|
||||
|
||||
self.send_sign_packet(block, final_block_pos, face).await;
|
||||
// Block was placed successfully, decrement inventory
|
||||
return Ok(true);
|
||||
|
||||
@ -28,7 +28,9 @@ use pumpkin_data::{
|
||||
world::WorldEvent,
|
||||
};
|
||||
use pumpkin_macros::send_cancellable;
|
||||
use pumpkin_protocol::client::play::{CBlockUpdate, CDisguisedChatMessage, CRespawn, CWorldEvent};
|
||||
use pumpkin_protocol::client::play::{
|
||||
CBlockUpdate, CDisguisedChatMessage, CRespawn, CSetBlockDestroyStage, CWorldEvent,
|
||||
};
|
||||
use pumpkin_protocol::{client::play::CLevelEvent, codec::identifier::Identifier};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
@ -985,6 +987,15 @@ impl World {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn set_block_breaking(&self, entity_id: EntityId, location: BlockPos, progress: i32) {
|
||||
self.broadcast_packet_all(&CSetBlockDestroyStage::new(
|
||||
entity_id.into(),
|
||||
location,
|
||||
progress as i8,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Sets a block
|
||||
pub async fn set_block_state(&self, position: &BlockPos, block_state_id: u16) -> u16 {
|
||||
let (chunk_coordinate, relative_coordinates) = position.chunk_and_chunk_relative_position();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user