steel_core/world/
redstone.rs1use std::collections::VecDeque;
4
5use steel_utils::BlockPos;
6
7use super::World;
8
9const RECENT_TOGGLE_TIMER: i64 = 60;
10const MAX_RECENT_TOGGLES: usize = 8;
11
12#[derive(Debug, Clone, Copy)]
13struct RedstoneTorchToggle {
14 pos: BlockPos,
15 when: i64,
16}
17
18#[derive(Debug, Default)]
20pub(super) struct RedstoneTorchToggleTracker {
21 toggles: VecDeque<RedstoneTorchToggle>,
22}
23
24impl RedstoneTorchToggleTracker {
25 fn prune(&mut self, game_time: i64) {
26 while self
27 .toggles
28 .front()
29 .is_some_and(|toggle| game_time.wrapping_sub(toggle.when) > RECENT_TOGGLE_TIMER)
30 {
31 self.toggles.pop_front();
32 }
33 }
34
35 fn is_toggled_too_frequently(&mut self, pos: BlockPos, game_time: i64, add: bool) -> bool {
36 if add {
37 self.toggles.push_back(RedstoneTorchToggle {
38 pos,
39 when: game_time,
40 });
41 }
42
43 self.toggles
44 .iter()
45 .filter(|toggle| toggle.pos == pos)
46 .take(MAX_RECENT_TOGGLES)
47 .count()
48 >= MAX_RECENT_TOGGLES
49 }
50}
51
52impl World {
53 pub(crate) fn prune_recent_redstone_torch_toggles(&self) {
55 let game_time = self.game_time();
56 self.redstone_torch_toggles.lock().prune(game_time);
57 }
58
59 pub(crate) fn redstone_torch_toggled_too_frequently(&self, pos: BlockPos, add: bool) -> bool {
64 let game_time = self.game_time();
65 self.redstone_torch_toggles
66 .lock()
67 .is_toggled_too_frequently(pos, game_time, add)
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn burnout_requires_eight_toggles_at_the_same_position() {
77 let mut tracker = RedstoneTorchToggleTracker::default();
78 let pos = BlockPos::new(4, 70, -2);
79 for _ in 0..7 {
80 assert!(!tracker.is_toggled_too_frequently(pos, 100, true));
81 }
82 assert!(tracker.is_toggled_too_frequently(pos, 100, true));
83 assert!(!tracker.is_toggled_too_frequently(pos.east(), 100, true));
84 }
85
86 #[test]
87 fn toggle_window_expires_only_after_sixty_game_ticks() {
88 let mut tracker = RedstoneTorchToggleTracker::default();
89 let pos = BlockPos::new(0, 64, 0);
90 for _ in 0..8 {
91 tracker.is_toggled_too_frequently(pos, 10, true);
92 }
93
94 tracker.prune(70);
95 assert!(tracker.is_toggled_too_frequently(pos, 70, false));
96 tracker.prune(71);
97 assert!(!tracker.is_toggled_too_frequently(pos, 71, false));
98 }
99}