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 {
20 base: BlockEntityBase,
21}
22
23unsafe impl DowncastType for DaylightDetectorBlockEntity {
26 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/daylight_detector");
27}
28
29impl DaylightDetectorBlockEntity {
30 #[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}