steel_core/block_entity/entities/
daylight_detector.rs1use std::sync::{Arc, Weak};
4
5use simdnbt::borrow::BaseNbtCompound as BorrowedNbtCompound;
6use simdnbt::owned::NbtCompound;
7use steel_registry::blocks::block_state_ext::BlockStateExt as _;
8use steel_registry::blocks::properties::BlockStateProperties;
9use steel_registry::vanilla_block_entity_types;
10use steel_utils::types::UpdateFlags;
11use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey};
12
13use crate::behavior::blocks::DaylightDetectorBlock;
14use crate::block_entity::{BlockEntity, BlockEntityBase};
15use crate::world::World;
16
17pub struct DaylightDetectorBlockEntity {
19 base: BlockEntityBase,
20}
21
22unsafe impl DowncastType for DaylightDetectorBlockEntity {
25 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/daylight_detector");
26}
27
28impl DaylightDetectorBlockEntity {
29 #[must_use]
31 pub fn new(world: Weak<World>, pos: BlockPos, state: BlockStateId) -> Self {
32 Self {
33 base: BlockEntityBase::new(
34 &vanilla_block_entity_types::DAYLIGHT_DETECTOR,
35 world,
36 pos,
37 state,
38 ),
39 }
40 }
41}
42
43impl BlockEntity for DaylightDetectorBlockEntity {
44 fn base(&self) -> &BlockEntityBase {
45 &self.base
46 }
47
48 fn load_additional(&self, _nbt: &BorrowedNbtCompound<'_>) {}
49
50 fn save_additional(&self, _nbt: &mut NbtCompound) {}
51
52 fn tick(&self, world: &Arc<World>) {
53 if world.game_time() % 20 != 0 {
54 return;
55 }
56
57 let pos = self.get_block_pos();
58 let state = self.get_block_state();
59 let target = DaylightDetectorBlock::signal_strength(world, pos, state);
60 if state.get_value(&BlockStateProperties::POWER) == target {
61 return;
62 }
63
64 world.set_block(
65 pos,
66 state.set_value(&BlockStateProperties::POWER, target),
67 UpdateFlags::UPDATE_ALL,
68 );
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use steel_registry::init_vanilla_registry;
75 use steel_registry::{vanilla_blocks, vanilla_world_clocks};
76 use steel_utils::ChunkPos;
77
78 use super::*;
79 use crate::behavior::init_behaviors;
80 use crate::test_support::{fresh_test_world, insert_ready_full_chunk};
81
82 #[test]
83 fn detector_updates_only_on_vanilla_twenty_game_tick_cadence() {
84 init_vanilla_registry();
85 init_behaviors();
86 let world = fresh_test_world("daylight_detector_cadence");
87 assert_eq!(
88 world.set_clock_total_ticks(&vanilla_world_clocks::OVERWORLD, 18_000),
89 Some(())
90 );
91 let pos = BlockPos::new(4, 64, 4);
92 insert_ready_full_chunk(&world, ChunkPos::from_block_pos(pos));
93 let state = vanilla_blocks::DAYLIGHT_DETECTOR
94 .default_state()
95 .set_value(&BlockStateProperties::INVERTED, true);
96 assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
97 let detector = DaylightDetectorBlockEntity::new(Arc::downgrade(&world), pos, state);
98
99 detector.tick(&world);
100 assert_eq!(
101 world
102 .get_block_state(pos)
103 .get_value(&BlockStateProperties::POWER),
104 11
105 );
106
107 world.level_data.write().set_game_time(1);
108 assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
109 detector.tick(&world);
110 assert_eq!(
111 world
112 .get_block_state(pos)
113 .get_value(&BlockStateProperties::POWER),
114 0
115 );
116 }
117}