From fb361145eb4896aa83b457b3665d41999dcfb765 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 30 Dec 2023 03:33:33 +0100 Subject: [PATCH] world: add sign position and material information to block entities --- src/core/region_processor.rs | 7 ++++- src/world/block_entity.rs | 36 ++++++++++++++++------- src/world/chunk.rs | 57 ++++++++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/core/region_processor.rs b/src/core/region_processor.rs index cff87b7..63d5c09 100644 --- a/src/core/region_processor.rs +++ b/src/core/region_processor.rs @@ -243,7 +243,12 @@ impl<'a> SingleRegionProcessor<'a> { } if self.entities_needed { - let mut block_entities = chunk.block_entities(); + let mut block_entities = chunk.block_entities().with_context(|| { + format!( + "Failed to process block entities for chunk {:?}", + chunk_coords, + ) + })?; self.entities.block_entities.append(&mut block_entities); } diff --git a/src/world/block_entity.rs b/src/world/block_entity.rs index 9e60a23..182ad50 100644 --- a/src/world/block_entity.rs +++ b/src/world/block_entity.rs @@ -1,5 +1,6 @@ //! Processing of block entity data +use minedmap_resource::{BlockFlag, BlockType}; use serde::{Deserialize, Serialize}; use super::{ @@ -11,10 +12,14 @@ use super::{ #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[serde(rename_all = "snake_case")] pub enum SignKind { - /// Standing or attached sign + /// Standing sign Sign, + /// Sign attached to wall + WallSign, /// Hanging sign HangingSign, + /// Hanging sign attached to wall + HangingWallSign, } /// Processed sign data @@ -22,6 +27,9 @@ pub enum SignKind { pub struct Sign { /// The kind of the sign pub kind: SignKind, + /// The material of the sign + #[serde(skip_serializing_if = "Option::is_none", default)] + pub material: Option, /// The sign's front text #[serde(skip_serializing_if = "SignText::is_empty", default)] pub front_text: SignText, @@ -32,12 +40,13 @@ pub struct Sign { impl Sign { /// Processes a [de::BlockEntitySign] into a [Sign] - fn new(sign: &de::BlockEntitySign, kind: SignKind) -> Sign { + fn new(sign: &de::BlockEntitySign, kind: SignKind, material: Option) -> Sign { let (front_text, back_text) = sign.text(); let front_text = front_text.decode(); let back_text = back_text.decode(); Sign { kind, + material, front_text, back_text, } @@ -68,16 +77,21 @@ pub struct BlockEntity { impl BlockEntity { /// Processes a [de::BlockEntity] into a [BlockEntity] - pub fn new(entity: &de::BlockEntity) -> Option { - let data = match &entity.data { - de::BlockEntityData::Sign(sign) => { - BlockEntityData::Sign(Sign::new(sign, SignKind::Sign)) - } - de::BlockEntityData::HangingSign(sign) => { - BlockEntityData::Sign(Sign::new(sign, SignKind::HangingSign)) - } - de::BlockEntityData::Other => return None, + pub fn new(entity: &de::BlockEntity, block_type: Option<&BlockType>) -> Option { + let wall_sign = block_type + .map(|block_type| block_type.block_color.is(BlockFlag::WallSign)) + .unwrap_or_default(); + let (kind, sign) = match (&entity.data, wall_sign) { + (de::BlockEntityData::Sign(sign), false) => (SignKind::Sign, sign), + (de::BlockEntityData::Sign(sign), true) => (SignKind::WallSign, sign), + (de::BlockEntityData::HangingSign(sign), false) => (SignKind::HangingSign, sign), + (de::BlockEntityData::HangingSign(sign), true) => (SignKind::HangingWallSign, sign), + (de::BlockEntityData::Other, _) => return None, }; + let material = block_type + .as_ref() + .and_then(|block_type| block_type.sign_material.as_ref()); + let data = BlockEntityData::Sign(Sign::new(sign, kind, material.cloned())); Some(BlockEntity { x: entity.x, diff --git a/src/world/chunk.rs b/src/world/chunk.rs index 5a8937e..bf1d78b 100644 --- a/src/world/chunk.rs +++ b/src/world/chunk.rs @@ -12,8 +12,9 @@ use anyhow::{bail, Context, Result}; use super::{block_entity::BlockEntity, de, section::*}; use crate::{ - resource::{BiomeTypes, BlockTypes}, + resource::{BiomeTypes, BlockType, BlockTypes}, types::*, + util::{self, ShiftMask}, }; /// Version-specific part of [Chunk] @@ -243,12 +244,58 @@ impl<'a> Chunk<'a> { } } + /// Returns the section at a [SectionY] coordinate + fn section_at(&self, y: SectionY) -> Option<&dyn Section> { + match &self.inner { + ChunkInner::V1_18 { section_map } => section_map + .get(&y) + .map(|(section, _, _)| -> &dyn Section { section }), + ChunkInner::V1_13 { section_map, .. } => section_map + .get(&y) + .map(|(section, _)| -> &dyn Section { section }), + ChunkInner::V0 { section_map, .. } => section_map + .get(&y) + .map(|(section, _)| -> &dyn Section { section }), + ChunkInner::Empty => None, + } + } + + /// Returns the [BlockType] at a given coordinate + fn block_type_at(&self, y: SectionY, coords: SectionBlockCoords) -> Result> { + let Some(section) = self.section_at(y) else { + return Ok(None); + }; + section.block_at(coords) + } + + /// Returns the [BlockType] at the coordinates of a [de::BlockEntity] + fn block_type_at_block_entity( + &self, + block_entity: &de::BlockEntity, + ) -> Result> { + let x: BlockX = util::from_flat_coord(block_entity.x).2; + let z: BlockZ = util::from_flat_coord(block_entity.z).2; + let (section_y, block_y) = block_entity.y.shift_mask(BLOCK_BITS); + + let coords = SectionBlockCoords { + xz: LayerBlockCoords { x, z }, + y: BlockY::new(block_y), + }; + + self.block_type_at(SectionY(section_y), coords) + } + /// Processes all of the chunk's block entities - pub fn block_entities(&self) -> Vec { - self.block_entities + pub fn block_entities(&self) -> Result> { + let entities: Vec> = self + .block_entities .iter() - .filter_map(BlockEntity::new) - .collect() + .map(|block_entity| { + let block_type = self.block_type_at_block_entity(block_entity)?; + Ok(BlockEntity::new(block_entity, block_type)) + }) + .collect::>()?; + Ok(entities.into_iter().flatten().collect()) } }