Skip to main content

steel_core/entity/
ticking.rs

1//! Shared vanilla entity tick helpers.
2
3use std::sync::Arc;
4
5use smallvec::SmallVec;
6
7use super::{Entity, SharedEntity};
8
9/// Cycle-detection stack sized for the shallow passenger chains vanilla produces.
10pub(crate) type VehicleChain = SmallVec<[i32; 8]>;
11
12/// Snapshots vanilla old position and rotation before an entity tick.
13pub(crate) fn snapshot_old_pos_and_rot_for_tick(entity: &dyn Entity) {
14    entity.set_old_position_to_current();
15    entity.base().set_old_rotation_to_current();
16}
17
18/// Recursively ticks vehicle passengers that are eligible in the caller's tick context.
19///
20/// Mirrors vanilla `ServerLevel.tickPassenger`: invalid vehicle links are detached, and
21/// passengers only recurse when the server-level entity tick list says they may tick.
22pub(crate) fn tick_vehicle_passengers_if(
23    vehicle: &dyn Entity,
24    post_tick: &mut impl FnMut(&SharedEntity),
25    can_tick: &mut impl FnMut(&SharedEntity) -> bool,
26) {
27    let passengers = vehicle.passengers();
28    if passengers.is_empty() {
29        return;
30    }
31
32    let mut visited = VehicleChain::new();
33    visited.push(vehicle.id());
34
35    for passenger in passengers {
36        tick_passenger(vehicle, &passenger, post_tick, can_tick, &mut visited);
37    }
38}
39
40fn tick_passenger(
41    vehicle: &dyn Entity,
42    entity: &SharedEntity,
43    post_tick: &mut impl FnMut(&SharedEntity),
44    can_tick: &mut impl FnMut(&SharedEntity) -> bool,
45    visited: &mut VehicleChain,
46) {
47    let entity_id = entity.id();
48    assert!(
49        !visited.contains(&entity_id),
50        "cyclic passenger relationship involving entity {entity_id}"
51    );
52    visited.push(entity_id);
53
54    if entity.is_removed()
55        || entity
56            .vehicle()
57            .is_none_or(|current_vehicle| current_vehicle.id() != vehicle.id())
58    {
59        entity.stop_riding();
60        visited.pop();
61        return;
62    }
63
64    if can_tick(entity) {
65        snapshot_old_pos_and_rot_for_tick(entity.as_ref());
66        entity.advance_tick_count();
67        Arc::clone(entity).ride_tick();
68        post_tick(entity);
69
70        for passenger in entity.passengers() {
71            tick_passenger(entity.as_ref(), &passenger, post_tick, can_tick, visited);
72        }
73    }
74
75    visited.pop();
76}