steel_core/block_entity/entities/
raw.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 RawBlockEntityState {
14 data: NbtCompound,
15}
16
17pub struct RawBlockEntity {
22 base: BlockEntityBase,
23 state: SyncMutex<RawBlockEntityState>,
24}
25
26unsafe impl DowncastType for RawBlockEntity {
29 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/raw");
30}
31
32impl RawBlockEntity {
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::with_data(block_entity_type, level, pos, state, NbtCompound::new())
42 }
43
44 #[must_use]
46 pub fn with_data(
47 block_entity_type: BlockEntityTypeRef,
48 level: Weak<World>,
49 pos: BlockPos,
50 state: BlockStateId,
51 data: NbtCompound,
52 ) -> Self {
53 Self {
54 base: BlockEntityBase::new(block_entity_type, level, pos, state),
55 state: SyncMutex::new(RawBlockEntityState { data }),
56 }
57 }
58}
59
60impl BlockEntity for RawBlockEntity {
61 fn base(&self) -> &BlockEntityBase {
62 &self.base
63 }
64
65 fn load_additional(&self, nbt: &BorrowedNbtCompound<'_>) {
66 let nbt_view: NbtCompoundView<'_, '_> = nbt.into();
67 self.state.lock().data = nbt_view.to_owned();
68 }
69
70 fn save_additional(&self, nbt: &mut NbtCompound) {
71 *nbt = self.state.lock().data.clone();
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::sync::Weak;
78
79 use steel_registry::{init_vanilla_registry, vanilla_block_entity_types, vanilla_blocks};
80
81 use super::*;
82
83 #[test]
84 fn full_metadata_replaces_stale_raw_metadata() {
85 init_vanilla_registry();
86 let mut data = NbtCompound::new();
87 data.insert("id", "minecraft:chest");
88 data.insert("x", 100_i32);
89 data.insert("custom", 7_i32);
90 let entity = RawBlockEntity::with_data(
91 &vanilla_block_entity_types::BARREL,
92 Weak::new(),
93 BlockPos::new(2, 70, -4),
94 vanilla_blocks::BARREL.default_state(),
95 data,
96 );
97
98 let saved = entity.save_with_full_metadata();
99 let custom = entity.save_custom_only();
100
101 assert_eq!(
102 saved.string("id").map(ToString::to_string),
103 Some("minecraft:barrel".to_owned())
104 );
105 assert_eq!(saved.int("x"), Some(2));
106 assert_eq!(saved.int("y"), Some(70));
107 assert_eq!(saved.int("z"), Some(-4));
108 assert_eq!(saved.int("custom"), Some(7));
109 assert!(!custom.contains("id"));
110 assert!(!custom.contains("x"));
111 assert_eq!(custom.int("custom"), Some(7));
112 }
113
114 #[test]
115 #[should_panic(expected = "invalid block entity minecraft:barrel state minecraft:stone")]
116 fn constructor_rejects_a_type_state_mismatch() {
117 init_vanilla_registry();
118 let _ = RawBlockEntity::new(
119 &vanilla_block_entity_types::BARREL,
120 Weak::new(),
121 BlockPos::new(2, 70, -4),
122 vanilla_blocks::STONE.default_state(),
123 );
124 }
125}