steel_core/block_entity/entities/
unimplemented.rs1use std::sync::Weak;
4
5use simdnbt::borrow::{BaseNbtCompound as BorrowedNbtCompound, NbtCompound as NbtCompoundView};
6use simdnbt::owned::NbtCompound;
7use steel_registry::block_entity_type::BlockEntityTypeRef;
8use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey, locks::SyncMutex};
9
10use crate::block_entity::{BlockEntity, BlockEntityBase};
11use crate::world::World;
12
13struct UnimplementedBlockEntityState {
14 data: NbtCompound,
15}
16
17pub struct UnimplementedBlockEntity {
22 base: BlockEntityBase,
23 state: SyncMutex<UnimplementedBlockEntityState>,
24}
25
26unsafe impl DowncastType for UnimplementedBlockEntity {
29 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/unimplemented");
30}
31
32impl UnimplementedBlockEntity {
33 #[must_use]
35 pub fn new(
36 block_entity_type: BlockEntityTypeRef,
37 level: Weak<World>,
38 pos: BlockPos,
39 state: BlockStateId,
40 ) -> Self {
41 Self {
42 base: BlockEntityBase::new(block_entity_type, level, pos, state),
43 state: SyncMutex::new(UnimplementedBlockEntityState {
44 data: NbtCompound::new(),
45 }),
46 }
47 }
48
49 pub fn with_data(
51 block_entity_type: BlockEntityTypeRef,
52 level: Weak<World>,
53 pos: BlockPos,
54 state: BlockStateId,
55 data: NbtCompound,
56 ) -> Result<Self, simdnbt::Error> {
57 let entity = Self::new(block_entity_type, level, pos, state);
58 entity.load_with_owned_components(&data)?;
59 Ok(entity)
60 }
61}
62
63impl BlockEntity for UnimplementedBlockEntity {
64 fn base(&self) -> &BlockEntityBase {
65 &self.base
66 }
67
68 fn load_additional(&self, nbt: &BorrowedNbtCompound<'_>) {
69 let nbt_view: NbtCompoundView<'_, '_> = nbt.into();
70 let mut data = nbt_view.to_owned();
71 while data.remove("components").is_some() {}
72 self.state.lock().data = data;
73 }
74
75 fn save_additional(&self, nbt: &mut NbtCompound) {
76 *nbt = self.state.lock().data.clone();
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use std::sync::Weak;
83
84 use steel_registry::data_components::{DataComponentMap, vanilla_components::REPAIR_COST};
85 use steel_registry::{init_vanilla_registry, vanilla_block_entity_types, vanilla_blocks};
86
87 use super::*;
88
89 #[test]
90 fn full_metadata_replaces_stale_unimplemented_metadata() {
91 init_vanilla_registry();
92 let mut data = NbtCompound::new();
93 data.insert("id", "minecraft:chest");
94 data.insert("x", 100_i32);
95 data.insert("custom", 7_i32);
96 let mut components = DataComponentMap::new();
97 components.set(REPAIR_COST, Some(3));
98 data.insert("components", components.to_nbt_tag_ref());
99 let entity = UnimplementedBlockEntity::with_data(
100 &vanilla_block_entity_types::BARREL,
101 Weak::new(),
102 BlockPos::new(2, 70, -4),
103 vanilla_blocks::BARREL.default_state(),
104 data,
105 )
106 .expect("fallback block entity should load owned NBT");
107
108 let saved = entity.save_with_full_metadata();
109 let custom = entity.save_custom_only();
110
111 assert_eq!(
112 saved.string("id").map(ToString::to_string),
113 Some("minecraft:barrel".to_owned())
114 );
115 assert_eq!(saved.int("x"), Some(2));
116 assert_eq!(saved.int("y"), Some(70));
117 assert_eq!(saved.int("z"), Some(-4));
118 assert_eq!(saved.int("custom"), Some(7));
119 assert_eq!(entity.collect_components().get(REPAIR_COST), Some(3));
120 assert!(!custom.contains("id"));
121 assert!(!custom.contains("x"));
122 assert!(!custom.contains("components"));
123 assert_eq!(custom.int("custom"), Some(7));
124 }
125
126 #[test]
127 #[should_panic(expected = "invalid block entity minecraft:barrel state minecraft:stone")]
128 fn constructor_rejects_a_type_state_mismatch() {
129 init_vanilla_registry();
130 let _ = UnimplementedBlockEntity::new(
131 &vanilla_block_entity_types::BARREL,
132 Weak::new(),
133 BlockPos::new(2, 70, -4),
134 vanilla_blocks::STONE.default_state(),
135 );
136 }
137}