feat(bedrock): add item drops
This commit is contained in:
parent
3a5f2d0855
commit
e47695d810
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -219,9 +219,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.12.1"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@ -490,7 +490,7 @@ version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1170,7 +1170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2199,7 +2199,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3118,7 +3118,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3441,7 +3441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3560,7 +3560,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4614,7 +4614,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -119,7 +119,7 @@ proc-macro-error2 = { version = "2", default-features = false }
|
||||
aes = { version = "0.9", default-features = false }
|
||||
async-compression = { version = "0.4.42", default-features = false }
|
||||
base64 = { version = "0.22.1", default-features = false, features = ["std"] }
|
||||
bitflags = { version = "2.12.1", default-features = false, features = ["std"] }
|
||||
bitflags = { version = "2.13.0", default-features = false, features = ["std"] }
|
||||
cesu8 = { version = "1.1", default-features = false }
|
||||
cfb8 = { version = "0.9", default-features = false }
|
||||
colored = { version = "3.1", default-features = false }
|
||||
|
||||
34
pumpkin-protocol/src/bedrock/client/add_item_actor.rs
Normal file
34
pumpkin-protocol/src/bedrock/client/add_item_actor.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::{
|
||||
codec::{var_long::VarLong, var_ulong::VarULong},
|
||||
serial::PacketWrite,
|
||||
};
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use std::io::{Error, Write};
|
||||
|
||||
use super::set_actor_data::EntityMetadata;
|
||||
use crate::bedrock::network_item::NetworkItemStackDescriptor;
|
||||
|
||||
#[packet(15)]
|
||||
pub struct CAddItemActor {
|
||||
pub entity_unique_id: VarLong,
|
||||
pub entity_runtime_id: VarULong,
|
||||
pub item: NetworkItemStackDescriptor,
|
||||
pub position: Vector3<f32>,
|
||||
pub velocity: Vector3<f32>,
|
||||
pub metadata: EntityMetadata,
|
||||
pub from_fishing: bool,
|
||||
}
|
||||
|
||||
impl PacketWrite for CAddItemActor {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
self.entity_unique_id.write(writer)?;
|
||||
self.entity_runtime_id.write(writer)?;
|
||||
self.item.write(writer)?;
|
||||
self.position.write(writer)?;
|
||||
self.velocity.write(writer)?;
|
||||
self.metadata.write(writer)?;
|
||||
self.from_fishing.write(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod add_actor;
|
||||
pub mod add_item_actor;
|
||||
pub mod add_player;
|
||||
pub mod available_commands;
|
||||
pub mod boss_event;
|
||||
@ -42,6 +43,7 @@ pub mod update_attributes;
|
||||
pub mod update_block;
|
||||
|
||||
pub use add_actor::*;
|
||||
pub use add_item_actor::*;
|
||||
pub use add_player::*;
|
||||
pub use available_commands::*;
|
||||
pub use boss_event::*;
|
||||
|
||||
@ -79,6 +79,12 @@ impl NetworkItemDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ItemStack> for NetworkItemDescriptor {
|
||||
fn from(stack: &ItemStack) -> Self {
|
||||
NetworkItemStackDescriptor::from(stack).item
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct NetworkItemStackDescriptor {
|
||||
// I hate mojang
|
||||
|
||||
@ -522,6 +522,21 @@ impl<T> From<Vector3<T>> for (T, T, T) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Vector3<f64> {
|
||||
/// Converts this vector to a `Vector3<f32>` (lossy).
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `Vector3<f32>` with each component cast to `f32`.
|
||||
#[must_use]
|
||||
pub const fn to_f32_lossy(&self) -> Vector3<f32> {
|
||||
Vector3 {
|
||||
x: self.x as f32,
|
||||
y: self.y as f32,
|
||||
z: self.z as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + Copy + Into<f64>> Vector3<T> {
|
||||
/// Converts this vector to a `Vector3<f64>`.
|
||||
///
|
||||
|
||||
@ -4,9 +4,12 @@ use pumpkin_data::data_component_impl::DamageResistantImpl;
|
||||
use pumpkin_data::data_component_impl::DamageResistantType;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::{damage::DamageType, meta_data_type::MetaDataType, tracked_data::TrackedData};
|
||||
use pumpkin_protocol::{
|
||||
codec::item_stack_seralizer::ItemStackSerializer, java::client::play::Metadata,
|
||||
};
|
||||
use pumpkin_protocol::bedrock::client::CAddItemActor;
|
||||
use pumpkin_protocol::bedrock::network_item::NetworkItemStackDescriptor;
|
||||
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
||||
use pumpkin_protocol::codec::var_long::VarLong;
|
||||
use pumpkin_protocol::codec::var_ulong::VarULong;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
use pumpkin_util::math::atomic_f32::AtomicF32;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Relaxed};
|
||||
@ -503,4 +506,25 @@ impl EntityBase for ItemEntity {
|
||||
fn cast_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn send_bedrock_spawn_packet<'a>(
|
||||
&'a self,
|
||||
client: &'a crate::net::bedrock::BedrockClient,
|
||||
) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = &self.entity;
|
||||
let runtime_id = entity.entity_id as u64;
|
||||
let item_stack = self.item_stack.lock().await;
|
||||
let packet = CAddItemActor {
|
||||
entity_unique_id: VarLong(runtime_id as i64),
|
||||
entity_runtime_id: VarULong(runtime_id),
|
||||
item: NetworkItemStackDescriptor::from(&*item_stack),
|
||||
position: entity.pos.load().to_f32_lossy(),
|
||||
velocity: entity.velocity.load().to_f32_lossy(),
|
||||
metadata: entity.bedrock_metadata(),
|
||||
from_fishing: false,
|
||||
};
|
||||
client.send_game_packet(&packet).await;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::entity::item::ItemEntity;
|
||||
use crate::net::ClientPlatform;
|
||||
use crate::{
|
||||
entity::item::ItemEntity,
|
||||
net::{ClientPlatform, bedrock::BedrockClient},
|
||||
server::Server,
|
||||
world::{
|
||||
World,
|
||||
@ -32,7 +32,8 @@ use pumpkin_data::{
|
||||
sound::{Sound, SoundCategory},
|
||||
};
|
||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||
use pumpkin_protocol::bedrock::client::CSetActorMotion;
|
||||
use pumpkin_protocol::bedrock::client::{CAddActor, CSetActorMotion};
|
||||
use pumpkin_protocol::codec::var_long::VarLong;
|
||||
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
||||
use pumpkin_protocol::{
|
||||
PositionFlag,
|
||||
@ -248,6 +249,35 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
||||
None
|
||||
}
|
||||
|
||||
fn send_bedrock_spawn_packet<'a>(
|
||||
&'a self,
|
||||
client: &'a BedrockClient,
|
||||
) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let runtime_id = entity.entity_id as u64;
|
||||
let packet = CAddActor::new(
|
||||
VarLong(runtime_id as i64),
|
||||
VarULong(runtime_id),
|
||||
self.get_entity().entity_type.resource_name.to_string(),
|
||||
entity.pos.load().to_f32_lossy(),
|
||||
entity.velocity.load().to_f32_lossy(),
|
||||
entity.pitch.load(),
|
||||
entity.yaw.load(),
|
||||
entity.head_yaw.load(),
|
||||
entity.body_yaw.load(),
|
||||
Vec::new(),
|
||||
entity.bedrock_metadata(),
|
||||
PropertySyncData {
|
||||
int_properties: std::collections::HashMap::new(),
|
||||
float_properties: std::collections::HashMap::new(),
|
||||
},
|
||||
Vec::new(),
|
||||
);
|
||||
client.send_game_packet(&packet).await;
|
||||
})
|
||||
}
|
||||
|
||||
fn damage_with_context<'a>(
|
||||
&'a self,
|
||||
caller: &'a dyn EntityBase,
|
||||
|
||||
@ -662,6 +662,13 @@ impl BedrockClient {
|
||||
self.close_token.is_cancelled()
|
||||
}
|
||||
|
||||
pub fn enqueue_spawn_packet(self: &Arc<Self>, entity: Arc<dyn crate::entity::EntityBase>) {
|
||||
let client = self.clone();
|
||||
self.spawn_task(async move {
|
||||
entity.send_bedrock_spawn_packet(&client).await;
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn send_acknowledgement(&self, ack: &Acknowledge, id: u8) -> Result<(), Error> {
|
||||
let mut packet_buf = Vec::new();
|
||||
ack.write(&mut packet_buf, id)?;
|
||||
|
||||
@ -497,6 +497,7 @@ impl BedrockClient {
|
||||
}
|
||||
|
||||
#[expect(clippy::match_same_arms)]
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub async fn handle_player_action(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
@ -509,7 +510,9 @@ impl BedrockClient {
|
||||
player.update_last_action_time();
|
||||
|
||||
match packet.action {
|
||||
PlayerAction::StartBreak | PlayerAction::CreativePlayerDestroyBlock => {
|
||||
PlayerAction::StartBreak
|
||||
| PlayerAction::CreativePlayerDestroyBlock
|
||||
| PlayerAction::ContinueDestroyBlock => {
|
||||
let location = packet.block_pos;
|
||||
if !player.can_interact_with_block_at(&location, 1.0) {
|
||||
return;
|
||||
@ -534,9 +537,6 @@ impl BedrockClient {
|
||||
.await;
|
||||
}
|
||||
} else if !state.is_air() {
|
||||
// Broadcast that breaking started
|
||||
world.set_block_breaking(entity, location, 0).await;
|
||||
|
||||
let speed = crate::block::calc_block_breaking(player, state, block).await;
|
||||
if speed >= 1.0 {
|
||||
let broken_state = world.get_block_state(&location);
|
||||
@ -565,11 +565,47 @@ impl BedrockClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
PlayerAction::PredictDestroyBlock | PlayerAction::StopBreak => {
|
||||
let location = packet.block_pos;
|
||||
if !player.can_interact_with_block_at(&location, 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let entity = &player.get_entity();
|
||||
let world = entity.world.load_full();
|
||||
|
||||
player.mining.store(false, Ordering::Relaxed);
|
||||
world.set_block_breaking(entity, location, -1).await;
|
||||
|
||||
let (block, state) = world.get_block_and_state(&location);
|
||||
if player.gamemode.load() != GameMode::Creative {
|
||||
let block_drop = player.can_harvest(state, block).await;
|
||||
|
||||
let new_state = world
|
||||
.break_block(
|
||||
&location,
|
||||
Some(player.clone()),
|
||||
if block_drop {
|
||||
BlockFlags::NOTIFY_NEIGHBORS
|
||||
} else {
|
||||
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
|
||||
},
|
||||
)
|
||||
.await;
|
||||
if new_state.is_some() {
|
||||
server
|
||||
.block_registry
|
||||
.broken(&world, block, player, &location, server, state)
|
||||
.await;
|
||||
player.apply_tool_damage_for_block_break(state).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
PlayerAction::CrackBreak => {
|
||||
// Don't do anything for this action. It is no longer used. Block
|
||||
// cracking is done fully server-side.
|
||||
}
|
||||
PlayerAction::AbortBreak | PlayerAction::StopBreak => {
|
||||
PlayerAction::AbortBreak => {
|
||||
let location = packet.block_pos;
|
||||
let entity = &player.get_entity();
|
||||
let world = entity.world.load();
|
||||
|
||||
@ -242,6 +242,13 @@ impl ClientPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_enqueue_spawn_packet(&self, entity: &Arc<dyn crate::entity::EntityBase>) {
|
||||
match self {
|
||||
Self::Java(java) => java.try_enqueue_packet(&entity.get_entity().create_spawn_packet()),
|
||||
Self::Bedrock(bedrock) => bedrock.enqueue_spawn_packet(entity.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_chunks(&self, chunks: &[SyncChunk]) {
|
||||
match self {
|
||||
Self::Java(java) => java.send_chunks(chunks).await,
|
||||
|
||||
@ -3722,9 +3722,8 @@ impl World {
|
||||
}
|
||||
|
||||
pub fn spawn_entity_non_save(&self, entity: &Arc<dyn EntityBase>) {
|
||||
let base_entity = entity.get_entity();
|
||||
let chunk_pos = base_entity.chunk_pos.load();
|
||||
self.broadcast_to_chunk(chunk_pos, &base_entity.create_spawn_packet());
|
||||
let _base_entity = entity.get_entity();
|
||||
self.broadcast_entity_spawn(entity);
|
||||
self.spawn_state.load().add_entity(self, entity.as_ref());
|
||||
|
||||
self.entities.rcu(|current_entities| {
|
||||
@ -3735,13 +3734,26 @@ impl World {
|
||||
}
|
||||
|
||||
pub async fn spawn_entity(&self, entity: Arc<dyn EntityBase>) {
|
||||
let base_entity = entity.get_entity();
|
||||
let chunk_pos = base_entity.chunk_pos.load();
|
||||
self.broadcast_to_chunk(chunk_pos, &base_entity.create_spawn_packet());
|
||||
self.broadcast_entity_spawn(&entity);
|
||||
entity.init_data_tracker().await;
|
||||
self.add_entity_silent(entity).await;
|
||||
}
|
||||
|
||||
pub fn broadcast_entity_spawn(&self, entity: &Arc<dyn EntityBase>) {
|
||||
let base_entity = entity.get_entity();
|
||||
let chunk_pos = base_entity.chunk_pos.load();
|
||||
|
||||
let players = self.players.load();
|
||||
for player in players.iter() {
|
||||
let center = player.get_entity().chunk_pos.load();
|
||||
let view_distance = get_view_distance(player).get() as i32;
|
||||
|
||||
if is_within_view_distance(chunk_pos, center, view_distance) {
|
||||
player.client.try_enqueue_spawn_packet(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_entity_silent(&self, entity: Arc<dyn EntityBase>) {
|
||||
let base_entity = entity.get_entity();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user