steel_core/world/tick_scheduler/
list.rs1use super::{
2 BinaryHeap, BlockPos, FxHashSet, Ordering, SavedTick, ScheduledTick, ScheduledTickKey, TickKey,
3 TickPriority,
4};
5
6impl<T: TickKey> ScheduledTick<T> {
7 #[must_use]
9 pub fn key(&self) -> ScheduledTickKey {
10 (self.pos, self.tick_type.key())
11 }
12
13 fn drain_order(&self, other: &Self) -> Ordering {
14 self.trigger_tick.cmp(&other.trigger_tick).then_with(|| {
15 intra_tick_drain_order(
16 self.priority,
17 self.sub_tick_order,
18 other.priority,
19 other.sub_tick_order,
20 )
21 })
22 }
23}
24
25pub(super) fn intra_tick_drain_order(
26 left_priority: TickPriority,
27 left_sub_tick_order: i64,
28 right_priority: TickPriority,
29 right_sub_tick_order: i64,
30) -> Ordering {
31 left_priority
32 .cmp(&right_priority)
33 .then_with(|| left_sub_tick_order.cmp(&right_sub_tick_order))
34}
35
36#[derive(Debug)]
37struct QueuedTick<T: TickKey> {
38 tick: ScheduledTick<T>,
39 insertion_order: u64,
40}
41
42impl<T: TickKey> PartialEq for QueuedTick<T> {
43 fn eq(&self, other: &Self) -> bool {
44 self.tick.drain_order(&other.tick) == Ordering::Equal
45 && self.insertion_order == other.insertion_order
46 }
47}
48
49impl<T: TickKey> Eq for QueuedTick<T> {}
50
51impl<T: TickKey> PartialOrd for QueuedTick<T> {
52 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
53 Some(self.cmp(other))
54 }
55}
56
57impl<T: TickKey> Ord for QueuedTick<T> {
58 fn cmp(&self, other: &Self) -> Ordering {
59 self.tick
60 .drain_order(&other.tick)
61 .reverse()
62 .then_with(|| other.insertion_order.cmp(&self.insertion_order))
63 }
64}
65
66#[derive(Debug)]
72pub struct TickList<T: TickKey> {
73 pending_ticks: Option<Vec<SavedTick<T>>>,
74 ticks: BinaryHeap<QueuedTick<T>>,
75 scheduled: FxHashSet<ScheduledTickKey>,
76 next_insertion_order: u64,
77}
78
79pub(super) struct TickListPackingSnapshot<T: TickKey> {
80 pending_ticks: Vec<SavedTick<T>>,
81 live_ticks: Vec<(ScheduledTick<T>, u64)>,
82}
83
84impl<T: TickKey> TickListPackingSnapshot<T> {
85 pub(super) fn pack(mut self, current_tick: i64) -> Vec<SavedTick<T>> {
86 self.live_ticks.sort_by(|left, right| {
87 left.0
88 .sub_tick_order
89 .cmp(&right.0.sub_tick_order)
90 .then_with(|| left.1.cmp(&right.1))
91 });
92 self.pending_ticks
93 .extend(self.live_ticks.into_iter().map(|(tick, _)| SavedTick {
94 tick_type: tick.tick_type,
95 pos: tick.pos,
96 delay: tick.trigger_tick.wrapping_sub(current_tick) as i32,
97 priority: tick.priority,
98 }));
99 self.pending_ticks
100 }
101}
102
103impl<T: TickKey> TickList<T> {
104 #[must_use]
106 pub fn new() -> Self {
107 Self {
108 pending_ticks: None,
109 ticks: BinaryHeap::new(),
110 scheduled: FxHashSet::default(),
111 next_insertion_order: 0,
112 }
113 }
114
115 #[must_use]
118 pub(crate) fn new_pending() -> Self {
119 Self {
120 pending_ticks: Some(Vec::new()),
121 ticks: BinaryHeap::new(),
122 scheduled: FxHashSet::default(),
123 next_insertion_order: 0,
124 }
125 }
126
127 #[must_use]
133 pub(crate) fn from_saved_ticks(saved_ticks: Vec<SavedTick<T>>) -> Self {
134 let mut result = Self::new_pending();
135 result.scheduled.reserve(saved_ticks.len());
136 for saved_tick in &saved_ticks {
137 result
138 .scheduled
139 .insert((saved_tick.pos, saved_tick.tick_type.key()));
140 }
141 result.pending_ticks = Some(saved_ticks);
142 result
143 }
144
145 #[must_use]
152 pub(crate) fn from_proto_saved_ticks(saved_ticks: Vec<SavedTick<T>>) -> Self {
153 let mut result = Self::new_pending();
154 result.scheduled.reserve(saved_ticks.len());
155 for saved_tick in saved_ticks {
156 result.schedule_saved_pending(saved_tick);
157 }
158 result
159 }
160
161 pub(crate) fn schedule(
166 &mut self,
167 tick_type: T,
168 pos: BlockPos,
169 trigger_tick: i64,
170 priority: TickPriority,
171 sub_tick_order: i64,
172 ) -> bool {
173 let key = (pos, tick_type.key());
174 if !self.scheduled.insert(key) {
175 return false;
176 }
177
178 self.push_unchecked(ScheduledTick {
179 tick_type,
180 pos,
181 trigger_tick,
182 priority,
183 sub_tick_order,
184 });
185 true
186 }
187
188 pub(crate) fn schedule_pending(
190 &mut self,
191 tick_type: T,
192 pos: BlockPos,
193 priority: TickPriority,
194 ) -> bool {
195 self.schedule_saved_pending(SavedTick {
196 tick_type,
197 pos,
198 delay: 0,
199 priority,
200 })
201 }
202
203 fn schedule_saved_pending(&mut self, saved_tick: SavedTick<T>) -> bool {
204 let key = (saved_tick.pos, saved_tick.tick_type.key());
205 if !self.scheduled.insert(key) {
206 return false;
207 }
208 let pending_ticks = self.pending_ticks.get_or_insert_default();
209 pending_ticks.push(saved_tick);
210 true
211 }
212
213 #[must_use]
215 pub(crate) fn has_tick(&self, pos: BlockPos, tick_type: T) -> bool {
216 self.scheduled.contains(&(pos, tick_type.key()))
217 }
218
219 #[must_use]
221 pub(crate) fn pending_entries(&self) -> &[SavedTick<T>] {
222 self.pending_ticks.as_deref().unwrap_or_default()
223 }
224
225 pub(crate) fn remove_pending_matching(
227 &mut self,
228 mut predicate: impl FnMut(&SavedTick<T>) -> bool,
229 ) -> usize {
230 let Self {
231 pending_ticks,
232 scheduled,
233 ..
234 } = self;
235 let Some(pending_ticks) = pending_ticks.as_mut() else {
236 return 0;
237 };
238
239 let old_len = pending_ticks.len();
240 pending_ticks.retain(|tick| {
241 if !predicate(tick) {
242 return true;
243 }
244
245 scheduled.remove(&(tick.pos, tick.tick_type.key()));
246 false
247 });
248 old_len - pending_ticks.len()
249 }
250
251 #[must_use]
253 #[cfg(test)]
254 pub(crate) fn pack(&self, current_tick: i64) -> Vec<SavedTick<T>> {
255 self.packing_snapshot().pack(current_tick)
256 }
257
258 pub(super) fn packing_snapshot(&self) -> TickListPackingSnapshot<T> {
259 let mut pending_ticks = Vec::with_capacity(self.len());
260 if let Some(pending) = &self.pending_ticks {
261 pending_ticks.extend_from_slice(pending);
262 }
263 let live_ticks = self
264 .ticks
265 .iter()
266 .map(|queued| (queued.tick, queued.insertion_order))
267 .collect();
268 TickListPackingSnapshot {
269 pending_ticks,
270 live_ticks,
271 }
272 }
273
274 pub(crate) fn unpack(&mut self, current_tick: i64) {
280 let Some(pending_ticks) = self.pending_ticks.take() else {
281 return;
282 };
283 let tick_count = pending_ticks.len() as i64;
284 self.ticks.reserve(pending_ticks.len());
285 for (index, saved_tick) in pending_ticks.into_iter().enumerate() {
286 self.push_unchecked(ScheduledTick {
287 tick_type: saved_tick.tick_type,
288 pos: saved_tick.pos,
289 trigger_tick: current_tick.wrapping_add(i64::from(saved_tick.delay)),
290 priority: saved_tick.priority,
291 sub_tick_order: -tick_count + index as i64,
292 });
293 }
294 }
295
296 #[must_use]
298 pub(crate) fn len(&self) -> usize {
299 self.ticks.len() + self.pending_ticks.as_ref().map_or(0, Vec::len)
300 }
301
302 fn push_unchecked(&mut self, tick: ScheduledTick<T>) {
303 let insertion_order = self.next_insertion_order;
304 self.next_insertion_order = self.next_insertion_order.wrapping_add(1);
305 self.ticks.push(QueuedTick {
306 tick,
307 insertion_order,
308 });
309 }
310
311 pub(super) fn peek(&self) -> Option<ScheduledTick<T>> {
312 Some(self.ticks.peek()?.tick)
313 }
314
315 pub(super) fn peek_ready(&self, current_tick: i64) -> Option<ScheduledTick<T>> {
316 let tick = self.ticks.peek()?.tick;
317 (tick.trigger_tick <= current_tick).then_some(tick)
318 }
319
320 pub(super) fn pop_ready(&mut self, current_tick: i64) -> Option<ScheduledTick<T>> {
321 self.peek_ready(current_tick)?;
322 let tick = self.ticks.pop()?.tick;
323 self.scheduled.remove(&tick.key());
324 Some(tick)
325 }
326
327 #[cfg(test)]
328 pub(super) fn drain_ready(&mut self, current_tick: i64) -> Vec<ScheduledTick<T>> {
329 let mut ready = Vec::new();
330 while let Some(tick) = self.pop_ready(current_tick) {
331 ready.push(tick);
332 }
333 ready
334 }
335}
336
337impl<T: TickKey> Default for TickList<T> {
338 fn default() -> Self {
339 Self::new()
340 }
341}