#![doc = include_str!("../README.md")] mod behavior; pub mod block_state; pub mod fluid_state; mod generated; mod range; use core::fmt::Debug; use std::{any::Any, collections::HashMap, str::FromStr}; use azalea_registry::builtin::BlockKind; pub use behavior::BlockBehavior; // re-exported for convenience pub use block_state::BlockState; pub use generated::{blocks, properties}; pub use range::BlockStates; /// A trait that's implemented on block structs. /// /// See the [azalea_block documentation](crate) for details. pub trait BlockTrait: Debug + Any { fn behavior(&self) -> BlockBehavior; /// Get the Minecraft string ID for this block. /// /// For example, `stone` or `grass_block`. fn id(&self) -> &'static str; /// Convert the block struct to a [`BlockState`]. /// /// This is a lossless conversion, as [`BlockState`] also contains state /// data. fn as_block_state(&self) -> BlockState; /// Convert the block struct to a [`BlockKind`]. /// /// This is a lossy conversion, as [`BlockKind`] doesn't contain any state /// data. fn as_block_kind(&self) -> BlockKind; #[deprecated = "renamed to as_block_kind"] #[doc(hidden)] fn as_registry_block(&self) -> BlockKind { self.as_block_kind() } /// Returns a map of property names on this block to their values as /// strings. /// /// Consider using [`Self::get_property`] if you only need a single /// property. fn property_map(&self) -> HashMap<&'static str, &'static str>; /// Get a property's value as a string by its name, or `None` if the block /// has no property with that name. /// /// To get all properties, you may use [`Self::property_map`]. /// /// To set a property, use [`Self::set_property`]. fn get_property(&self, name: &str) -> Option<&'static str>; /// Update a property on this block, with the name and value being strings. /// /// Returns `Ok(())`, if the property name and value are valid, otherwise it /// returns `Err(InvalidPropertyError)`. /// /// To get a property, use [`Self::get_property`]. fn set_property(&mut self, name: &str, new_value: &str) -> Result<(), InvalidPropertyError>; } #[derive(Debug)] pub struct InvalidPropertyError; impl dyn BlockTrait { pub fn downcast_ref(&self) -> Option<&T> { (self as &dyn Any).downcast_ref::() } } pub trait Property: FromStr { type Value; fn try_from_block_state(state: BlockState) -> Option; /// Convert the value of the property to a string, like "x" or "true". fn to_static_str(&self) -> &'static str; } #[cfg(test)] mod tests { use crate::BlockTrait; #[test] pub fn roundtrip_block_state() { let block = crate::blocks::OakTrapdoor { facing: crate::properties::FacingCardinal::East, half: crate::properties::TopBottom::Bottom, open: true, powered: false, waterlogged: false, }; let block_state = block.as_block_state(); let block_from_state = Box::::from(block_state); let block_from_state = *block_from_state .downcast_ref::() .unwrap(); assert_eq!(block, block_from_state); } #[test] pub fn test_property_map() { let block = crate::blocks::OakTrapdoor { facing: crate::properties::FacingCardinal::East, half: crate::properties::TopBottom::Bottom, open: true, powered: false, waterlogged: false, }; let property_map = block.property_map(); assert_eq!(property_map.len(), 5); assert_eq!(property_map.get("facing"), Some(&"east")); assert_eq!(property_map.get("half"), Some(&"bottom")); assert_eq!(property_map.get("open"), Some(&"true")); assert_eq!(property_map.get("powered"), Some(&"false")); assert_eq!(property_map.get("waterlogged"), Some(&"false")); } #[test] pub fn test_integer_properties() { // Test with oak sapling that has an integer-like stage property let sapling_stage_0 = crate::blocks::OakSapling { stage: crate::properties::Stage::_0, }; let sapling_stage_1 = crate::blocks::OakSapling { stage: crate::properties::Stage::_1, }; // Test stage 0 let properties_0 = sapling_stage_0.property_map(); assert_eq!(properties_0.len(), 1); assert_eq!(properties_0.get("stage"), Some(&"0")); assert_eq!(sapling_stage_0.get_property("stage"), Some("0")); // Test stage 1 let properties_1 = sapling_stage_1.property_map(); assert_eq!(properties_1.len(), 1); assert_eq!(properties_1.get("stage"), Some(&"1")); assert_eq!(sapling_stage_1.get_property("stage"), Some("1")); // Test non-existent property assert_eq!(sapling_stage_0.get_property("nonexistent"), None); } }