Skip to main content

steel_core/world/tick_scheduler/
run_batch.rs

1use super::{
2    AtomicOrdering, AtomicUsize, BlockPos, FxHashMap, OnceLock, ScheduledTick, ScheduledTickKey,
3    TickKey,
4};
5
6/// Immutable ticks selected for one execution phase with a lazily built lookup index.
7///
8/// Vanilla creates its `willTickThisTick` hash set only on the first query. The executor advances
9/// `next_index` before each callback, so a materialized key index needs no per-callback removal.
10#[derive(Debug)]
11pub(crate) struct ScheduledTickRunBatch<T: TickKey> {
12    ticks: Vec<ScheduledTick<T>>,
13    next_index: AtomicUsize,
14    lookup: OnceLock<FxHashMap<ScheduledTickKey, usize>>,
15}
16
17impl<T: TickKey> ScheduledTickRunBatch<T> {
18    #[must_use]
19    pub(crate) const fn new(ticks: Vec<ScheduledTick<T>>) -> Self {
20        Self {
21            ticks,
22            next_index: AtomicUsize::new(0),
23            lookup: OnceLock::new(),
24        }
25    }
26
27    #[must_use]
28    pub(crate) fn ticks(&self) -> &[ScheduledTick<T>] {
29        &self.ticks
30    }
31
32    pub(crate) fn start(&self, index: usize) {
33        assert!(
34            index < self.ticks.len(),
35            "scheduled-tick batch index out of bounds"
36        );
37        self.next_index.store(index + 1, AtomicOrdering::Relaxed);
38    }
39
40    #[must_use]
41    pub(crate) fn contains(&self, pos: BlockPos, tick_type: T) -> bool {
42        let initial_index = self.next_index.load(AtomicOrdering::Relaxed);
43        if initial_index >= self.ticks.len() {
44            return false;
45        }
46        let lookup = self.lookup.get_or_init(|| {
47            self.ticks[initial_index..]
48                .iter()
49                .enumerate()
50                .map(|(index, tick)| (tick.key(), initial_index + index))
51                .collect()
52        });
53        let next_index = self.next_index.load(AtomicOrdering::Relaxed);
54        lookup
55            .get(&(pos, tick_type.key()))
56            .is_some_and(|&index| index >= next_index)
57    }
58
59    #[cfg(test)]
60    pub(super) fn lookup_is_initialized(&self) -> bool {
61        self.lookup.get().is_some()
62    }
63}