steel_core/block_entity/
block_state_nbt.rs1use std::str::FromStr;
4
5use simdnbt::borrow::NbtCompound as BorrowedNbtCompound;
6use simdnbt::owned::NbtCompound;
7use steel_registry::blocks::block_state_ext::BlockStateExt as _;
8use steel_registry::{REGISTRY, RegistryExt as _};
9use steel_utils::{BlockStateId, Identifier};
10
11#[must_use]
12pub(crate) fn save(state: BlockStateId) -> NbtCompound {
13 let mut encoded = NbtCompound::new();
14 encoded.insert("Name", state.get_block().key.to_string());
15
16 let state_properties = REGISTRY.blocks.get_properties(state);
17 if !state_properties.is_empty() {
18 let mut properties = NbtCompound::new();
19 for (name, value) in state_properties {
20 properties.insert(name, value);
21 }
22 encoded.insert("Properties", properties);
23 }
24
25 encoded
26}
27
28#[must_use]
29pub(crate) fn load(encoded: BorrowedNbtCompound<'_, '_>) -> Option<BlockStateId> {
30 let name = encoded.string("Name")?;
31 let identifier = Identifier::from_str(name.to_str().as_ref()).ok()?;
32 let block = REGISTRY.blocks.by_key(&identifier)?;
33
34 let mut properties = Vec::new();
35 if let Some(encoded_properties) = encoded.compound("Properties") {
36 for (name, value) in encoded_properties.iter() {
37 let value = value.string()?;
38 properties.push((name.to_str().into_owned(), value.to_str().into_owned()));
39 }
40 }
41
42 REGISTRY.blocks.state_id_from_block_defaulted_properties(
43 block,
44 properties
45 .iter()
46 .map(|(name, value)| (name.as_str(), value.as_str())),
47 )
48}
49
50#[cfg(test)]
51mod tests {
52 use std::io::Cursor;
53
54 use simdnbt::borrow::read_compound as read_borrowed_compound;
55 use simdnbt::owned::NbtTag;
56 use steel_registry::blocks::properties::{BlockStateProperties, Direction, PistonType};
57 use steel_registry::init_vanilla_registry;
58 use steel_registry::vanilla_blocks;
59
60 use super::*;
61
62 #[test]
63 fn propertyful_block_state_round_trips_vanilla_nbt_shape() {
64 init_vanilla_registry();
65 let state = vanilla_blocks::PISTON_HEAD
66 .default_state()
67 .set_value(&BlockStateProperties::FACING, Direction::East)
68 .set_value(&BlockStateProperties::PISTON_TYPE, PistonType::Sticky)
69 .set_value(&BlockStateProperties::SHORT, true);
70
71 let encoded = save(state);
72 assert_eq!(
73 encoded.string("Name").map(|name| name.to_str()),
74 Some("minecraft:piston_head".into())
75 );
76 assert!(matches!(
77 encoded.get("Properties"),
78 Some(NbtTag::Compound(_))
79 ));
80
81 let mut bytes = Vec::new();
82 encoded.write(&mut bytes);
83 let borrowed = read_borrowed_compound(&mut Cursor::new(bytes.as_slice()))
84 .expect("test NBT should reborrow");
85 let view = BorrowedNbtCompound::from(&borrowed);
86
87 assert_eq!(load(view), Some(state));
88 }
89}