Skip to main content

steel_core/block_entity/entities/
daylight_detector.rs

1//! Stateless ticking storage for daylight detectors.
2
3use 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
17/// Ticking storage for a daylight detector; recomputes its redstone signal
18/// from sky light every 20 ticks.
19pub struct DaylightDetectorBlockEntity {
20    base: BlockEntityBase,
21}
22
23// SAFETY: This key is owned by Steel and uniquely identifies
24// `DaylightDetectorBlockEntity`.
25unsafe impl DowncastType for DaylightDetectorBlockEntity {
26    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/daylight_detector");
27}
28
29impl DaylightDetectorBlockEntity {
30    /// Creates daylight-detector ticking storage.
31    #[must_use]
32    pub fn new(world: Weak<World>, pos: BlockPos, state: BlockStateId) -> Self {
33        Self {
34            base: BlockEntityBase::new(
35                &vanilla_block_entity_types::DAYLIGHT_DETECTOR,
36                world,
37                pos,
38                state,
39            ),
40        }
41    }
42}
43
44impl BlockEntity for DaylightDetectorBlockEntity {
45    fn base(&self) -> &BlockEntityBase {
46        &self.base
47    }
48
49    fn load_additional(&self, _nbt: &BorrowedNbtCompound<'_>) {}
50
51    fn save_additional(&self, _nbt: &mut NbtCompound) {}
52
53    fn tick(&self, world: &Arc<World>) {
54        if world.game_time() % 20 != 0 {
55            return;
56        }
57
58        let pos = self.get_block_pos();
59        let state = self.get_block_state();
60        let target = DaylightDetectorBlock::signal_strength(world, pos, state);
61        if state.get_value(&BlockStateProperties::POWER) == target {
62            return;
63        }
64
65        world.set_block(
66            pos,
67            state.set_value(&BlockStateProperties::POWER, target),
68            UpdateFlags::UPDATE_ALL,
69        );
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use crate::test_support::advance_test_game_time_to;
76    use steel_registry::init_vanilla_registry;
77    use steel_registry::{vanilla_blocks, vanilla_world_clocks};
78    use steel_utils::ChunkPos;
79
80    use super::*;
81    use crate::behavior::init_behaviors;
82    use crate::test_support::{fresh_test_world, insert_ready_full_chunk};
83
84    #[test]
85    fn detector_updates_only_on_vanilla_twenty_game_tick_cadence() {
86        init_vanilla_registry();
87        init_behaviors();
88        let world_fixture = fresh_test_world("daylight_detector_cadence");
89        let world = &world_fixture.world;
90        assert_eq!(
91            world.set_clock_total_ticks(&vanilla_world_clocks::OVERWORLD, 18_000),
92            Some(())
93        );
94        let pos = BlockPos::new(4, 64, 4);
95        insert_ready_full_chunk(world, ChunkPos::from_block_pos(pos));
96        let state = vanilla_blocks::DAYLIGHT_DETECTOR
97            .default_state()
98            .set_value(&BlockStateProperties::INVERTED, true);
99        assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
100        let detector = DaylightDetectorBlockEntity::new(Arc::downgrade(world), pos, state);
101
102        detector.tick(world);
103        assert_eq!(
104            world
105                .get_block_state(pos)
106                .get_value(&BlockStateProperties::POWER),
107            11
108        );
109
110        advance_test_game_time_to(world, 1);
111        assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
112        detector.tick(world);
113        assert_eq!(
114            world
115                .get_block_state(pos)
116                .get_value(&BlockStateProperties::POWER),
117            0
118        );
119    }
120}