use crate::simple_world::EntityId;
use alloc::vec::Vec;
use soroban_sdk::Symbol;
pub struct DirtyTracker {
dirty_entities: Vec<EntityId>,
dirty_components: Vec<(EntityId, Symbol)>,
despawned: Vec<EntityId>,
new_entities: Vec<EntityId>,
meta_dirty: bool,
}
impl DirtyTracker {
pub fn new() -> Self {
Self {
dirty_entities: Vec::new(),
dirty_components: Vec::new(),
despawned: Vec::new(),
new_entities: Vec::new(),
meta_dirty: false,
}
}
pub fn mark_entity_dirty(&mut self, entity_id: EntityId) {
if !self.dirty_entities.contains(&entity_id) {
self.dirty_entities.push(entity_id);
}
}
pub fn mark_component_dirty(&mut self, entity_id: EntityId, component_type: Symbol) {
let entry = (entity_id, component_type);
if !self.dirty_components.contains(&entry) {
self.dirty_components.push(entry);
}
}
pub fn mark_despawned(&mut self, entity_id: EntityId) {
if !self.despawned.contains(&entity_id) {
self.despawned.push(entity_id);
}
}
pub fn mark_new_entity(&mut self, entity_id: EntityId) {
if !self.new_entities.contains(&entity_id) {
self.new_entities.push(entity_id);
}
self.meta_dirty = true;
}
pub fn mark_meta_dirty(&mut self) {
self.meta_dirty = true;
}
pub fn is_dirty(&self) -> bool {
self.meta_dirty
|| !self.dirty_entities.is_empty()
|| !self.dirty_components.is_empty()
|| !self.despawned.is_empty()
|| !self.new_entities.is_empty()
}
pub fn dirty_entities(&self) -> &[EntityId] {
&self.dirty_entities
}
pub fn dirty_components(&self) -> &[(EntityId, Symbol)] {
&self.dirty_components
}
pub fn despawned(&self) -> &[EntityId] {
&self.despawned
}
#[cfg(test)]
pub fn new_entities(&self) -> &[EntityId] {
&self.new_entities
}
pub fn is_meta_dirty(&self) -> bool {
self.meta_dirty
}
pub fn clear(&mut self) {
self.dirty_entities.clear();
self.dirty_components.clear();
self.despawned.clear();
self.new_entities.clear();
self.meta_dirty = false;
}
}
impl Default for DirtyTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::{symbol_short, Env};
#[test]
fn test_new_tracker_not_dirty() {
let tracker = DirtyTracker::new();
assert!(!tracker.is_dirty());
assert!(tracker.dirty_entities().is_empty());
assert!(tracker.dirty_components().is_empty());
assert!(tracker.despawned().is_empty());
}
#[test]
fn test_mark_entity_dirty() {
let mut tracker = DirtyTracker::new();
tracker.mark_entity_dirty(1);
assert!(tracker.is_dirty());
assert_eq!(tracker.dirty_entities(), &[1]);
tracker.mark_entity_dirty(1);
assert_eq!(tracker.dirty_entities().len(), 1);
}
#[test]
fn test_mark_component_dirty() {
let _env = Env::default();
let mut tracker = DirtyTracker::new();
tracker.mark_component_dirty(1, symbol_short!("pos"));
assert!(tracker.is_dirty());
assert_eq!(tracker.dirty_components().len(), 1);
}
#[test]
fn test_mark_despawned() {
let mut tracker = DirtyTracker::new();
tracker.mark_despawned(5);
assert!(tracker.is_dirty());
assert_eq!(tracker.despawned(), &[5]);
}
#[test]
fn test_mark_new_entity() {
let mut tracker = DirtyTracker::new();
tracker.mark_new_entity(10);
assert!(tracker.is_dirty());
assert!(tracker.is_meta_dirty());
assert_eq!(tracker.new_entities(), &[10]);
}
#[test]
fn test_clear() {
let _env = Env::default();
let mut tracker = DirtyTracker::new();
tracker.mark_entity_dirty(1);
tracker.mark_component_dirty(1, symbol_short!("pos"));
tracker.mark_despawned(2);
tracker.mark_new_entity(3);
assert!(tracker.is_dirty());
tracker.clear();
assert!(!tracker.is_dirty());
assert!(tracker.dirty_entities().is_empty());
assert!(tracker.dirty_components().is_empty());
assert!(tracker.despawned().is_empty());
assert!(tracker.new_entities().is_empty());
assert!(!tracker.is_meta_dirty());
}
}