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 crate::test_support::advance_test_game_time_to;
75 use steel_registry::init_vanilla_registry;
76 use steel_registry::{vanilla_blocks, vanilla_world_clocks};
77 use steel_utils::ChunkPos;
78
79 use super::*;
80 use crate::behavior::init_behaviors;
81 use crate::test_support::{fresh_test_world, insert_ready_full_chunk};
82
83 #[test]
84 fn detector_updates_only_on_vanilla_twenty_game_tick_cadence() {
85 init_vanilla_registry();
86 init_behaviors();
87 let world = fresh_test_world("daylight_detector_cadence");
88 assert_eq!(
89 world.set_clock_total_ticks(&vanilla_world_clocks::OVERWORLD, 18_000),
90 Some(())
91 );
92 let pos = BlockPos::new(4, 64, 4);
93 insert_ready_full_chunk(&world, ChunkPos::from_block_pos(pos));
94 let state = vanilla_blocks::DAYLIGHT_DETECTOR
95 .default_state()
96 .set_value(&BlockStateProperties::INVERTED, true);
97 assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
98 let detector = DaylightDetectorBlockEntity::new(Arc::downgrade(&world), pos, state);
99
100 detector.tick(&world);
101 assert_eq!(
102 world
103 .get_block_state(pos)
104 .get_value(&BlockStateProperties::POWER),
105 11
106 );
107
108 advance_test_game_time_to(&world, 1);
109 assert!(world.set_block(pos, state, UpdateFlags::UPDATE_ALL));
110 detector.tick(&world);
111 assert_eq!(
112 world
113 .get_block_state(pos)
114 .get_value(&BlockStateProperties::POWER),
115 0
116 );
117 }
118}