mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
fix(bedrock): synchronize colored beds (#2917)
* fix(bedrock): synchronize colored beds * refactor(bedrock): abstract bed block actor data
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
use std::io::{Error, Write};
|
||||
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_nbt::{Nbt, compound::NbtCompound};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use crate::serial::PacketWrite;
|
||||
|
||||
/// Synchronizes the complete block-actor data for a block position.
|
||||
#[packet(56)]
|
||||
pub struct CBlockActorData {
|
||||
pub position: BlockPos,
|
||||
pub data: NbtCompound,
|
||||
}
|
||||
|
||||
impl CBlockActorData {
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos, data: NbtCompound) -> Self {
|
||||
Self { position, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketWrite for CBlockActorData {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
self.position.write(writer)?;
|
||||
writer.write_all(&Nbt::from(self.data.clone()).write_bedrock())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use pumpkin_nbt::deserializer::NbtReadHelperBedrock;
|
||||
|
||||
use super::*;
|
||||
use crate::{Packet, serial::PacketWrite};
|
||||
|
||||
#[test]
|
||||
fn block_actor_data_uses_bedrock_network_nbt() {
|
||||
assert_eq!(<CBlockActorData as Packet>::PACKET_ID, 56);
|
||||
|
||||
let mut data = NbtCompound::new();
|
||||
data.put_string("id", "Bed".to_string());
|
||||
data.put_byte("color", 11);
|
||||
|
||||
let mut encoded = Vec::new();
|
||||
CBlockActorData::new(BlockPos::new(1, 64, -2), data)
|
||||
.write(&mut encoded)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(&encoded[..4], &[2, 128, 1, 3]);
|
||||
let mut reader = NbtReadHelperBedrock::new(Cursor::new(&encoded[4..]));
|
||||
let parsed = Nbt::read(&mut reader).unwrap();
|
||||
assert_eq!(parsed.get_string("id"), Some("Bed"));
|
||||
assert_eq!(parsed.get_byte("color"), Some(11));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ pub mod add_item_actor;
|
||||
pub mod add_player;
|
||||
pub mod available_commands;
|
||||
pub mod biome_definition_list;
|
||||
pub mod block_actor_data;
|
||||
pub mod block_event;
|
||||
pub mod boss_event;
|
||||
pub mod change_dimension;
|
||||
@@ -61,6 +62,7 @@ pub use add_item_actor::*;
|
||||
pub use add_player::*;
|
||||
pub use available_commands::*;
|
||||
pub use biome_definition_list::*;
|
||||
pub use block_actor_data::*;
|
||||
pub use block_event::*;
|
||||
pub use boss_event::*;
|
||||
pub use change_dimension::*;
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::block::bounce_entity_after_fall;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, OnStateReplacedArgs,
|
||||
PlacedArgs,
|
||||
PlacedArgs, PlayerPlacedArgs,
|
||||
};
|
||||
use crate::entity::{Entity, EntityBase};
|
||||
use crate::world::World;
|
||||
@@ -135,6 +135,16 @@ impl BlockBehaviour for BedBlock {
|
||||
})
|
||||
}
|
||||
|
||||
fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
args.world.play_bedrock_level_sound(
|
||||
"place",
|
||||
&args.position.to_centered_f64(),
|
||||
i32::from(pumpkin_data::BlockState::to_be_network_id(args.state_id)),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let bed_props = BedProperties::from_state_id(args.state.id, args.block);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_data::{
|
||||
BlockState, BlockStateId,
|
||||
item::JavaToBedrockItemMapping,
|
||||
tag::{Block as BlockTag, Taggable},
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
@@ -32,6 +37,25 @@ impl BlockEntity for BedBlockEntity {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn bedrock_block_actor_data(&self, state_id: BlockStateId) -> Option<NbtCompound> {
|
||||
let (block, _) = BlockState::from_id_with_block(state_id);
|
||||
if !block.has_tag(&BlockTag::MINECRAFT_BEDS) {
|
||||
return None;
|
||||
}
|
||||
let color = JavaToBedrockItemMapping::from_java_item_id(block.item_id)?
|
||||
.bedrock_data
|
||||
.try_into()
|
||||
.ok()?;
|
||||
let mut nbt = NbtCompound::new();
|
||||
nbt.put_string("id", "Bed".to_string());
|
||||
nbt.put_int("x", self.position.0.x);
|
||||
nbt.put_int("y", self.position.0.y);
|
||||
nbt.put_int("z", self.position.0.z);
|
||||
nbt.put_byte("color", color);
|
||||
nbt.put_bool("isMovable", true);
|
||||
Some(nbt)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
@@ -44,3 +68,33 @@ impl BedBlockEntity {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::{BedBlockEntity, BlockEntity};
|
||||
|
||||
#[test]
|
||||
fn bedrock_block_actor_uses_the_java_bed_color() {
|
||||
let entity = BedBlockEntity::new(BlockPos::new(5, 64, 7));
|
||||
let blue = entity
|
||||
.bedrock_block_actor_data(Block::BLUE_BED.default_state.id)
|
||||
.unwrap();
|
||||
let red = entity
|
||||
.bedrock_block_actor_data(Block::RED_BED.default_state.id)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(blue.get_string("id"), Some("Bed"));
|
||||
assert_eq!(blue.get_byte("color"), Some(11));
|
||||
assert_eq!(red.get_byte("color"), Some(14));
|
||||
assert_eq!(blue.get_int("x"), Some(5));
|
||||
assert_eq!(blue.get_bool("isMovable"), Some(true));
|
||||
assert!(
|
||||
entity
|
||||
.bedrock_block_actor_data(Block::STONE.default_state.id)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,11 @@ pub trait BlockEntity: Any + Send + Sync {
|
||||
None
|
||||
}
|
||||
|
||||
/// Obtain block actor NBT for fields Bedrock does not include in its block state.
|
||||
fn bedrock_block_actor_data(&self, _state_id: BlockStateId) -> Option<NbtCompound> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use pumpkin_data::chunk::Biome;
|
||||
use pumpkin_data::item::{BedrockItem, BedrockItemVersion};
|
||||
use pumpkin_protocol::bedrock::client::item_registry::{CItemRegistry, ItemDefinition};
|
||||
use pumpkin_protocol::bedrock::client::level_event::{CLevelEvent, LevelEvent};
|
||||
use pumpkin_protocol::bedrock::client::{CBiomeDefinitionList, EntityProperties};
|
||||
use pumpkin_protocol::bedrock::client::{
|
||||
CBiomeDefinitionList, EntityProperties, block_actor_data::CBlockActorData,
|
||||
};
|
||||
use pumpkin_protocol::bedrock::network_item::{NetworkItemDescriptor, NetworkItemStackDescriptor};
|
||||
use pumpkin_protocol::codec::data_component::data_to_proto_sound;
|
||||
use pumpkin_world::generation::proto_chunk::GenerationCache;
|
||||
@@ -1289,6 +1291,12 @@ impl World {
|
||||
be_block_id as u32,
|
||||
),
|
||||
);
|
||||
if let Some(data) = self.bedrock_block_entity_data(block_state_id, block_pos) {
|
||||
self.broadcast_to_chunk_bedrock(
|
||||
chunk_pos,
|
||||
&CBlockActorData::new(block_pos, data),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let players = self.players.load();
|
||||
let mut java_recipients = Vec::new();
|
||||
@@ -1311,6 +1319,13 @@ impl World {
|
||||
be_block_id as u32,
|
||||
),
|
||||
);
|
||||
if let Some(data) =
|
||||
self.bedrock_block_entity_data(*block_state_id, *block_pos)
|
||||
{
|
||||
be_client.try_enqueue_packet(&CBlockActorData::new(
|
||||
*block_pos, data,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5442,33 +5457,61 @@ impl World {
|
||||
Some(entity)
|
||||
}
|
||||
|
||||
fn bedrock_block_entity_data(
|
||||
&self,
|
||||
state_id: BlockStateId,
|
||||
position: BlockPos,
|
||||
) -> Option<NbtCompound> {
|
||||
self.get_block_entity(&position)?
|
||||
.bedrock_block_actor_data(state_id)
|
||||
}
|
||||
|
||||
/// Builds Bedrock block actor tags that are not represented by Java block states alone.
|
||||
pub fn bedrock_chunk_block_actors(&self, chunk: &ChunkData) -> Vec<NbtCompound> {
|
||||
let chunk_pos = Vector2::new(chunk.x, chunk.z);
|
||||
let live_positions: FxHashSet<_> = self
|
||||
let live_entities: FxHashMap<_, _> = self
|
||||
.block_entities
|
||||
.get(&chunk_pos)
|
||||
.map(|entities| entities.keys().copied().collect())
|
||||
.map(|entities| {
|
||||
entities
|
||||
.iter()
|
||||
.map(|(position, entity)| (*position, entity.clone()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let pending = chunk
|
||||
.pending_block_entities
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
live_positions
|
||||
|
||||
live_entities
|
||||
.iter()
|
||||
.chain(
|
||||
pending
|
||||
.keys()
|
||||
.filter(|position| !live_positions.contains(position)),
|
||||
)
|
||||
.filter_map(|position| {
|
||||
.filter_map(|(position, entity)| {
|
||||
let relative = position.chunk_relative_position();
|
||||
chunk
|
||||
.section
|
||||
.get_block_absolute_y(relative.x as usize, relative.y, relative.z as usize)
|
||||
.and_then(|state_id| bedrock_chest_block_actor(state_id, *position))
|
||||
.and_then(|state_id| {
|
||||
bedrock_chest_block_actor(state_id, *position)
|
||||
.or_else(|| entity.bedrock_block_actor_data(state_id))
|
||||
})
|
||||
})
|
||||
.chain(
|
||||
pending
|
||||
.iter()
|
||||
.filter(|(position, _)| !live_entities.contains_key(position))
|
||||
.filter_map(|(position, nbt)| {
|
||||
let relative = position.chunk_relative_position();
|
||||
let state_id = chunk.section.get_block_absolute_y(
|
||||
relative.x as usize,
|
||||
relative.y,
|
||||
relative.z as usize,
|
||||
)?;
|
||||
bedrock_chest_block_actor(state_id, *position).or_else(|| {
|
||||
block_entity_from_nbt(nbt)?.bedrock_block_actor_data(state_id)
|
||||
})
|
||||
}),
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -5805,6 +5848,19 @@ impl World {
|
||||
Self::broadcast_java_grouped(packet, recipients_by_version);
|
||||
}
|
||||
|
||||
fn broadcast_to_chunk_bedrock<P: BClientPacket>(&self, chunk_pos: Vector2<i32>, packet: &P) {
|
||||
let players = self.players.load();
|
||||
for player in players.iter().filter(|player| {
|
||||
let center = player.get_entity().chunk_pos.load();
|
||||
let view_distance = get_view_distance(player).get() as i32;
|
||||
is_within_view_distance(chunk_pos, center, view_distance)
|
||||
}) {
|
||||
if let ClientPlatform::Bedrock(client) = player.client.as_ref() {
|
||||
client.try_enqueue_packet(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast_to_chunk_editioned_sync<J: ClientPacket, B: BClientPacket>(
|
||||
&self,
|
||||
chunk_pos: Vector2<i32>,
|
||||
|
||||
Reference in New Issue
Block a user