mod entity;
mod error;
mod timeline;
mod timewave;
pub use entity::{Entity, EntityId, EntityState, Event, NameId};
pub use error::{Error, Result};
pub use timeline::Timeline;
pub use timewave::{Timewave, TimewaveId};
use std::collections::HashMap;
pub type Tick = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum LifecycleState {
Unborn = 0,
Prebirth = 3,
Dead = 4,
Chronoporting = 5,
Born = 6,
}
impl Default for LifecycleState {
fn default() -> Self {
Self::Unborn
}
}
pub struct Engine<S: EntityState> {
pub timeline: Timeline<S>,
pub timewaves: HashMap<TimewaveId, Timewave>,
pub current_tick: Tick,
next_wave_id: TimewaveId,
}
impl<S: EntityState> Engine<S> {
pub fn new() -> Self {
let mut engine = Self {
timeline: Timeline::new(),
timewaves: HashMap::new(),
current_tick: 0,
next_wave_id: TimewaveId(1),
};
let present = Timewave::new(TimewaveId(0), 0, 1.0);
engine.timewaves.insert(TimewaveId(0), present);
engine
}
pub fn with_capacity(max_entities: usize) -> Self {
let mut engine = Self {
timeline: Timeline::with_capacity(max_entities),
timewaves: HashMap::new(),
current_tick: 0,
next_wave_id: TimewaveId(1),
};
let present = Timewave::new(TimewaveId(0), 0, 1.0);
engine.timewaves.insert(TimewaveId(0), present);
engine
}
pub fn spawn(&mut self, state: S) -> EntityId {
self.spawn_at(state, self.current_tick)
}
pub fn spawn_at(&mut self, state: S, tick: Tick) -> EntityId {
self.timeline.spawn(state, tick)
}
pub fn get_state(&self, id: EntityId) -> Option<&Event<S>> {
self.timeline.get_event_at(id, self.current_tick)
}
pub fn set_state(&mut self, id: EntityId, state: S) -> Result<()> {
self.timeline.add_event(id, self.current_tick, state)
}
pub fn destroy(&mut self, id: EntityId) -> Result<()> {
self.timeline.destroy(id, self.current_tick)
}
pub fn chronoport(&mut self, id: EntityId, target_tick: Tick) -> Result<EntityId> {
self.timeline.chronoport(id, self.current_tick, target_tick)
}
pub fn get_same_name_entities(&self, id: EntityId) -> Vec<EntityId> {
self.timeline.get_same_name_entities(id)
}
pub fn add_timewave(&mut self, speed: f64) -> TimewaveId {
let id = self.next_wave_id;
self.next_wave_id = TimewaveId(self.next_wave_id.0 + 1);
let wave = Timewave::new(id, self.current_tick, speed);
self.timewaves.insert(id, wave);
id
}
pub fn add_timewave_at(&mut self, tick: Tick, speed: f64) -> TimewaveId {
let id = self.next_wave_id;
self.next_wave_id = TimewaveId(self.next_wave_id.0 + 1);
let wave = Timewave::new(id, tick, speed);
self.timewaves.insert(id, wave);
id
}
pub fn remove_timewave(&mut self, id: TimewaveId) -> Option<Timewave> {
if id.0 == 0 {
return None; }
self.timewaves.remove(&id)
}
pub fn get_wave_position(&self, id: TimewaveId) -> Option<Tick> {
self.timewaves.get(&id).map(|w| w.position)
}
pub fn tick(&mut self) {
self.tick_by(1);
}
pub fn tick_by(&mut self, delta: u64) {
self.current_tick += delta;
let wave_ids: Vec<TimewaveId> = self.timewaves.keys().copied().collect();
for id in &wave_ids {
if let Some(wave) = self.timewaves.get_mut(id) {
wave.advance(delta);
}
}
let mut sorted: Vec<TimewaveId> = wave_ids;
sorted.sort_by_key(|id| self.timewaves.get(id).map(|w| w.position).unwrap_or(0));
self.link_wave_chain(&sorted);
for wave_id in &sorted {
if let Some(wave) = self.timewaves.get(wave_id) {
self.timeline.activate_prebirth_entities(wave.position);
}
}
for i in 0..sorted.len() {
let wave_id = sorted[i];
self.refresh_wave_views(wave_id);
if i > 0 {
let prev_id = sorted[i - 1];
self.propagate_between_waves(prev_id, wave_id);
}
}
for wave_id in &sorted {
if let Some(wave) = self.timewaves.get_mut(wave_id) {
wave.clear_dirty();
wave.needs_sync = false;
}
}
}
fn link_wave_chain(&mut self, sorted: &[TimewaveId]) {
for id in sorted {
if let Some(wave) = self.timewaves.get_mut(id) {
wave.prev_wave = None;
wave.next_wave = None;
}
}
for i in 0..sorted.len() {
let current = sorted[i];
if i > 0 {
if let Some(wave) = self.timewaves.get_mut(¤t) {
wave.prev_wave = Some(sorted[i - 1]);
}
}
if i < sorted.len() - 1 {
if let Some(wave) = self.timewaves.get_mut(¤t) {
wave.next_wave = Some(sorted[i + 1]);
}
}
}
}
fn refresh_wave_views(&mut self, wave_id: TimewaveId) {
let wave_pos = match self.timewaves.get(&wave_id) {
Some(w) => w.position,
None => return,
};
let views: Vec<(EntityId, Tick)> = self
.timeline
.iter()
.filter_map(|(&id, entity)| {
entity
.get_event_at(wave_pos)
.map(|event| (id, event.timestamp))
})
.collect();
if let Some(wave) = self.timewaves.get_mut(&wave_id) {
for (id, tick) in views {
let old_tick = wave.get_entity_view(id);
wave.update_entity_view(id, tick);
if old_tick != Some(tick) {
wave.mark_dirty(id);
}
}
}
}
fn propagate_between_waves(&mut self, earlier_id: TimewaveId, later_id: TimewaveId) {
let (earlier_pos, later_pos, later_prev_pos) = {
let earlier = match self.timewaves.get(&earlier_id) {
Some(w) => w,
None => return,
};
let later = match self.timewaves.get(&later_id) {
Some(w) => w,
None => return,
};
if later.position <= earlier.position {
return;
}
(earlier.position, later.position, later.previous_position)
};
let crossed = later_prev_pos <= earlier_pos && later_pos > earlier_pos;
if !crossed {
return;
}
let views_to_propagate: Vec<(EntityId, Tick)> = {
let earlier = match self.timewaves.get(&earlier_id) {
Some(w) => w,
None => return,
};
earlier.entity_views().collect()
};
if let Some(later) = self.timewaves.get_mut(&later_id) {
for (id, tick) in views_to_propagate {
if later.get_entity_view(id).map(|t| t < tick).unwrap_or(true) {
later.update_entity_view(id, tick);
later.mark_dirty(id);
}
}
later.needs_sync = true;
}
}
pub fn get_state_at_wave(&self, id: EntityId, wave_id: TimewaveId) -> Option<&Event<S>> {
let wave = self.timewaves.get(&wave_id)?;
self.timeline.get_event_at(id, wave.position)
}
pub fn entities(&self) -> impl Iterator<Item = (EntityId, &Event<S>)> {
self.timeline.entities_at(self.current_tick)
}
pub fn entity_count(&self) -> usize {
self.timeline.entity_count()
}
pub fn active_entity_count(&self) -> usize {
self.entities().count()
}
pub fn set_time_window(&mut self, past: Option<Tick>, future: Option<Tick>) {
self.timeline.set_time_window(past, future);
}
pub fn get_time_window(&self) -> (Option<Tick>, Option<Tick>) {
(self.timeline.window_past, self.timeline.window_future)
}
}
impl<S: EntityState> Default for Engine<S> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Default, Debug, PartialEq)]
struct TestState {
value: i32,
}
impl EntityState for TestState {}
#[test]
fn test_spawn_and_get() {
let mut engine = Engine::<TestState>::new();
let id = engine.spawn(TestState { value: 42 });
let state = engine.get_state(id).unwrap();
assert_eq!(state.state.value, 42);
assert_eq!(state.lifecycle, LifecycleState::Born);
}
#[test]
fn test_chronoport_creates_duplicate() {
let mut engine = Engine::<TestState>::new();
let id = engine.spawn(TestState { value: 100 });
for _ in 0..10 {
engine.tick();
}
let new_id = engine.chronoport(id, 5).unwrap();
let same_name = engine.get_same_name_entities(id);
assert_eq!(same_name.len(), 2);
assert!(same_name.contains(&id));
assert!(same_name.contains(&new_id));
}
#[test]
fn test_timewave_advancement() {
let mut engine = Engine::<TestState>::new();
let fast_wave = engine.add_timewave(3.0);
for _ in 0..10 {
engine.tick();
}
assert_eq!(engine.get_wave_position(TimewaveId(0)), Some(10));
assert_eq!(engine.get_wave_position(fast_wave), Some(30));
}
#[test]
fn test_entity_state_history() {
let mut engine = Engine::<TestState>::new();
let id = engine.spawn(TestState { value: 0 });
for i in 1..=5 {
engine.tick();
engine.set_state(id, TestState { value: i * 10 }).unwrap();
}
let past_wave = engine.add_timewave_at(3, 0.0);
assert_eq!(engine.get_state(id).unwrap().state.value, 50);
assert_eq!(
engine.get_state_at_wave(id, past_wave).unwrap().state.value,
30
);
}
#[test]
fn test_prebirth_to_born_transition() {
let mut engine = Engine::<TestState>::new();
let original = engine.spawn(TestState { value: 100 });
for _ in 0..10 {
engine.tick();
}
let duplicate = engine.chronoport(original, 5).unwrap();
let before_wave = engine.add_timewave_at(5, 0.0);
let after_wave = engine.add_timewave_at(7, 0.0);
engine.tick();
let before_state = engine.get_state_at_wave(duplicate, before_wave);
assert!(
before_state.is_none()
|| before_state.map(|e| e.lifecycle) == Some(LifecycleState::Prebirth)
);
let after_state = engine.get_state_at_wave(duplicate, after_wave);
assert!(after_state.is_some());
assert_eq!(after_state.unwrap().lifecycle, LifecycleState::Born);
assert_eq!(after_state.unwrap().state.value, 100);
}
#[test]
fn test_wave_chaining() {
let mut engine = Engine::<TestState>::new();
let wave_a = engine.add_timewave_at(10, 1.0);
let wave_b = engine.add_timewave_at(20, 1.0);
let wave_c = engine.add_timewave_at(30, 1.0);
engine.tick();
let present = engine.timewaves.get(&TimewaveId(0)).unwrap();
assert!(present.prev_wave.is_none()); assert_eq!(present.next_wave, Some(wave_a));
let a = engine.timewaves.get(&wave_a).unwrap();
assert_eq!(a.prev_wave, Some(TimewaveId(0)));
assert_eq!(a.next_wave, Some(wave_b));
let c = engine.timewaves.get(&wave_c).unwrap();
assert_eq!(c.prev_wave, Some(wave_b));
assert!(c.next_wave.is_none()); }
#[test]
fn test_wave_view_cache() {
let mut engine = Engine::<TestState>::new();
let entity = engine.spawn(TestState { value: 42 });
engine.tick();
engine.set_state(entity, TestState { value: 100 }).unwrap();
let present = engine.timewaves.get(&TimewaveId(0)).unwrap();
let view = present.get_entity_view(entity);
assert!(view.is_some());
}
#[test]
fn test_fast_wave_passes_slow_wave() {
let mut engine = Engine::<TestState>::new();
let entity = engine.spawn(TestState { value: 1 });
let slow_wave = engine.add_timewave_at(0, 0.5);
let fast_wave = engine.add_timewave(2.0);
for i in 1..=10 {
engine.tick();
engine
.set_state(entity, TestState { value: i * 10 })
.unwrap();
}
assert_eq!(engine.get_wave_position(fast_wave), Some(20));
assert_eq!(engine.get_wave_position(slow_wave), Some(5));
let fast = engine.timewaves.get(&fast_wave).unwrap();
let slow = engine.timewaves.get(&slow_wave).unwrap();
assert!(fast.get_entity_view(entity).is_some());
assert!(slow.get_entity_view(entity).is_some());
let fast_state = engine.get_state_at_wave(entity, fast_wave).unwrap();
let slow_state = engine.get_state_at_wave(entity, slow_wave).unwrap();
assert!(fast_state.state.value > slow_state.state.value);
}
#[test]
fn test_time_window_past_constraint() {
let mut engine = Engine::<TestState>::new();
let entity = engine.spawn(TestState { value: 42 });
for _ in 0..100 {
engine.tick();
}
engine.set_time_window(Some(50), None);
let result = engine.chronoport(entity, 60);
assert!(result.is_ok());
let result = engine.chronoport(entity, 40);
assert!(result.is_err());
match result {
Err(Error::ChronoportPastWindow { target, min_allowed }) => {
assert_eq!(target, 40);
assert_eq!(min_allowed, 50);
}
_ => panic!("Expected ChronoportPastWindow error"),
}
}
#[test]
fn test_time_window_future_constraint() {
let mut engine = Engine::<TestState>::new();
let entity = engine.spawn(TestState { value: 42 });
for _ in 0..50 {
engine.tick();
}
engine.set_time_window(None, Some(20));
let result = engine.chronoport(entity, 60);
assert!(result.is_ok());
let result = engine.chronoport(entity, 80);
assert!(result.is_err());
match result {
Err(Error::ChronoportFutureWindow { target, max_allowed }) => {
assert_eq!(target, 80);
assert_eq!(max_allowed, 70);
}
_ => panic!("Expected ChronoportFutureWindow error"),
}
}
#[test]
fn test_time_window_both_constraints() {
let mut engine = Engine::<TestState>::new();
let entity = engine.spawn(TestState { value: 42 });
for _ in 0..100 {
engine.tick();
}
engine.set_time_window(Some(30), Some(30));
assert!(engine.chronoport(entity, 90).is_ok());
assert!(matches!(
engine.chronoport(entity, 50),
Err(Error::ChronoportPastWindow { .. })
));
assert!(matches!(
engine.chronoport(entity, 150),
Err(Error::ChronoportFutureWindow { .. })
));
}
}