steel_core/block_entity/entities/
comparator.rs1use std::sync::Weak;
4
5use simdnbt::borrow::{BaseNbtCompound as BorrowedNbtCompound, NbtCompound as NbtCompoundView};
6use simdnbt::owned::NbtCompound;
7use steel_registry::vanilla_block_entity_types;
8use steel_utils::nbt::NbtValueInput as _;
9use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey, locks::SyncMutex};
10
11use crate::block_entity::{BlockEntity, BlockEntityBase};
12use crate::world::World;
13
14struct ComparatorState {
15 output_signal: i32,
16}
17
18pub struct ComparatorBlockEntity {
21 base: BlockEntityBase,
22 state: SyncMutex<ComparatorState>,
23}
24
25unsafe impl DowncastType for ComparatorBlockEntity {
27 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/comparator");
28}
29
30impl ComparatorBlockEntity {
31 #[must_use]
33 pub fn new(world: Weak<World>, pos: BlockPos, state: BlockStateId) -> Self {
34 Self {
35 base: BlockEntityBase::new(&vanilla_block_entity_types::COMPARATOR, world, pos, state),
36 state: SyncMutex::new(ComparatorState { output_signal: 0 }),
37 }
38 }
39
40 #[must_use]
42 pub fn output_signal(&self) -> i32 {
43 self.state.lock().output_signal
44 }
45
46 pub fn set_output_signal(&self, output_signal: i32) {
48 self.state.lock().output_signal = output_signal;
49 }
50}
51
52impl BlockEntity for ComparatorBlockEntity {
53 fn base(&self) -> &BlockEntityBase {
54 &self.base
55 }
56
57 fn load_additional(&self, nbt: &BorrowedNbtCompound<'_>) {
58 let nbt: NbtCompoundView<'_, '_> = nbt.into();
59 self.state.lock().output_signal = nbt.int_value("OutputSignal").unwrap_or(0);
60 }
61
62 fn save_additional(&self, nbt: &mut NbtCompound) {
63 nbt.insert("OutputSignal", self.output_signal());
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use std::io::Cursor;
70
71 use simdnbt::borrow::read_compound as read_borrowed_compound;
72 use steel_registry::{init_vanilla_registry, vanilla_blocks};
73
74 use super::*;
75
76 fn comparator() -> ComparatorBlockEntity {
77 init_vanilla_registry();
78 ComparatorBlockEntity::new(
79 Weak::new(),
80 BlockPos::new(4, 65, -9),
81 vanilla_blocks::COMPARATOR.default_state(),
82 )
83 }
84
85 #[test]
86 fn output_signal_round_trips_with_vanilla_nbt_key() {
87 let source = comparator();
88 source.set_output_signal(11);
89 let mut nbt = NbtCompound::new();
90 source.save_additional(&mut nbt);
91 assert_eq!(nbt.int("OutputSignal"), Some(11));
92
93 let mut bytes = Vec::new();
94 nbt.write(&mut bytes);
95 let borrowed = read_borrowed_compound(&mut Cursor::new(bytes.as_slice()))
96 .expect("test NBT should reborrow");
97 let loaded = comparator();
98 loaded.load_additional(&borrowed);
99 assert_eq!(loaded.output_signal(), 11);
100 }
101
102 #[test]
103 fn output_signal_loads_from_any_numeric_tag() {
104 let mut nbt = NbtCompound::new();
105 nbt.insert("OutputSignal", 11i8);
106 let mut bytes = Vec::new();
107 nbt.write(&mut bytes);
108 let borrowed = read_borrowed_compound(&mut Cursor::new(bytes.as_slice()))
109 .expect("test NBT should reborrow");
110 let loaded = comparator();
111 loaded.load_additional(&borrowed);
112 assert_eq!(loaded.output_signal(), 11);
113 }
114
115 #[test]
116 fn missing_output_signal_loads_vanilla_default() {
117 let nbt = NbtCompound::new();
118 let mut bytes = Vec::new();
119 nbt.write(&mut bytes);
120 let borrowed = read_borrowed_compound(&mut Cursor::new(bytes.as_slice()))
121 .expect("test NBT should reborrow");
122 let loaded = comparator();
123 loaded.set_output_signal(15);
124 loaded.load_additional(&borrowed);
125 assert_eq!(loaded.output_signal(), 0);
126 }
127}