diff options
Diffstat (limited to 'ecs')
38 files changed, 0 insertions, 6898 deletions
diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml deleted file mode 100644 index cf35a74..0000000 --- a/ecs/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ecs" -version = "0.1.0" -edition = "2021" - -[features] -vizoxide = ["dep:vizoxide"] - -[dependencies] -seq-macro = "0.3.5" -paste = "1.0.14" -thiserror = "1.0.49" -tracing = "0.1.39" -linkme = "0.3.29" -hashbrown = "0.15.2" -parking_lot = "0.12.3" -ecs-macros = { path = "../ecs-macros" } -util-macros = { path = "../util-macros" } -vizoxide = { version = "1.0.5", optional = true } - -[dev-dependencies.criterion] -version = "0.5.1" -default-features = false -features = ["cargo_bench_support"] - -[[bench]] -name = "query" -harness = false diff --git a/ecs/benches/query.rs b/ecs/benches/query.rs deleted file mode 100644 index f14bb06..0000000 --- a/ecs/benches/query.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::hint::black_box; -use std::path::PathBuf; - -use criterion::{criterion_group, criterion_main, Criterion}; -use ecs::{Component, World}; - -#[derive(Component)] -struct Foo -{ - _text: String, -} - -#[derive(Component)] -struct Bar -{ - _path: PathBuf, - _num: u64, -} - -#[derive(Component)] -struct Position -{ - _x: f32, - _y: f32, - _z: f32, -} - -#[derive(Component)] -struct PosA -{ - _x: f32, - _y: f32, - _z: f32, -} - -#[derive(Component)] -struct PosB -{ - _x: f32, - _y: f32, - _z: f32, -} - -#[derive(Component)] -struct PosC -{ - _x: f32, - _y: f32, - _z: f32, -} - -#[derive(Component)] -struct MoreText -{ - _more_text: String, -} - -#[derive(Component)] -struct EvenMoreText -{ - _even_more_text: String, -} - -fn spawn_1000_entities(world: &mut World) -{ - for _ in 0..300 { - world.create_entity(( - Bar { - _path: "/dev/zero".into(), - _num: 65789, - }, - Position { _x: 13.98, _y: 27.0, _z: 0.2 }, - Foo { _text: "Hello there".to_string() }, - PosA { - _x: 1183.98, - _y: 272628.0, - _z: 3306.2, - }, - PosB { - _x: 171183.98, - _y: 28.0, - _z: 336.2901, - }, - PosC { _x: 8273.98, _y: 28.0, _z: 336.2901 }, - MoreText { - _more_text: "Lorem ipsum".to_string(), - }, - EvenMoreText { - _even_more_text: "Wow so much text".to_string(), - }, - )); - } - - for _ in 0..700 { - world.create_entity(( - Bar { - _path: "/dev/null".into(), - _num: 65789, - }, - Position { _x: 88.11, _y: 9.0, _z: 36.11 }, - Foo { _text: "Hey".to_string() }, - PosA { - _x: 118310.98, - _y: 272628.0, - _z: 3306.2, - }, - PosB { _x: 11323.98, _y: 28.0, _z: 336.2901 }, - PosC { - _x: 8273.98, - _y: 21818.0, - _z: 336.2901, - }, - MoreText { - _more_text: "Lorem ipsum".to_string(), - }, - EvenMoreText { - _even_more_text: "Wow much text".to_string(), - }, - )); - } -} - -fn benchbark(criterion: &mut Criterion) -{ - criterion.bench_function("Iterate 1000 entities", |bencher| { - let mut world = World::new(); - - spawn_1000_entities(&mut world); - - let query = world.query::<(&Bar, &Position, &Foo), ()>(); - - bencher.iter(|| { - for comps in query.iter() { - black_box(comps); - } - }) - }); -} - -criterion_group!(benches, benchbark); -criterion_main!(benches); diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs deleted file mode 100644 index 2365eb0..0000000 --- a/ecs/examples/event_loop.rs +++ /dev/null @@ -1,98 +0,0 @@ -use ecs::actions::Actions; -use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::relationship::{ChildOf, Relationship}; -use ecs::{static_entity, Component, Query, World}; - -#[derive(Component)] -struct Wool -{ - remaining: u32, -} - -#[derive(Component)] -struct Health -{ - health: u32, -} - -#[derive(Component)] -struct Name -{ - name: &'static str, -} - -fn sheer(query: Query<(&mut Wool, &Name)>) -{ - for (mut wool, name) in &query { - if wool.remaining == 0 { - println!("{} Has no wool left", name.name); - - continue; - } - - // Sheer the whool - wool.remaining -= 5; - - println!("Sheered 5 wool from {}", name.name); - } -} - -fn feed(query: Query<(&mut Health, &Name)>) -{ - for (mut health, name) in &query { - health.health += 1; - - println!("Feeded {} which gained 1 health", name.name); - } -} - -fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions) -{ - for (mut health, name) in &query { - if health.health <= 2 { - health.health = 0; - - println!("{} passed away", name.name); - - actions.stop(); - - continue; - } - - health.health -= 2; - - println!("{} aged and lost 2 health", name.name); - } -} - -static_entity!( - SHEER_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE)) -); - -static_entity!( - FEED_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*SHEER_PHASE)) -); - -static_entity!( - AGE_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*FEED_PHASE)) -); - -fn main() -{ - let mut world = World::new(); - - world.register_system(*SHEER_PHASE, sheer); - world.register_system(*FEED_PHASE, feed); - world.register_system(*AGE_PHASE, age); - - world.create_entity(( - Wool { remaining: 30 }, - Health { health: 3 }, - Name { name: "Bessy" }, - )); - - world.start_loop(); -} diff --git a/ecs/examples/extension.rs b/ecs/examples/extension.rs deleted file mode 100644 index f6282e1..0000000 --- a/ecs/examples/extension.rs +++ /dev/null @@ -1,70 +0,0 @@ -use ecs::actions::Actions; -use ecs::extension::{Collector as ExtensionCollector, Extension}; -use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Debug, Component)] -struct Position -{ - x: u32, - y: u32, -} - -#[derive(Debug, Component)] -struct EnemySpawnSource; - -#[derive(Debug, Component)] -enum EvilnessLevel -{ - Medium, -} - -fn spawn_enemies( - spawner_query: Query<(&EnemySpawnSource, &Position)>, - enemies_query: Query<(&EvilnessLevel,)>, - mut actions: Actions, -) -{ - let Some((_, enemy_spawner_position)) = spawner_query.iter().next() else { - return; - }; - - let enemy_cnt = enemies_query.iter().count(); - - if enemy_cnt > 3 { - return; - } - - actions.spawn(( - EvilnessLevel::Medium, - Position { - x: enemy_spawner_position.x * enemy_cnt as u32, - y: enemy_spawner_position.y, - }, - )); - - println!("Spawned enemy with medium evilness and 45 strength"); -} - -struct EnemySpawningExtension; - -impl Extension for EnemySpawningExtension -{ - fn collect(self, mut collector: ExtensionCollector<'_>) - { - collector.add_system(*UPDATE_PHASE, spawn_enemies); - - collector.add_entity((Position { x: 187, y: 30 }, EnemySpawnSource)); - } -} - -fn main() -{ - let mut world = World::new(); - - world.add_extension(EnemySpawningExtension); - - for _ in 0..7 { - world.step(); - } -} diff --git a/ecs/examples/multiple_queries.rs b/ecs/examples/multiple_queries.rs deleted file mode 100644 index e0c957f..0000000 --- a/ecs/examples/multiple_queries.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::fmt::Display; - -use ecs::phase::START as START_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct Health -{ - health: u32, -} - -#[derive(Component)] -enum AttackStrength -{ - Strong, - Weak, -} - -#[derive(Component)] -struct EnemyName -{ - name: String, -} - -impl Display for EnemyName -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - self.name.fmt(formatter) - } -} - -fn do_attacks( - attacker_query: Query<(&AttackStrength,)>, - enemy_query: Query<(&mut Health, &EnemyName)>, -) -{ - for (attack_strength,) in &attacker_query { - for (mut health, enemy_name) in &enemy_query { - let damage = match *attack_strength { - AttackStrength::Strong => 20, - AttackStrength::Weak => 10, - }; - - if health.health <= damage { - println!("Enemy '{}' died", *enemy_name); - - health.health = 0; - - continue; - } - - health.health -= damage; - - println!("Enemy '{}' took {damage} damage", *enemy_name); - } - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*START_PHASE, do_attacks); - - world.create_entity(( - Health { health: 100 }, - EnemyName { name: "Big spider".to_string() }, - )); - - world.create_entity(( - Health { health: 30 }, - EnemyName { name: "Small goblin".to_string() }, - )); - - world.create_entity(( - Health { health: 30 }, - EnemyName { name: "Headcrab".to_string() }, - )); - - world.create_entity((AttackStrength::Strong,)); - world.create_entity((AttackStrength::Weak,)); - - world.step(); -} diff --git a/ecs/examples/optional_component.rs b/ecs/examples/optional_component.rs deleted file mode 100644 index 488dad2..0000000 --- a/ecs/examples/optional_component.rs +++ /dev/null @@ -1,81 +0,0 @@ -use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Debug, Component)] -struct PettingCapacity -{ - capacity_left: u32, -} - -#[derive(Debug, Clone, Copy, Component)] -enum Aggressivity -{ - High, - Medium, - Low, -} - -#[derive(Debug, Component)] -pub struct CatName -{ - name: String, -} - -fn pet_cats(query: Query<(&CatName, &mut PettingCapacity, &Option<Aggressivity>)>) -{ - for (cat_name, mut petting_capacity, aggressivity) in &query { - let Some(aggressivity) = aggressivity else { - println!("Aggressivity of cat {} is unknown. Skipping", cat_name.name); - continue; - }; - - if let Aggressivity::High = *aggressivity { - println!("Cat {} is aggressive. Skipping", cat_name.name); - continue; - } - - if petting_capacity.capacity_left == 0 { - println!( - "Cat {} have had enough of being petted. Skipping", - cat_name.name - ); - continue; - } - - println!("Petting cat {}", cat_name.name); - - petting_capacity.capacity_left -= 1; - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*UPDATE_PHASE, pet_cats); - - world.create_entity(( - CatName { name: "Jasper".to_string() }, - Aggressivity::Medium, - PettingCapacity { capacity_left: 5 }, - )); - - world.create_entity(( - CatName { name: "Otto".to_string() }, - PettingCapacity { capacity_left: 9 }, - )); - - world.create_entity(( - CatName { name: "Carrie".to_string() }, - PettingCapacity { capacity_left: 2 }, - Aggressivity::High, - )); - - world.create_entity(( - CatName { name: "Tommy".to_string() }, - PettingCapacity { capacity_left: 1 }, - Aggressivity::Low, - )); - - world.step(); -} diff --git a/ecs/examples/relationship.rs b/ecs/examples/relationship.rs deleted file mode 100644 index 240884a..0000000 --- a/ecs/examples/relationship.rs +++ /dev/null @@ -1,50 +0,0 @@ -use ecs::phase::START as START_PHASE; -use ecs::relationship::Relationship; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct Sword -{ - attack_strength: u32, -} - -#[derive(Component)] -struct Player; - -#[derive(Component)] -struct Health -{ - health: u32, -} - -struct Holding; - -fn print_player_stats( - player_query: Query<(&Player, &Health, &Relationship<Holding, Sword>)>, -) -{ - for (_, health, sword_relationship) in &player_query { - println!("Player health: {}", health.health); - - if let Some(sword) = sword_relationship.get(0) { - println!("Player sword attack strength: {}", sword.attack_strength); - } - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*START_PHASE, print_player_stats); - - let sword_uid = world.create_entity((Sword { attack_strength: 17 },)); - - world.create_entity(( - Player, - Health { health: 180 }, - Relationship::<Holding, Sword>::new(sword_uid), - )); - - world.step(); -} diff --git a/ecs/examples/simple.rs b/ecs/examples/simple.rs deleted file mode 100644 index 0169062..0000000 --- a/ecs/examples/simple.rs +++ /dev/null @@ -1,42 +0,0 @@ -use ecs::phase::START as START_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct SomeData -{ - num: u64, -} - -#[derive(Component)] -struct Greeting -{ - greeting: String, -} - -fn say_hello(query: Query<(&SomeData, &Greeting)>) -{ - for (data, greeting) in &query { - println!("{}: {}", greeting.greeting, data.num); - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*START_PHASE, say_hello); - - world.create_entity(( - SomeData { num: 987_654 }, - Greeting { - greeting: "Good afternoon".to_string(), - }, - )); - - world.create_entity(( - SomeData { num: 345 }, - Greeting { greeting: "Good evening".to_string() }, - )); - - world.step(); -} diff --git a/ecs/examples/with_local.rs b/ecs/examples/with_local.rs deleted file mode 100644 index 4658fc0..0000000 --- a/ecs/examples/with_local.rs +++ /dev/null @@ -1,69 +0,0 @@ -use ecs::component::local::Local; -use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::system::{Into, System}; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct SomeData -{ - num: u64, -} - -#[derive(Component)] -struct Name -{ - name: String, -} - -#[derive(Component)] -struct SayHelloState -{ - cnt: usize, -} - -fn say_hello(query: Query<(&SomeData,)>, mut state: Local<SayHelloState>) -{ - for (data,) in &query { - println!("Hello there. Count {}: {}", state.cnt, data.num); - - state.cnt += 1; - } -} - -fn say_whats_up(query: Query<(&SomeData, &Name)>, mut state: Local<SayHelloState>) -{ - for (data, name) in &query { - println!( - "Whats up, {}. Number is {}. Count {}", - name.name, data.num, state.cnt - ); - - state.cnt += 1; - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system( - *UPDATE_PHASE, - say_hello - .into_system() - .initialize((SayHelloState { cnt: 0 },)), - ); - - world.register_system( - *UPDATE_PHASE, - say_whats_up - .into_system() - .initialize((SayHelloState { cnt: 0 },)), - ); - - world.create_entity((SomeData { num: 987_654 }, Name { name: "Bob".to_string() })); - - world.create_entity((SomeData { num: 345 },)); - - world.step(); - world.step(); -} diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs deleted file mode 100644 index 689e562..0000000 --- a/ecs/examples/with_sole.rs +++ /dev/null @@ -1,53 +0,0 @@ -use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::relationship::{ChildOf, Relationship}; -use ecs::sole::Single; -use ecs::{static_entity, Component, Query, Sole, World}; - -#[derive(Component)] -struct Ammo -{ - ammo_left: u32, -} - -#[derive(Sole, Default)] -struct AmmoCounter -{ - counter: u32, -} - -fn count_ammo(query: Query<(&Ammo,)>, mut ammo_counter: Single<AmmoCounter>) -{ - for (ammo,) in &query { - println!("Found {} ammo", ammo.ammo_left); - - ammo_counter.counter += ammo.ammo_left; - } -} - -fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>) -{ - println!("Total ammo count: {}", ammo_counter.counter); - - assert_eq!(ammo_counter.counter, 19); -} - -static_entity!( - PRINT_AMMO_COUNT_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE)) -); - -fn main() -{ - let mut world = World::new(); - - world.register_system(*UPDATE_PHASE, count_ammo); - world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count); - - world.create_entity((Ammo { ammo_left: 4 },)); - world.create_entity((Ammo { ammo_left: 7 },)); - world.create_entity((Ammo { ammo_left: 8 },)); - - world.add_sole(AmmoCounter::default()).unwrap(); - - world.step(); -} diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs deleted file mode 100644 index 7dff3a5..0000000 --- a/ecs/src/actions.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::marker::PhantomData; -use std::sync::{Arc, Weak}; - -use crate::component::{ - Component, - Metadata as ComponentMetadata, - Sequence as ComponentSequence, -}; -use crate::system::{Param as SystemParam, System}; -use crate::uid::{Kind as UidKind, Uid}; -use crate::{ActionQueue, World}; - -/// Used to to queue up actions for a [`World`] to perform. -#[derive(Debug)] -pub struct Actions<'world> -{ - action_queue: &'world ActionQueue, - action_queue_weak: Weak<ActionQueue>, -} - -impl<'world> Actions<'world> -{ - /// Queues up a entity to spawn at the end of the current tick. - pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps) - { - self.action_queue.push(Action::Spawn( - components.into_array().into(), - EventIds { ids: Comps::added_event_ids() }, - )); - } - - /// Queues up despawning a entity at the end of the current tick. - pub fn despawn(&mut self, entity_uid: Uid) - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - self.action_queue.push(Action::Despawn(entity_uid)); - } - - /// Queues up adding component(s) to a entity at the end of the current tick. - pub fn add_components<Comps>(&mut self, entity_uid: Uid, components: Comps) - where - Comps: ComponentSequence, - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - if Comps::COUNT == 0 { - return; - } - - self.action_queue.push(Action::AddComponents( - entity_uid, - components.into_array().into(), - EventIds { ids: Comps::added_event_ids() }, - )); - } - - /// Queues up removing component(s) from a entity at the end of the current tick. - pub fn remove_components<Comps>(&mut self, entity_uid: Uid) - where - Comps: ComponentSequence, - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - if Comps::COUNT == 0 { - return; - } - - self.action_queue.push(Action::RemoveComponents( - entity_uid, - Comps::metadata().into_iter().collect(), - EventIds { ids: Comps::removed_event_ids() }, - )); - } - - /// Stops the [`World`]. The world will finish the current tick and that tick will be - /// the last. - pub fn stop(&mut self) - { - self.action_queue.push(Action::Stop); - } - - /// Returns a struct which holds a weak reference to the [`World`] that `Actions` - /// references and that can be used to aquire a new `Actions` instance if the - /// referenced [`World`] is still alive. - #[must_use] - pub fn to_weak_ref(&self) -> WeakRef - { - WeakRef { - action_queue: self.action_queue_weak.clone(), - } - } - - fn new(action_queue: &'world Arc<ActionQueue>) -> Self - { - Self { - action_queue, - action_queue_weak: Arc::downgrade(action_queue), - } - } -} - -impl<'world> SystemParam<'world> for Actions<'world> -{ - type Input = (); - - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self - { - Self::new(&world.data.action_queue) - } -} - -#[derive(Debug, Clone)] -pub struct WeakRef -{ - action_queue: Weak<ActionQueue>, -} - -impl WeakRef -{ - /// Returns a struct which can be used to retrieve a [`Actions`]. - /// - /// Returns [`None`] if the referenced [`World`] has been dropped. - #[must_use] - pub fn access(&self) -> Option<Ref<'_>> - { - Some(Ref { - action_queue: self.action_queue.upgrade()?, - _pd: PhantomData, - }) - } -} - -/// Intermediate between [`Actions`] and [`WeakRef`]. Contains a strong reference to -/// a world which is not allowed direct access to. -#[derive(Debug, Clone)] -pub struct Ref<'weak_ref> -{ - action_queue: Arc<ActionQueue>, - _pd: PhantomData<&'weak_ref ()>, -} - -impl Ref<'_> -{ - #[must_use] - pub fn to_actions(&self) -> Actions<'_> - { - Actions::new(&self.action_queue) - } -} - -#[derive(Debug)] -pub(crate) struct EventIds -{ - pub(crate) ids: Vec<Uid>, -} - -/// A action for a [`System`] to perform. -#[derive(Debug)] -pub(crate) enum Action -{ - Spawn(Vec<(Uid, Box<dyn Component>)>, EventIds), - Despawn(Uid), - AddComponents(Uid, Vec<(Uid, Box<dyn Component>)>, EventIds), - RemoveComponents(Uid, Vec<ComponentMetadata>, EventIds), - Stop, -} diff --git a/ecs/src/component.rs b/ecs/src/component.rs deleted file mode 100644 index 525bd98..0000000 --- a/ecs/src/component.rs +++ /dev/null @@ -1,472 +0,0 @@ -use std::any::{type_name, Any}; -use std::fmt::Debug; -use std::ops::{Deref, DerefMut}; - -use seq_macro::seq; - -use crate::event::component::{ - Added as ComponentAddedEvent, - Kind as ComponentEventKind, - Removed as ComponentRemovedEvent, -}; -use crate::lock::{ - Error as LockError, - Lock, - MappedReadGuard, - MappedWriteGuard, - ReadGuard, - WriteGuard, -}; -use crate::system::Input as SystemInput; -use crate::type_name::TypeName; -use crate::uid::Uid; -use crate::util::Array; -use crate::World; - -pub mod local; - -pub(crate) mod storage; - -pub trait Component: SystemInput + Any + TypeName -{ - /// The component type in question. Will usually be `Self` - type Component: Component - where - Self: Sized; - - type HandleMut<'component>: FromLockedOptional<'component> - where - Self: Sized; - - type Handle<'component>: FromLockedOptional<'component> - where - Self: Sized; - - /// Returns the ID of this component. - fn id() -> Uid - where - Self: Sized; - - /// Returns the component UID of a component event for this component. - fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid; - - /// Returns whether the component `self` is optional. - fn self_is_optional(&self) -> bool - { - false - } - - /// Returns whether this component is optional. - #[must_use] - fn is_optional() -> bool - where - Self: Sized, - { - false - } -} - -impl dyn Component -{ - pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real> - { - (self as &mut dyn Any).downcast_mut() - } - - pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real> - { - (self as &dyn Any).downcast_ref() - } - - pub fn is<Other: 'static>(&self) -> bool - { - (self as &dyn Any).is::<Other>() - } -} - -impl Debug for dyn Component -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - formatter.debug_struct("Component").finish_non_exhaustive() - } -} - -impl TypeName for Box<dyn Component> -{ - fn type_name(&self) -> &'static str - { - self.as_ref().type_name() - } -} - -impl<ComponentT> Component for Option<ComponentT> -where - ComponentT: Component, - for<'a> Option<ComponentT::Handle<'a>>: FromLockedOptional<'a>, - for<'a> Option<ComponentT::HandleMut<'a>>: FromLockedOptional<'a>, -{ - type Component = ComponentT; - type Handle<'component> = Option<ComponentT::Handle<'component>>; - type HandleMut<'component> = Option<ComponentT::HandleMut<'component>>; - - fn id() -> Uid - { - ComponentT::id() - } - - fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid - { - match event_kind { - ComponentEventKind::Removed => ComponentRemovedEvent::<Self>::id(), - } - } - - fn self_is_optional(&self) -> bool - { - true - } - - fn is_optional() -> bool - { - true - } -} - -impl<ComponentT> TypeName for Option<ComponentT> -where - ComponentT: Component, -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} - -impl<ComponentT> SystemInput for Option<ComponentT> where ComponentT: Component {} - -/// A sequence of components. -pub trait Sequence -{ - /// The number of components in this component sequence. - const COUNT: usize; - - type Array: Array<(Uid, Box<dyn Component>)>; - - fn into_array(self) -> Self::Array; - - fn metadata() -> impl Array<Metadata>; - - fn added_event_ids() -> Vec<Uid>; - - fn removed_event_ids() -> Vec<Uid>; -} - -/// A mutable or immutable reference to a component. -pub trait Ref -{ - type Component: Component; - type Handle<'component>: FromLockedOptional<'component>; -} - -impl<ComponentT> Ref for &ComponentT -where - ComponentT: Component, -{ - type Component = ComponentT; - type Handle<'component> = ComponentT::Handle<'component>; -} - -impl<ComponentT> Ref for &mut ComponentT -where - ComponentT: Component, -{ - type Component = ComponentT; - type Handle<'component> = ComponentT::HandleMut<'component>; -} - -/// [`Component`] metadata. -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Metadata -{ - pub id: Uid, - pub is_optional: bool, -} - -impl Metadata -{ - #[must_use] - pub fn new_non_optional(id: Uid) -> Self - { - Self { id, is_optional: false } - } - - #[must_use] - pub fn of<ComponentT: Component>() -> Self - { - Self { - id: ComponentT::id(), - is_optional: ComponentT::is_optional(), - } - } -} - -pub trait FromLockedOptional<'comp>: Sized -{ - /// Converts a reference to a optional locked boxed component to a instance of `Self`. - /// - /// # Errors - /// Returns `Err` if taking the lock (in a non-blocking way) fails. - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - world: &'comp World, - ) -> Result<Self, LockError>; -} - -#[derive(Debug)] -pub struct Handle<'a, ComponentT: Component> -{ - inner: MappedReadGuard<'a, ComponentT>, -} - -impl<'a, ComponentT: Component> Handle<'a, ComponentT> -{ - pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Component>>) -> Self - { - Self { - inner: inner.map(|component| { - component.downcast_ref::<ComponentT>().unwrap_or_else(|| { - panic!( - "Cannot downcast component {} to type {}", - component.type_name(), - type_name::<ComponentT>() - ); - }) - }), - } - } -} - -impl<'component, ComponentT: Component> FromLockedOptional<'component> - for Handle<'component, ComponentT> -{ - fn from_locked_optional_component( - optional_component: Option<&'component crate::lock::Lock<Box<dyn Component>>>, - _world: &'component World, - ) -> Result<Self, LockError> - { - let component = optional_component.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - }); - - Ok(Self::new(component.read_nonblock()?)) - } -} - -impl<'comp, ComponentT> FromLockedOptional<'comp> for Option<Handle<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - _world: &'comp World, - ) -> Result<Self, LockError> - { - optional_component - .map(|lock| Ok(Handle::new(lock.read_nonblock()?))) - .transpose() - } -} - -impl<ComponentT: Component> Deref for Handle<'_, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -#[derive(Debug)] -pub struct HandleMut<'a, ComponentT: Component> -{ - inner: MappedWriteGuard<'a, ComponentT>, -} - -impl<'a, ComponentT: Component> HandleMut<'a, ComponentT> -{ - pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Component>>) -> Self - { - Self { - inner: inner.map(|component| { - let component_type_name = component.type_name(); - - component.downcast_mut::<ComponentT>().unwrap_or_else(|| { - panic!( - "Cannot downcast component {component_type_name} to type {}", - type_name::<ComponentT>() - ); - }) - }), - } - } -} - -impl<'component, ComponentT: Component> FromLockedOptional<'component> - for HandleMut<'component, ComponentT> -{ - fn from_locked_optional_component( - optional_component: Option<&'component Lock<Box<dyn Component>>>, - _world: &'component World, - ) -> Result<Self, LockError> - { - let component = optional_component.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - }); - - Ok(Self::new(component.write_nonblock()?)) - } -} - -impl<'comp, ComponentT> FromLockedOptional<'comp> for Option<HandleMut<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - _world: &'comp World, - ) -> Result<Self, LockError> - { - optional_component - .map(|lock| Ok(HandleMut::new(lock.write_nonblock()?))) - .transpose() - } -} - -impl<ComponentT: Component> Deref for HandleMut<'_, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<ComponentT: Component> DerefMut for HandleMut<'_, ComponentT> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -macro_rules! inner { - ($c: tt) => { - seq!(I in 0..=$c { - impl<#(IntoComp~I,)*> Sequence for (#(IntoComp~I,)*) - where - #( - for<'comp> IntoComp~I: Into<Component: Component>, - )* - { - const COUNT: usize = $c + 1; - - type Array = [(Uid, Box<dyn Component>); $c + 1]; - - fn into_array(self) -> Self::Array - { - [#({ - let (id, component) = self.I.into_component(); - - (id, Box::new(component)) - },)*] - } - - fn metadata() -> impl Array<Metadata> - { - [ - #( - Metadata { - id: IntoComp~I::Component::id(), - is_optional: IntoComp~I::Component::is_optional(), - }, - )* - ] - } - - fn added_event_ids() -> Vec<Uid> - { - vec![ - #(ComponentAddedEvent::<IntoComp~I::Component>::id(),)* - ] - } - - fn removed_event_ids() -> Vec<Uid> - { - vec![ - #(ComponentRemovedEvent::<IntoComp~I::Component>::id(),)* - ] - } - } - }); - }; -} - -seq!(C in 0..=16 { - inner!(C); -}); - -impl Sequence for () -{ - type Array = [(Uid, Box<dyn Component>); 0]; - - const COUNT: usize = 0; - - fn into_array(self) -> Self::Array - { - [] - } - - fn metadata() -> impl Array<Metadata> - { - [] - } - - fn added_event_ids() -> Vec<Uid> - { - Vec::new() - } - - fn removed_event_ids() -> Vec<Uid> - { - Vec::new() - } -} - -pub trait Into -{ - type Component; - - fn into_component(self) -> (Uid, Self::Component); -} - -impl<ComponentT> Into for ComponentT -where - ComponentT: Component, -{ - type Component = Self; - - fn into_component(self) -> (Uid, Self::Component) - { - (Self::id(), self) - } -} diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs deleted file mode 100644 index 0f6f641..0000000 --- a/ecs/src/component/local.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::system::{Param as SystemParam, System}; -use crate::World; - -/// Holds a component which is local to a single system. -#[derive(Debug)] -pub struct Local<'world, LocalComponent: Component> -{ - local_component: ComponentHandleMut<'world, LocalComponent>, -} - -impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> -where - LocalComponent: Component, -{ - type Input = LocalComponent; - - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ) - { - system.set_local_component(input); - } - - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - _world: &'world World, - ) -> Self - { - let local_component = system - .get_local_component_mut::<LocalComponent>() - .expect("Local component is uninitialized"); - - Self { local_component } - } -} - -impl<LocalComponent> Deref for Local<'_, LocalComponent> -where - LocalComponent: Component, -{ - type Target = LocalComponent; - - fn deref(&self) -> &Self::Target - { - &self.local_component - } -} - -impl<LocalComponent> DerefMut for Local<'_, LocalComponent> -where - LocalComponent: Component, -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.local_component - } -} diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs deleted file mode 100644 index 40909fb..0000000 --- a/ecs/src/component/storage.rs +++ /dev/null @@ -1,798 +0,0 @@ -use std::any::type_name; -use std::array::IntoIter as ArrayIter; -use std::cell::RefCell; -use std::vec::IntoIter as VecIntoIter; - -use hashbrown::HashMap; - -use crate::component::storage::archetype::{ - Archetype, - Entity as ArchetypeEntity, - EntityComponent as ArchetypeEntityComponent, - Id as ArchetypeId, -}; -use crate::component::storage::graph::{ - ArchetypeAddEdgeDfsIter, - ArchetypeAddEdgeDfsIterResult, - ArchetypeEdges, - Graph, -}; -use crate::component::Component; -use crate::type_name::TypeName; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::{BorrowedOrOwned, Either, StreamingIterator, VecExt}; - -pub mod archetype; - -mod graph; - -#[derive(Debug)] -pub struct ArchetypeSearchTerms<'a> -{ - pub required_components: &'a [Uid], - pub excluded_components: &'a [Uid], -} - -impl ArchetypeSearchTerms<'_> -{ - fn excluded_contains(&self, uid: Uid) -> bool - { - self.excluded_components.binary_search(&uid).is_ok() - } - - fn required_contains(&self, uid: Uid) -> bool - { - self.required_components.binary_search(&uid).is_ok() - } -} - -#[derive(Debug, Default)] -pub struct Storage -{ - graph: Graph, - entity_archetype_lookup: HashMap<Uid, ArchetypeId>, - imaginary_archetypes: RefCell<Vec<ImaginaryArchetype>>, -} - -impl Storage -{ - pub fn search_archetypes<'search_terms>( - &self, - search_terms: ArchetypeSearchTerms<'search_terms>, - ) -> ArchetypeRefIter<'_, 'search_terms> - { - let archetype_id = ArchetypeId::new(&search_terms.required_components); - - if search_terms - .excluded_components - .iter() - .any(|excluded_comp_id| search_terms.required_contains(*excluded_comp_id)) - { - return ArchetypeRefIter { - storage: self, - pre_iter: Either::B(Vec::new().into_iter()), - dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &[]), - search_terms, - }; - } - - let Some(add_edge_recursive_iter) = - self.graph.dfs_archetype_add_edges(archetype_id) - else { - self.imaginary_archetypes - .borrow_mut() - .push(ImaginaryArchetype { - id: archetype_id, - component_ids: search_terms.required_components.to_vec(), - }); - - let found_archetypes = self.find_all_archetype_with_comps(&search_terms); - - return ArchetypeRefIter { - storage: self, - pre_iter: Either::B(found_archetypes.clone().into_iter()), - dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &found_archetypes), - search_terms, - }; - }; - - ArchetypeRefIter { - storage: self, - pre_iter: Either::A([archetype_id].into_iter()), - dfs_iter: add_edge_recursive_iter, - search_terms, - } - } - - pub fn get_archetype_by_id(&self, id: ArchetypeId) -> Option<&Archetype> - { - Some(self.graph.get_node_by_id(id)?.archetype()) - } - - pub fn create_entity(&mut self, uid: Uid) -> Result<(), Error> - { - debug_assert_eq!(uid.kind(), UidKind::Entity); - - if self.entity_archetype_lookup.contains_key(&uid) { - return Err(Error::EntityAlreadyExists(uid)); - } - - let empty_archetype_id = ArchetypeId::from_components_metadata(&[]); - - let archetype_node = self.graph.get_or_create_node(empty_archetype_id, &[]); - - archetype_node - .archetype_mut() - .push_entity(ArchetypeEntity::new(uid, [])); - - self.entity_archetype_lookup.insert(uid, empty_archetype_id); - - Ok(()) - } - - pub fn remove_entity(&mut self, entity_uid: Uid) -> Result<ArchetypeEntity, Error> - { - let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { - return Err(Error::EntityDoesNotExist(entity_uid)); - }; - - let archetype_node = self - .graph - .get_node_by_id_mut(*archetype_id) - .expect("Archetype should exist"); - - let entity = archetype_node - .archetype_mut() - .remove_entity(entity_uid) - .expect("Entity should exist in archetype"); - - self.entity_archetype_lookup.remove(&entity_uid); - - Ok(entity) - } - - pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype> - { - let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; - - self.get_archetype_by_id(*archetype_id) - } - - pub fn add_entity_component( - &mut self, - entity_uid: Uid, - component: (Uid, Box<dyn Component>), - ) -> Result<(), Error> - { - let (component_id, component) = component; - - debug_assert!( - !component.self_is_optional(), - "Adding a optional component to a entity is not supported" - ); - - let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { - return Err(Error::EntityDoesNotExist(entity_uid)); - }; - - let archetype_id = *archetype_id; - - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - if archetype_node - .archetype() - .has_component_with_id(component_id) - { - return Err(Error::ComponentAlreadyInEntity { - entity: entity_uid, - component: component_id, - }); - } - - let add_edge_archetype_id = match archetype_node - .get_or_insert_edges(component_id, ArchetypeEdges::default) - .add - { - Some(add_edge_id) => { - if !self.graph.contains_archetype(add_edge_id) { - let (_, add_edge_comp_ids) = self - .graph - .get_node_by_id(archetype_id) - .expect("Archetype should exist") - .make_add_edge(component_id); - - self.graph.create_node(add_edge_id, &add_edge_comp_ids); - } - - add_edge_id - } - None => { - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - let (add_edge_id, add_edge_comp_ids) = - archetype_node.make_add_edge(component_id); - - archetype_node - .get_edges_mut(component_id) - .expect("Edges for component in archetype should exist") - .add = Some(add_edge_id); - - if !self.graph.contains_archetype(add_edge_id) { - self.graph.create_node(add_edge_id, &add_edge_comp_ids); - } - - add_edge_id - } - }; - - { - let add_edge_archetype_node = self - .graph - .get_node_by_id_mut(add_edge_archetype_id) - .expect("Add edge archetype should exist"); - - let add_edge_archetype_edges = add_edge_archetype_node - .get_or_insert_edges(component_id, ArchetypeEdges::default); - - add_edge_archetype_edges.remove = Some(archetype_id); - } - - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - let mut entity = archetype_node - .archetype_mut() - .remove_entity(entity_uid) - .expect("Entity should exist in archetype"); - - let add_edge_archetype = self - .graph - .get_node_by_id_mut(add_edge_archetype_id) - .expect("Add edge archetype should exist") - .archetype_mut(); - - entity.insert_component( - component_id, - ArchetypeEntityComponent::new(component), - add_edge_archetype, - ); - - add_edge_archetype.push_entity(entity); - - self.entity_archetype_lookup - .insert(entity_uid, add_edge_archetype_id); - - Ok(()) - } - - pub fn remove_entity_component( - &mut self, - entity_uid: Uid, - component_id: Uid, - ) -> Result<(), Error> - { - let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { - return Err(Error::EntityDoesNotExist(entity_uid)); - }; - - let archetype_id = *archetype_id; - - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - if !archetype_node - .archetype() - .has_component_with_id(component_id) - { - return Err(Error::ComponentNotFoundInEntity { - entity: entity_uid, - component: component_id, - }); - } - - let remove_edge_id = archetype_node - .get_or_insert_edges(component_id, ArchetypeEdges::default) - .remove - .unwrap_or_else(|| { - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - let (remove_edge_id, remove_edge_comp_ids) = - archetype_node.make_remove_edge(component_id); - - archetype_node - .get_edges_mut(component_id) - .expect("Edges for component in archetype should exist") - .remove = Some(remove_edge_id); - - if !self.graph.contains_archetype(remove_edge_id) { - self.graph - .create_node(remove_edge_id, &remove_edge_comp_ids); - } - - remove_edge_id - }); - - let archetype_node = self - .graph - .get_node_by_id_mut(archetype_id) - .expect("Archetype should exist"); - - let mut entity = archetype_node - .archetype_mut() - .remove_entity(entity_uid) - .expect("Entity should exist in archetype"); - - entity.remove_component(component_id, archetype_node.archetype()); - - self.graph - .get_node_by_id_mut(remove_edge_id) - .expect("Remove edge archetype should exist") - .archetype_mut() - .push_entity(entity); - - self.entity_archetype_lookup - .insert(entity_uid, remove_edge_id); - - Ok(()) - } - - pub fn create_imaginary_archetypes(&mut self) - { - for imaginary_archetype in self.imaginary_archetypes.get_mut().drain(..) { - if self.graph.contains_archetype(imaginary_archetype.id) { - continue; - } - - self.graph - .create_node(imaginary_archetype.id, &imaginary_archetype.component_ids); - } - } - - fn find_all_archetype_with_comps( - &self, - search_terms: &ArchetypeSearchTerms<'_>, - ) -> Vec<ArchetypeId> - { - let Some(mut search_iter) = - self.graph.dfs_archetype_add_edges(ArchetypeId::new(&[])) - else { - // If the root archetype doesn't exist, no other archetype can exist either - return Vec::new(); - }; - - let mut found = Vec::<ArchetypeId>::new(); - - while let Some(node_id) = search_iter.streaming_next() { - let ArchetypeAddEdgeDfsIterResult::AddEdge { - add_edge_archetype_id: node_id, - add_edge_component_id, - } = node_id - else { - continue; - }; - - if search_terms.excluded_contains(add_edge_component_id) { - search_iter.pop(); - continue; - } - - let node = self - .graph - .get_node_by_id(node_id) - .expect("Graph node found through DFS doesn't exist"); - - if node.archetype().component_cnt() < search_terms.required_components.len() { - continue; - } - - if !search_terms - .required_components - .iter() - .all(|comp_id| node.archetype().has_component_with_id(*comp_id)) - { - continue; - } - - found.push(node.archetype().id()); - - search_iter.pop(); - } - - found - } -} - -impl TypeName for Storage -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} - -#[cfg(feature = "vizoxide")] -impl Storage -{ - pub fn create_vizoxide_archetype_graph( - &self, - graph_name: impl AsRef<str>, - params: VizoxideArchetypeGraphParams, - ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> - { - let viz_graph = vizoxide::Graph::builder(graph_name.as_ref()) - .strict(true) - .directed(true) - .build()?; - - let mut viz_node_lookup = HashMap::new(); - - for node in self.graph.iter_nodes() { - let id = node.archetype().id(); - - if !viz_node_lookup.contains_key(&id) { - let node = self.graph.get_node_by_id(id).unwrap(); - - let viz_node = (params.create_node_cb)( - node.archetype(), - ArchetypeMetadata { is_imaginary: false }, - viz_graph.create_node(&(params.create_node_name)( - node.archetype(), - ArchetypeMetadata { is_imaginary: false }, - )), - ) - .build()?; - - viz_node_lookup.insert(id, viz_node); - } - - for (edge_comp_id, edges) in node.iter_edges() { - if let Some(add_edge) = edges.add { - if !viz_node_lookup.contains_key(&add_edge) { - let viz_node = self.create_vizoxide_archetype_graph_edge_node( - &viz_graph, - node, - add_edge, - *edge_comp_id, - ¶ms, - )?; - - viz_node_lookup.insert(add_edge, viz_node); - } - - (params.create_edge_cb)( - node.archetype(), - *edge_comp_id, - VizoxideArchetypeGraphEdgeKind::Add, - viz_graph.create_edge( - viz_node_lookup.get(&id).unwrap(), - viz_node_lookup.get(&add_edge).unwrap(), - Some(&format!("Add {}", edge_comp_id.id())), - ), - ) - .build()?; - } - - if let Some(remove_edge) = edges.remove { - if !viz_node_lookup.contains_key(&remove_edge) { - let viz_node = self.create_vizoxide_archetype_graph_edge_node( - &viz_graph, - node, - remove_edge, - *edge_comp_id, - ¶ms, - )?; - - viz_node_lookup.insert(remove_edge, viz_node); - } - - (params.create_edge_cb)( - node.archetype(), - *edge_comp_id, - VizoxideArchetypeGraphEdgeKind::Remove, - viz_graph.create_edge( - viz_node_lookup.get(&id).unwrap(), - viz_node_lookup.get(&remove_edge).unwrap(), - Some(&format!("Remove {}", edge_comp_id.id())), - ), - ) - .build()?; - } - } - } - - drop(viz_node_lookup); - - Ok(viz_graph) - } - - fn create_vizoxide_archetype_graph_edge_node<'vizoxide_graph>( - &self, - viz_graph: &'vizoxide_graph vizoxide::Graph, - node: &graph::ArchetypeNode, - edge_id: ArchetypeId, - edge_comp_id: Uid, - params: &VizoxideArchetypeGraphParams, - ) -> Result<vizoxide::Node<'vizoxide_graph>, vizoxide::GraphvizError> - { - match self.graph.get_node_by_id(edge_id) { - Some(edge_node) => (params.create_node_cb)( - edge_node.archetype(), - ArchetypeMetadata { is_imaginary: false }, - viz_graph.create_node(&(params.create_node_name)( - edge_node.archetype(), - ArchetypeMetadata { is_imaginary: false }, - )), - ) - .build(), - None => { - let mut comp_ids = - node.archetype().component_ids_sorted().collect::<Vec<_>>(); - - let insert_index = comp_ids.partition_point(|cid| *cid <= edge_comp_id); - - comp_ids.insert(insert_index, edge_comp_id); - - let imaginary_edge_archetype = Archetype::new(edge_id, comp_ids); - - (params.create_node_cb)( - &imaginary_edge_archetype, - ArchetypeMetadata { is_imaginary: true }, - viz_graph.create_node(&(params.create_node_name)( - &imaginary_edge_archetype, - ArchetypeMetadata { is_imaginary: true }, - )), - ) - .build() - } - } - } -} - -#[cfg(feature = "vizoxide")] -pub struct VizoxideArchetypeGraphParams -{ - pub create_node_name: fn(&Archetype, ArchetypeMetadata) -> std::borrow::Cow<'_, str>, - pub create_node_cb: for<'storage, 'graph> fn( - &'storage Archetype, - ArchetypeMetadata, - vizoxide::NodeBuilder<'graph>, - ) -> vizoxide::NodeBuilder<'graph>, - pub create_edge_cb: for<'storage, 'graph> fn( - &'storage Archetype, - Uid, - VizoxideArchetypeGraphEdgeKind, - vizoxide::EdgeBuilder<'graph>, - ) -> vizoxide::EdgeBuilder<'graph>, -} - -#[cfg(feature = "vizoxide")] -#[derive(Debug, Clone)] -pub struct ArchetypeMetadata -{ - pub is_imaginary: bool, -} - -#[cfg(feature = "vizoxide")] -#[derive(Debug, Clone, Copy)] -pub enum VizoxideArchetypeGraphEdgeKind -{ - Add, - Remove, -} - -#[derive(Debug)] -pub struct ArchetypeRefIter<'storage, 'search_terms> -{ - storage: &'storage Storage, - pre_iter: Either<ArrayIter<ArchetypeId, 1>, VecIntoIter<ArchetypeId>>, - dfs_iter: ArchetypeAddEdgeDfsIter<'storage>, - search_terms: ArchetypeSearchTerms<'search_terms>, -} - -impl<'component_storage, 'search_terms> Iterator - for ArchetypeRefIter<'component_storage, 'search_terms> -{ - type Item = &'component_storage Archetype; - - fn next(&mut self) -> Option<Self::Item> - { - if let Some(pre_iter_archetype_id) = self.pre_iter.next() { - return Some( - self.storage - .get_archetype_by_id(pre_iter_archetype_id) - .expect("Archetype should exist"), - ); - } - - let archetype_id = loop { - match self.dfs_iter.streaming_find(|res| { - matches!( - res, - ArchetypeAddEdgeDfsIterResult::AddEdge { .. } - | ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { .. } - ) - })? { - ArchetypeAddEdgeDfsIterResult::AddEdge { - add_edge_archetype_id, - add_edge_component_id, - } => { - if self.search_terms.excluded_contains(add_edge_component_id) { - self.dfs_iter.pop(); - continue; - } - - break add_edge_archetype_id; - } - ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { - archetype, - add_edge_archetype_id, - add_edge_component_id, - } => { - if self.search_terms.excluded_contains(add_edge_component_id) { - continue; - } - - let mut add_edge_archetype_comps = - archetype.component_ids_sorted().collect::<Vec<_>>(); - - add_edge_archetype_comps.insert_at_partition_point_by_key( - add_edge_component_id, - |comp_id| *comp_id, - ); - - self.storage.imaginary_archetypes.borrow_mut().push( - ImaginaryArchetype { - id: add_edge_archetype_id, - component_ids: add_edge_archetype_comps - .iter() - .map(|comp_id| *comp_id) - .collect::<Vec<_>>(), - }, - ); - - let found = - self.find_edges_of_imaginary_archetype(&add_edge_archetype_comps); - - self.dfs_iter.push(( - BorrowedOrOwned::Owned(Archetype::new( - add_edge_archetype_id, - add_edge_archetype_comps.clone(), - )), - found.into_iter(), - )); - - continue; - } - _ => { - unreachable!(); - } - } - }; - - Some( - self.storage - .get_archetype_by_id(archetype_id) - .expect("Archetype should exist"), - ) - } -} - -impl ArchetypeRefIter<'_, '_> -{ - fn find_edges_of_imaginary_archetype( - &self, - imaginary_archetype_comps: &[Uid], - ) -> Vec<(Uid, ArchetypeEdges)> - { - self.storage - .find_all_archetype_with_comps(&ArchetypeSearchTerms { - required_components: imaginary_archetype_comps, - excluded_components: &[], - }) - .into_iter() - .filter_map(|found_id| { - let found_archetype = self.storage.get_archetype_by_id(found_id).unwrap(); - - if found_archetype.component_cnt() < imaginary_archetype_comps.len() + 1 { - return None; - } - - let unique_comp_id = found_archetype - .component_ids_sorted() - .find(|found_archetype_comp_id| { - !imaginary_archetype_comps.iter().any( - |imaginary_archetype_comp_id| { - *imaginary_archetype_comp_id == *found_archetype_comp_id - }, - ) - }) - .expect("Oh noooo"); - - let mut add_edge_comp_ids = imaginary_archetype_comps.to_vec(); - - add_edge_comp_ids - .insert_at_partition_point_by_key(unique_comp_id, |id| *id); - - let add_edge = ArchetypeId::new(&add_edge_comp_ids); - - Some(( - unique_comp_id, - ArchetypeEdges { add: Some(add_edge), remove: None }, - )) - }) - .collect::<Vec<_>>() - } -} - -#[derive(Debug, thiserror::Error)] -pub enum Error -{ - #[error("Entity with ID {0:?} already exists")] - EntityAlreadyExists(Uid), - - #[error("Entity with ID {0:?} does not exist")] - EntityDoesNotExist(Uid), - - #[error("Entity with ID {entity:?} already has component with ID {component:?}")] - ComponentAlreadyInEntity - { - entity: Uid, component: Uid - }, - - #[error("Entity with ID {entity:?} does not have component with ID {component:?}")] - ComponentNotFoundInEntity - { - entity: Uid, component: Uid - }, -} - -#[derive(Debug)] -struct ImaginaryArchetype -{ - id: ArchetypeId, - component_ids: Vec<Uid>, -} - -#[cfg(test)] -mod tests -{ - use crate::component::storage::archetype::Id as ArchetypeId; - use crate::component::storage::Storage; - use crate::uid::{Kind as UidKind, Uid}; - - #[test] - fn create_entity_works() - { - let mut new_storage = Storage::default(); - - let uid = Uid::new_unique(UidKind::Entity); - - new_storage.create_entity(uid).expect("Expected Ok"); - - let archetype_node = new_storage - .graph - .get_node_by_id(ArchetypeId::from_components_metadata(&[])) - .expect("Archetype for entities with no component doesn't exist"); - - assert_eq!(archetype_node.archetype().component_cnt(), 0); - assert_eq!(archetype_node.archetype().entity_cnt(), 1); - - assert_eq!( - new_storage.entity_archetype_lookup.get(&uid).copied(), - Some(ArchetypeId::from_components_metadata(&[])) - ); - } -} diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs deleted file mode 100644 index 5306cf9..0000000 --- a/ecs/src/component/storage/archetype.rs +++ /dev/null @@ -1,300 +0,0 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::slice::Iter as SliceIter; - -use hashbrown::HashMap; - -use crate::component::{Component, Metadata as ComponentMetadata}; -use crate::lock::Lock; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::HashMapExt; - -#[derive(Debug)] -pub struct Archetype -{ - id: Id, - entities: Vec<Entity>, - entity_index_lookup: HashMap<Uid, usize>, - component_index_lookup: HashMap<Uid, usize>, - component_ids: Vec<Uid>, -} - -impl Archetype -{ - pub fn new(id: Id, component_ids: impl AsRef<[Uid]>) -> Self - { - Self { - id, - entities: Vec::new(), - entity_index_lookup: HashMap::new(), - component_index_lookup: component_ids - .as_ref() - .iter() - .enumerate() - .map(|(index, id)| (*id, index)) - .collect(), - component_ids: component_ids.as_ref().to_vec(), - } - } - - pub fn id(&self) -> Id - { - self.id - } - - pub fn is_superset(&self, other: &Self) -> bool - { - self.component_index_lookup - .keys_is_superset(&other.component_index_lookup) - } - - pub fn is_subset(&self, other: &Self) -> bool - { - self.component_index_lookup - .keys_is_subset(&other.component_index_lookup) - } - - pub fn get_entity_by_id(&self, entity_uid: Uid) -> Option<&Entity> - { - let index = *self.entity_index_lookup.get(&entity_uid)?; - - Some(self.entities.get(index).unwrap_or_else(|| { - panic!( - "In invalid state! Index of entity with ID {entity_uid:?} is out of bounds" - ); - })) - } - - pub fn push_entity(&mut self, entity: Entity) - { - self.entity_index_lookup - .insert(entity.uid, self.entities.len()); - - self.entities.push(entity); - } - - pub fn remove_entity(&mut self, entity_uid: Uid) -> Option<Entity> - { - //debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - let entity_index = self.entity_index_lookup.remove(&entity_uid)?; - - if self.entities.len() == 1 { - return Some(self.entities.remove(entity_index)); - } - - let last_entity_uid = self - .entities - .last() - .expect(concat!( - "Invalid state. No entities in archetype but entry was ", - "removed successfully from entity index lookup" - )) - .uid; - - // By using swap_remove, no memory reallocation occurs and only one index in the - // entity lookup needs to be updated - let removed_entity = self.entities.swap_remove(entity_index); - - self.entity_index_lookup - .insert(last_entity_uid, entity_index); - - Some(removed_entity) - } - - pub fn entities(&self) -> EntityIter<'_> - { - EntityIter { iter: self.entities.iter() } - } - - pub fn entity_cnt(&self) -> usize - { - self.entities.len() - } - - pub fn component_cnt(&self) -> usize - { - self.component_index_lookup.len() - } - - pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize> - { - self.component_index_lookup.get(&component_id).copied() - } - - pub fn component_ids_unsorted(&self) -> impl Iterator<Item = Uid> + '_ - { - self.component_index_lookup.keys().copied() - } - - pub fn component_ids_sorted(&self) -> impl Iterator<Item = Uid> + '_ - { - self.component_ids.iter().copied() - } - - pub fn has_component_with_id(&self, component_id: Uid) -> bool - { - debug_assert_eq!(component_id.kind(), UidKind::Component); - - self.component_index_lookup.contains_key(&component_id) - } -} - -#[derive(Debug)] -pub struct EntityIter<'archetype> -{ - iter: SliceIter<'archetype, Entity>, -} - -impl<'archetype> Iterator for EntityIter<'archetype> -{ - type Item = &'archetype Entity; - - fn next(&mut self) -> Option<Self::Item> - { - self.iter.next() - } -} - -#[derive(Debug)] -pub struct Entity -{ - uid: Uid, - components: Vec<EntityComponent>, -} - -impl Entity -{ - pub fn new(uid: Uid, components: impl IntoIterator<Item = EntityComponent>) -> Self - { - Self { - uid, - components: components.into_iter().collect(), - } - } - - pub fn uid(&self) -> Uid - { - self.uid - } - - pub fn components(&self) -> &[EntityComponent] - { - &self.components - } - - pub fn remove_component(&mut self, component_id: Uid, archetype: &Archetype) - { - let index = archetype - .get_index_for_component(component_id) - .expect("Archetype should contain component"); - - self.components.remove(index); - } - - pub fn insert_component( - &mut self, - component_id: Uid, - component: EntityComponent, - archetype: &Archetype, - ) - { - let index = archetype - .get_index_for_component(component_id) - .expect("Archetype should contain component"); - - self.components.insert(index, component); - } -} - -#[derive(Debug)] -pub struct EntityComponent -{ - name: &'static str, - component: Lock<Box<dyn Component>>, -} - -impl EntityComponent -{ - pub fn new(component: Box<dyn Component>) -> Self - { - Self { - name: component.type_name(), - component: Lock::new(component), - } - } - - pub fn name(&self) -> &str - { - self.name - } - - pub fn component(&self) -> &Lock<Box<dyn Component>> - { - &self.component - } -} - -/// Archetype ID. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Id -{ - hash: u64, -} - -impl Id -{ - pub fn new(component_ids: &impl AsRef<[Uid]>) -> Self - { - if component_ids.as_ref().is_empty() { - return Self { hash: 0 }; - } - - debug_assert!( - component_ids.as_ref().is_sorted(), - "Cannot create archetype ID from unsorted component IDs" - ); - - let mut hasher = DefaultHasher::new(); - - for component_id in component_ids.as_ref() { - component_id.hash(&mut hasher); - } - - Self { hash: hasher.finish() } - } - - pub fn from_components_metadata<'a>( - components_metadata: impl IntoIterator<Item = &'a ComponentMetadata>, - ) -> Self - { - let mut hasher = DefaultHasher::new(); - - let mut prev_component_id: Option<Uid> = None; - - let mut comp_metadata_iter = components_metadata.into_iter().peekable(); - - if comp_metadata_iter.peek().is_none() { - return Self { hash: 0 }; - } - - for comp_metadata in comp_metadata_iter { - if prev_component_id - .is_some_and(|prev_comp_id| comp_metadata.id < prev_comp_id) - { - panic!( - "Cannot create archetype ID from a unsorted component metadata list" - ); - } - - prev_component_id = Some(comp_metadata.id); - - if comp_metadata.is_optional { - continue; - } - - comp_metadata.id.hash(&mut hasher); - } - - Self { hash: hasher.finish() } - } -} diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs deleted file mode 100644 index 11160e7..0000000 --- a/ecs/src/component/storage/graph.rs +++ /dev/null @@ -1,420 +0,0 @@ -use std::vec::IntoIter as VecIntoIter; - -use hashbrown::{HashMap, HashSet}; - -use crate::component::storage::archetype::{Archetype, Id as ArchetypeId}; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::{BorrowedOrOwned, StreamingIterator}; - -#[derive(Debug, Default)] -pub struct Graph -{ - nodes: Vec<ArchetypeNode>, - archetype_index_lookup: HashMap<ArchetypeId, usize>, -} - -impl Graph -{ - pub fn create_node(&mut self, id: ArchetypeId, component_ids: &impl AsRef<[Uid]>) - { - debug_assert!(!self.contains_archetype(id)); - - let _ = self.get_or_create_node(id, component_ids); - } - - pub fn get_or_create_node( - &mut self, - id: ArchetypeId, - component_ids: &impl AsRef<[Uid]>, - ) -> &mut ArchetypeNode - { - let exists_before = self.archetype_index_lookup.contains_key(&id); - - let index = *self.archetype_index_lookup.entry(id).or_insert_with(|| { - self.nodes.push(ArchetypeNode { - archetype: Archetype::new(id, component_ids.as_ref()), - edges: HashMap::new(), - }); - - self.nodes.len() - 1 - }); - - if !exists_before { - self.create_missing_edges(id); - } - - self.nodes - .get_mut(index) - .expect("Archetype index from lookup is out of bounds") - } - - pub fn contains_archetype(&self, id: ArchetypeId) -> bool - { - self.archetype_index_lookup.contains_key(&id) - } - - pub fn get_node_by_id(&self, id: ArchetypeId) -> Option<&ArchetypeNode> - { - let index = self.archetype_index_lookup.get(&id)?; - - Some(self.nodes.get(*index).unwrap_or_else(|| { - panic!("In invalid state! Index of archetype with ID {id:?} is out of bounds") - })) - } - - pub fn get_node_by_id_mut(&mut self, id: ArchetypeId) -> Option<&mut ArchetypeNode> - { - let index = self.archetype_index_lookup.get(&id)?; - - Some(self.nodes.get_mut(*index).unwrap_or_else(|| { - panic!("In invalid state! Index of archetype with ID {id:?} is out of bounds") - })) - } - - #[cfg(feature = "vizoxide")] - pub fn iter_nodes(&self) -> impl Iterator<Item = &ArchetypeNode> - { - self.nodes.iter() - } - - pub fn dfs_archetype_add_edges( - &self, - archetype_id: ArchetypeId, - ) -> Option<ArchetypeAddEdgeDfsIter> - { - let node = self.get_node_by_id(archetype_id)?; - - Some(ArchetypeAddEdgeDfsIter { - graph: self, - stack: vec![( - BorrowedOrOwned::Borrowned(node.archetype()), - node.edges - .iter() - .map(|(comp_id, edges)| (*comp_id, edges.clone())) - .collect::<Vec<_>>() - .into_iter(), - )], - visited: HashSet::new(), - }) - } - - fn create_missing_edges(&mut self, archetype_id: ArchetypeId) - { - let archetype_node_index = *self - .archetype_index_lookup - .get(&archetype_id) - .expect("Archetype should exist"); - - let (nodes_before, nodes_rest) = self.nodes.split_at_mut(archetype_node_index); - - let ([archetype_node], nodes_after) = nodes_rest.split_at_mut(1) else { - unreachable!(); - }; - - for other_archetype_node in nodes_before.iter_mut().chain(nodes_after.iter_mut()) - { - if archetype_node.archetype().component_cnt() - > other_archetype_node.archetype().component_cnt() - && other_archetype_node - .archetype() - .is_subset(archetype_node.archetype()) - { - Self::create_missing_subset_node_edges( - archetype_node, - other_archetype_node, - ); - - continue; - } - - if other_archetype_node - .archetype() - .is_superset(archetype_node.archetype()) - { - Self::create_missing_superset_node_edges( - archetype_node, - other_archetype_node, - ); - } - } - } - - fn create_missing_subset_node_edges( - target_node: &ArchetypeNode, - subset_node: &mut ArchetypeNode, - ) - { - let uniq_comp_id = target_node - .archetype() - .component_ids_sorted() - .find(|id| !subset_node.archetype().has_component_with_id(*id)) - .unwrap(); - - subset_node - .get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default) - .add = Some(subset_node.make_add_edge(uniq_comp_id).0); - } - - fn create_missing_superset_node_edges( - target_node: &mut ArchetypeNode, - superset_node: &mut ArchetypeNode, - ) - { - if superset_node.archetype().component_cnt() - > target_node.archetype().component_cnt() + 1 - { - let first_unique_comp_id = superset_node - .archetype() - .component_ids_sorted() - .find(|other_archetype_comp_id| { - !target_node - .archetype() - .has_component_with_id(*other_archetype_comp_id) - }) - .or_else(|| { - if target_node.archetype().component_cnt() != 0 { - return None; - } - - superset_node.archetype().component_ids_sorted().next() - }) - .expect("Not possible"); - - target_node - .get_or_insert_edges(first_unique_comp_id, ArchetypeEdges::default) - .add = Some(target_node.make_add_edge(first_unique_comp_id).0); - - return; - } - - if superset_node.archetype().component_cnt() - != target_node.archetype().component_cnt() + 1 - { - return; - } - - let extra_comp_id = superset_node - .archetype() - .component_ids_unsorted() - .find(|comp_id| !target_node.archetype().has_component_with_id(*comp_id)) - .expect("Archetype should contain one extra component ID"); - - superset_node - .get_or_insert_edges(extra_comp_id, ArchetypeEdges::default) - .remove = Some(target_node.archetype().id()); - - target_node - .get_or_insert_edges(extra_comp_id, ArchetypeEdges::default) - .add = Some(superset_node.archetype().id()); - } -} - -#[derive(Debug)] -pub struct ArchetypeNode -{ - archetype: Archetype, - edges: HashMap<Uid, ArchetypeEdges>, -} - -impl ArchetypeNode -{ - pub fn archetype(&self) -> &Archetype - { - &self.archetype - } - - pub fn archetype_mut(&mut self) -> &mut Archetype - { - &mut self.archetype - } - - pub fn get_or_insert_edges( - &mut self, - component_id: Uid, - insert_fn: impl FnOnce() -> ArchetypeEdges, - ) -> &mut ArchetypeEdges - { - debug_assert_eq!(component_id.kind(), UidKind::Component); - - self.edges.entry(component_id).or_insert_with(insert_fn) - } - - #[cfg(feature = "vizoxide")] - pub fn iter_edges(&self) -> impl Iterator<Item = (&Uid, &ArchetypeEdges)> - { - self.edges.iter() - } - - pub fn get_edges_mut(&mut self, component_id: Uid) -> Option<&mut ArchetypeEdges> - { - debug_assert_eq!(component_id.kind(), UidKind::Component); - - self.edges.get_mut(&component_id) - } - - pub fn make_add_edge(&self, component_id: Uid) -> (ArchetypeId, Vec<Uid>) - { - let mut edge_comp_ids = self - .archetype() - .component_ids_unsorted() - .chain([component_id]) - .collect::<Vec<_>>(); - - edge_comp_ids.sort(); - - let add_edge_id = ArchetypeId::new(&edge_comp_ids); - - (add_edge_id, edge_comp_ids) - } - - pub fn make_remove_edge(&self, component_id: Uid) -> (ArchetypeId, Vec<Uid>) - { - let mut edge_comp_ids = self - .archetype() - .component_ids_unsorted() - .filter(|id| *id != component_id) - .collect::<Vec<_>>(); - - edge_comp_ids.sort(); - - let remove_edge_id = ArchetypeId::new(&edge_comp_ids); - - (remove_edge_id, edge_comp_ids) - } -} - -#[derive(Debug, Default, Clone)] -pub struct ArchetypeEdges -{ - pub add: Option<ArchetypeId>, - pub remove: Option<ArchetypeId>, -} - -type ArchetypeAddEdgeDfsIterStackElem<'graph> = ( - BorrowedOrOwned<'graph, Archetype>, - VecIntoIter<(Uid, ArchetypeEdges)>, -); - -#[derive(Debug)] -pub struct ArchetypeAddEdgeDfsIter<'graph> -{ - graph: &'graph Graph, - stack: Vec<ArchetypeAddEdgeDfsIterStackElem<'graph>>, - visited: HashSet<ArchetypeId>, -} - -impl<'graph> ArchetypeAddEdgeDfsIter<'graph> -{ - pub fn new(graph: &'graph Graph, start_nodes: &[ArchetypeId]) -> Self - { - Self { - graph, - stack: start_nodes - .iter() - .map(|start_node_id| { - let start_node = graph - .get_node_by_id(*start_node_id) - .expect("Start node does not exist"); - - ( - BorrowedOrOwned::Borrowned(start_node.archetype()), - start_node - .edges - .iter() - .map(|(comp_id, edges)| (*comp_id, edges.clone())) - .collect::<Vec<_>>() - .into_iter(), - ) - }) - .collect(), - visited: start_nodes.iter().copied().collect::<HashSet<_>>(), - } - } - - pub fn push( - &mut self, - item: ( - BorrowedOrOwned<'graph, Archetype>, - VecIntoIter<(Uid, ArchetypeEdges)>, - ), - ) - { - self.stack.push(item); - } - - pub fn pop(&mut self) - { - self.stack.pop(); - } -} - -impl<'graph> StreamingIterator for ArchetypeAddEdgeDfsIter<'graph> -{ - type Item<'a> - = ArchetypeAddEdgeDfsIterResult<'graph, 'a> - where - Self: 'a; - - fn streaming_next(&mut self) -> Option<Self::Item<'_>> - { - let (_, edges_iter) = self.stack.last_mut()?; - - let Some((component_id, edges)) = edges_iter.next() else { - self.stack.pop(); - - return Some(ArchetypeAddEdgeDfsIterResult::NoEdgesLeftForArchetype); - }; - - let Some(add_edge) = edges.add else { - return Some(ArchetypeAddEdgeDfsIterResult::NoAddEdge); - }; - - if self.visited.contains(&add_edge) { - return Some(ArchetypeAddEdgeDfsIterResult::AddEdgeAlreadyVisited); - } - - self.visited.insert(add_edge); - - let Some(add_edge_archetype) = self.graph.get_node_by_id(add_edge) else { - return Some(ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { - archetype: &self.stack.last().unwrap().0, - add_edge_archetype_id: add_edge, - add_edge_component_id: component_id, - }); - }; - - self.stack.push(( - BorrowedOrOwned::Borrowned(add_edge_archetype.archetype()), - add_edge_archetype - .edges - .iter() - .map(|(comp_id, edges)| (*comp_id, edges.clone())) - .collect::<Vec<_>>() - .into_iter(), - )); - - Some(ArchetypeAddEdgeDfsIterResult::AddEdge { - add_edge_archetype_id: add_edge, - add_edge_component_id: component_id, - }) - } -} - -#[derive(Debug)] -pub enum ArchetypeAddEdgeDfsIterResult<'graph, 'iter> -{ - AddEdge - { - add_edge_archetype_id: ArchetypeId, - add_edge_component_id: Uid, - }, - NoEdgesLeftForArchetype, - NoAddEdge, - AddEdgeAlreadyVisited, - AddEdgeArchetypeNotFound - { - archetype: &'iter BorrowedOrOwned<'graph, Archetype>, - add_edge_archetype_id: ArchetypeId, - add_edge_component_id: Uid, - }, -} diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs deleted file mode 100644 index a43f9ce..0000000 --- a/ecs/src/entity.rs +++ /dev/null @@ -1,68 +0,0 @@ -use linkme::distributed_slice; - -use crate::component::storage::archetype::{Archetype, Entity as ArchetypeEntity}; -use crate::uid::Uid; -use crate::{EntityComponentRef, World}; - -/// A handle to a entity. -pub struct Handle<'a> -{ - archetype: &'a Archetype, - entity: &'a ArchetypeEntity, -} - -impl<'a> Handle<'a> -{ - /// Returns the [`Uid`] of this entity. - #[inline] - #[must_use] - pub fn uid(&self) -> Uid - { - self.entity.uid() - } - - #[inline] - #[must_use] - pub fn get_component(&self, component_uid: Uid) -> Option<EntityComponentRef<'a>> - { - let index = self.archetype.get_index_for_component(component_uid)?; - - Some(EntityComponentRef::new( - self.entity.components().get(index).unwrap(), - )) - } - - pub(crate) fn new(archetype: &'a Archetype, entity: &'a ArchetypeEntity) -> Self - { - Self { archetype, entity } - } -} - -#[allow(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! static_entity { - ($visibility: vis $ident: ident, $components: expr) => { - $visibility static $ident: ::std::sync::LazyLock<$crate::uid::Uid> = - ::std::sync::LazyLock::new(|| { - $crate::uid::Uid::new_unique($crate::uid::Kind::Entity) - }); - - $crate::private::paste::paste! { - mod [<__ecs_ $ident:lower _static_entity_priv>] { - use super::*; - - #[$crate::private::linkme::distributed_slice( - $crate::entity::CREATE_STATIC_ENTITIES - )] - #[linkme(crate=$crate::private::linkme)] - static CREATE_STATIC_ENTITY: fn(&$crate::World) = |world| { - world.create_entity_with_uid($components, *$ident); - }; - } - } - } -} - -#[distributed_slice] -#[doc(hidden)] -pub static CREATE_STATIC_ENTITIES: [fn(&World)]; diff --git a/ecs/src/event.rs b/ecs/src/event.rs deleted file mode 100644 index 9cea807..0000000 --- a/ecs/src/event.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod component; diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs deleted file mode 100644 index b4edffc..0000000 --- a/ecs/src/event/component.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Component events. - -use std::fmt::{Debug, Formatter}; -use std::marker::PhantomData; - -use ecs_macros::Component; - -use crate::component::Component; - -/// Event emitted when: -/// a) A entity with component `ComponentT` is spawned. -/// b) A component `ComponentT` is added to a entity. -#[derive(Clone, Component)] -pub struct Added<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Debug for Added<ComponentT> -where - ComponentT: Component, -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("Added") - .field("_pd", &self._pd) - .finish() - } -} - -impl<ComponentT> Default for Added<ComponentT> -where - ComponentT: Component, -{ - fn default() -> Self - { - Self { _pd: PhantomData } - } -} - -/// Event emitted when: -/// a) A `ComponentT` component is removed from a entity. -/// b) A entity with component `ComponentT` is despawned. -#[derive(Clone, Component)] -pub struct Removed<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Debug for Removed<ComponentT> -where - ComponentT: Component, -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("Removed") - .field("_pd", &self._pd) - .finish() - } -} - -impl<ComponentT> Default for Removed<ComponentT> -where - ComponentT: Component, -{ - fn default() -> Self - { - Self { _pd: PhantomData } - } -} - -/// Specifies a kind of component event UID. -#[derive(Debug, Clone, Copy)] -#[non_exhaustive] -pub enum Kind -{ - Removed, -} diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs deleted file mode 100644 index 42ebef9..0000000 --- a/ecs/src/extension.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::component::Sequence as ComponentSequence; -use crate::sole::Sole; -use crate::system::System; -use crate::uid::Uid; -use crate::{SoleAlreadyExistsError, World}; - -/// A collection of systems, entities & soles that can be added to a [`World`]. -pub trait Extension -{ - fn collect(self, collector: Collector<'_>); -} - -/// Passed to a [`Extension`] to collects it's systems, entities & soles. -pub struct Collector<'world> -{ - world: &'world mut World, -} - -impl<'world> Collector<'world> -{ - /// Returns a new `Collector` for the given [`World`]. - pub fn new(world: &'world mut World) -> Self - { - Self { world } - } - - /// Adds a system to the [`World`]. - pub fn add_system<'this, SystemImpl>( - &'this mut self, - phase_euid: Uid, - system: impl System<'this, SystemImpl>, - ) - { - self.world.register_system(phase_euid, system); - } - - /// Adds a entity to the [`World`]. - pub fn add_entity<Comps>(&mut self, components: Comps) - where - Comps: ComponentSequence, - { - self.world.create_entity(components); - } - - /// Adds a globally shared singleton value to the [`World`]. - /// - /// # Errors - /// Returns `Err` if this [`Sole`] has already been added. - pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - where - SoleT: Sole, - { - self.world.add_sole(sole) - } -} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs deleted file mode 100644 index 32d82bc..0000000 --- a/ecs/src/lib.rs +++ /dev/null @@ -1,748 +0,0 @@ -#![deny(clippy::all, clippy::pedantic)] - -use std::any::{type_name, TypeId}; -use std::cell::RefCell; -use std::fmt::Debug; -use std::mem::ManuallyDrop; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; - -use hashbrown::HashMap; - -use crate::actions::Action; -use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; -use crate::component::storage::Storage as ComponentStorage; -use crate::component::{Component, Sequence as ComponentSequence}; -use crate::entity::CREATE_STATIC_ENTITIES; -use crate::event::component::Kind as ComponentEventKind; -use crate::extension::{Collector as ExtensionCollector, Extension}; -use crate::lock::{Lock, WriteGuard}; -use crate::phase::{Phase, START as START_PHASE}; -use crate::query::flexible::Query as FlexibleQuery; -use crate::query::term::Without; -use crate::query::{ - Iter as QueryIter, - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, - Terms as QueryTerms, - TermsBuilderInterface, -}; -use crate::relationship::{ChildOf, DependsOn, Relationship}; -use crate::sole::Sole; -use crate::stats::Stats; -use crate::system::{System, SystemComponent}; -use crate::type_name::TypeName; -use crate::uid::{Kind as UidKind, Uid}; - -pub mod actions; -pub mod component; -pub mod entity; -pub mod event; -pub mod extension; -pub mod lock; -pub mod phase; -pub mod query; -pub mod relationship; -pub mod sole; -pub mod stats; -pub mod system; -pub mod tuple; -pub mod type_name; -pub mod uid; -pub mod util; - -#[doc(hidden)] -pub mod private; - -pub use ecs_macros::{Component, Sole}; - -pub use crate::query::Query; - -#[derive(Debug)] -pub struct World -{ - data: WorldData, - stop: AtomicBool, - is_first_tick: AtomicBool, -} - -impl World -{ - #[must_use] - pub fn new() -> Self - { - let mut world = Self { - data: WorldData::default(), - stop: AtomicBool::new(false), - is_first_tick: AtomicBool::new(false), - }; - - world.add_sole(Stats::default()).ok(); - - for create_static_entity in CREATE_STATIC_ENTITIES { - create_static_entity(&world); - } - - world - } - - /// Creates a new entity with the given components. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. - pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid - where - Comps: ComponentSequence, - { - let entity_uid = Uid::new_unique(UidKind::Entity); - - self.create_entity_with_uid(components, entity_uid); - - entity_uid - } - - #[tracing::instrument(skip_all)] - #[doc(hidden)] - pub fn create_entity_with_uid<Comps>(&self, components: Comps, entity_uid: Uid) - where - Comps: ComponentSequence, - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - { - let mut component_storage_lock = self.lock_component_storage_rw(); - - if let Err(err) = component_storage_lock.create_entity(entity_uid) { - tracing::warn!("Failed to create entity: {err}"); - return; - }; - - Self::add_entity_components( - entity_uid, - components.into_array(), - &mut component_storage_lock, - ); - } - - for added_event_id in Comps::added_event_ids() { - self.emit_event_by_id(added_event_id); - } - } - - /// Adds a globally shared singleton value. - /// - /// # Errors - /// Returns `Err` if this [`Sole`] has already been added. - pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - where - SoleT: Sole, - { - self.data.sole_storage.insert(sole) - } - - pub fn register_system<'this, SystemImpl>( - &'this mut self, - phase_euid: Uid, - system: impl System<'this, SystemImpl>, - ) - { - self.create_entity(( - SystemComponent { system: system.into_type_erased() }, - Relationship::<DependsOn, Phase>::new(phase_euid), - )); - } - - pub fn register_observer_system<'this, SystemImpl, Event>( - &'this mut self, - system: impl System<'this, SystemImpl>, - event: Event, - ) where - Event: Component, - { - self.create_entity::<(SystemComponent, Event)>(( - SystemComponent { system: system.into_type_erased() }, - event, - )); - } - - /// Adds a extensions. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. - pub fn add_extension(&mut self, extension: impl Extension) - { - let extension_collector = ExtensionCollector::new(self); - - extension.collect(extension_collector); - } - - pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> - where - FieldTerms: QueryTermWithFieldTuple, - FieldlessTerms: QueryTermWithoutFieldTuple, - { - Query::new(self) - } - - pub fn flexible_query<const MAX_TERM_CNT: usize>( - &self, - terms: QueryTerms<MAX_TERM_CNT>, - ) -> FlexibleQuery<'_, MAX_TERM_CNT> - { - FlexibleQuery::new(self, terms) - } - - /// Performs a single tick. - /// - /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn step(&self) -> StepResult - { - if self.stop.load(Ordering::Relaxed) { - return StepResult::Stop; - } - - if self - .is_first_tick - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - self.query_and_run_systems(*START_PHASE); - } - - self.perform_phases(); - - self.lock_component_storage_rw() - .create_imaginary_archetypes(); - - self.perform_queued_actions(); - - if self.stop.load(Ordering::Relaxed) { - return StepResult::Stop; - } - - let mut stats_lock = self - .data - .sole_storage - .get::<Stats>() - .expect("No stats sole found") - .write_nonblock() - .expect("Failed to aquire read-write stats sole lock"); - - let stats = stats_lock - .downcast_mut::<Stats>() - .expect("Casting stats sole to Stats type failed"); - - stats.current_tick += 1; - - StepResult::Continue - } - - /// Starts a loop which calls [`Self::step`] until the world is stopped. - /// - /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn start_loop(&self) - { - while let StepResult::Continue = self.step() {} - } - - #[cfg(feature = "vizoxide")] - pub fn create_vizoxide_archetype_graph( - &self, - name: impl AsRef<str>, - ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> - { - use std::borrow::Cow; - - use crate::component::storage::{ - VizoxideArchetypeGraphEdgeKind, - VizoxideArchetypeGraphParams, - }; - - let component_storage_lock = self - .data - .component_storage - .read_nonblock() - .expect("Failed to acquire read-only component storage lock"); - - component_storage_lock.create_vizoxide_archetype_graph( - name, - VizoxideArchetypeGraphParams { - create_node_name: |archetype, _| { - Cow::Owned(format!( - "[{}]", - archetype - .component_ids_sorted() - .into_iter() - .map(|comp_id| comp_id.id().to_string()) - .collect::<Vec<_>>() - .join(", ") - )) - }, - create_node_cb: |_archetype, archetype_metadata, node_builder| { - if archetype_metadata.is_imaginary { - return node_builder.attribute("shape", "ellipse"); - } - - node_builder.attribute("shape", "box") - }, - create_edge_cb: |_, _, edge_kind, edge_builder| { - edge_builder.attribute( - "color", - match edge_kind { - VizoxideArchetypeGraphEdgeKind::Add => "green", - VizoxideArchetypeGraphEdgeKind::Remove => "red", - }, - ) - }, - }, - ) - } - - fn query_and_run_systems(&self, phase_euid: Uid) - { - let system_comps_query = - self.query::<(&SystemComponent, &Relationship<DependsOn, Phase>), ()>(); - - let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| { - phase_rel - .target_uids() - .any(|target_uid| target_uid == phase_euid) - }); - - for (system_component, _) in system_iter { - // SAFETY: The world lives long enough - unsafe { - system_component.system.run(self); - } - } - } - - fn perform_child_phases(&self, parent_phase_euid: Uid) - { - let phase_query = self.query::<(&Phase, &Relationship<ChildOf, Phase>), ()>(); - - for (child_phase_euid, (_, phase_rel)) in phase_query.iter_with_euids() { - if !phase_rel - .target_uids() - .any(|phase_euid| phase_euid == parent_phase_euid) - { - continue; - } - - self.query_and_run_systems(child_phase_euid); - self.perform_child_phases(child_phase_euid); - } - } - - fn perform_phases(&self) - { - let phase_query = - self.query::<(&Phase,), (Without<Relationship<ChildOf, Phase>>,)>(); - - for (phase_euid, (_,)) in phase_query.iter_with_euids() { - if phase_euid == *START_PHASE { - continue; - } - - self.query_and_run_systems(phase_euid); - self.perform_child_phases(phase_euid); - } - } - - #[tracing::instrument(skip_all)] - fn perform_queued_actions(&self) - { - let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() - { - ActiveActionQueue::A => &self.data.action_queue.queue_a, - ActiveActionQueue::B => &self.data.action_queue.queue_b, - } - .write_nonblock() - .unwrap_or_else(|err| { - panic!( - "Failed to take read-write action queue lock {:?}: {err}", - self.data.action_queue.active_queue - ); - }); - - let mut has_swapped_active_queue = false; - - for action in active_action_queue.drain(..) { - match action { - Action::Spawn(components, component_added_event_ids) => { - { - let mut component_storage_lock = self.lock_component_storage_rw(); - - let new_entity_uid = Uid::new_unique(UidKind::Entity); - - if let Err(err) = - component_storage_lock.create_entity(new_entity_uid) - { - tracing::warn!("Failed to create entity: {err}"); - continue; - }; - - Self::add_entity_components( - new_entity_uid, - components, - &mut component_storage_lock, - ); - } - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); - } - } - Action::Despawn(entity_uid) => { - self.despawn_entity(entity_uid, &mut has_swapped_active_queue); - } - Action::AddComponents( - entity_uid, - components, - component_added_event_ids, - ) => { - { - let mut component_storage_lock = self.lock_component_storage_rw(); - - Self::add_entity_components( - entity_uid, - components, - &mut component_storage_lock, - ); - } - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - // TODO: Fix that events are emitted for components that haven't been - // added because a error occurred (for example, the entity already has - // the component) - for comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); - } - } - Action::RemoveComponents( - entity_uid, - components_metadata, - component_removed_event_ids, - ) => { - { - let mut component_storage_lock = self.lock_component_storage_rw(); - - Self::remove_entity_components( - entity_uid, - components_metadata - .iter() - .map(|comp_metadata| comp_metadata.id), - &mut component_storage_lock, - ); - } - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - // TODO: Fix that events are emitted for components that haven't been - // removed because a error occurred (for example, the entity does not - // have the component) - for comp_removed_event_id in component_removed_event_ids.ids { - self.emit_event_by_id(comp_removed_event_id); - } - } - Action::Stop => { - self.stop.store(true, Ordering::Relaxed); - } - } - } - } - - #[tracing::instrument(skip_all)] - fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool) - { - let mut component_storage_lock = self.lock_component_storage_rw(); - - let removed_entity = match component_storage_lock.remove_entity(entity_uid) { - Ok(components) => components, - Err(err) => { - tracing::error!("Failed to despawn entity: {err}"); - return; - } - }; - - let component_removed_event_uids = removed_entity - .components() - .iter() - .map(|component| { - component - .component() - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to acquire read-only {} component lock", - component.name() - ) - }) - .get_event_uid(ComponentEventKind::Removed) - }) - .collect::<Vec<_>>(); - - drop(component_storage_lock); - - if !*has_swapped_active_queue { - self.swap_event_queue(has_swapped_active_queue); - } - - for comp_removed_event_id in component_removed_event_uids { - self.emit_event_by_id(comp_removed_event_id); - } - } - - fn add_entity_components( - entity_uid: Uid, - components: impl IntoIterator<Item = (Uid, Box<dyn Component>)>, - component_storage: &mut ComponentStorage, - ) - { - for (component_id, component) in components { - if let Err(err) = component_storage - .add_entity_component(entity_uid, (component_id, component)) - { - tracing::error!("Failed to add component to entity: {err}"); - } - } - } - - fn remove_entity_components( - entity_uid: Uid, - component_ids: impl IntoIterator<Item = Uid>, - component_storage: &mut ComponentStorage, - ) - { - for component_id in component_ids { - if let Err(err) = - component_storage.remove_entity_component(entity_uid, component_id) - { - tracing::error!("Failed to remove component to entity: {err}"); - } - } - } - - fn emit_event_by_id(&self, event_id: Uid) - { - let query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([SystemComponent::id(), event_id]) - .build(), - ); - - for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { - unsafe { - system.system.run(self); - } - } - } - - fn swap_event_queue(&self, has_swapped_active_queue: &mut bool) - { - let mut active_queue = self.data.action_queue.active_queue.borrow_mut(); - - *active_queue = match *active_queue { - ActiveActionQueue::A => ActiveActionQueue::B, - ActiveActionQueue::B => ActiveActionQueue::A, - }; - - *has_swapped_active_queue = true; - } - - fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage> - { - self.data - .component_storage - .write_nonblock() - .expect("Failed to acquire read-write component storage lock") - } -} - -impl Default for World -{ - fn default() -> Self - { - Self::new() - } -} - -/// The result of calling [`World::step`]. -pub enum StepResult -{ - /// Another step can be made. - Continue, - - /// The world have been stopped so no step can be made again. - Stop, -} - -#[derive(Debug, Default)] -pub struct WorldData -{ - component_storage: Arc<Lock<ComponentStorage>>, - sole_storage: SoleStorage, - action_queue: Arc<ActionQueue>, -} - -#[derive(Debug)] -pub struct EntityComponentRef<'a> -{ - comp: &'a ArchetypeEntityComponent, -} - -impl<'a> EntityComponentRef<'a> -{ - pub fn component(&self) -> &'a Lock<Box<dyn Component>> - { - self.comp.component() - } - - fn new(comp: &'a ArchetypeEntityComponent) -> Self - { - Self { comp } - } -} - -#[derive(Debug, Default, Clone, Copy)] -enum ActiveActionQueue -{ - #[default] - A, - B, -} - -#[derive(Debug, Default)] -struct ActionQueue -{ - queue_a: Lock<Vec<Action>>, - queue_b: Lock<Vec<Action>>, - active_queue: RefCell<ActiveActionQueue>, -} - -impl ActionQueue -{ - fn push(&self, action: Action) - { - match *self.active_queue.borrow() { - ActiveActionQueue::A => self - .queue_a - .write_nonblock() - .expect("Failed to aquire read-write action queue A lock") - .push(action), - ActiveActionQueue::B => self - .queue_b - .write_nonblock() - .expect("Failed to aquire read-write action queue A lock") - .push(action), - } - } -} - -impl TypeName for ActionQueue -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} - -#[derive(Debug, thiserror::Error)] -#[error("Sole {0} already exists")] -pub struct SoleAlreadyExistsError(pub &'static str); - -#[derive(Debug)] -struct StoredSole -{ - sole: Arc<Lock<Box<dyn Sole>>>, - drop_last: bool, -} - -#[derive(Debug, Default)] -struct SoleStorage -{ - storage: HashMap<TypeId, ManuallyDrop<StoredSole>>, -} - -impl SoleStorage -{ - fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>> - { - self.storage - .get(&TypeId::of::<SoleT>()) - .map(|sole| &sole.sole) - } - - fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - { - let sole_type_id = TypeId::of::<SoleT>(); - - if self.storage.contains_key(&sole_type_id) { - return Err(SoleAlreadyExistsError(type_name::<SoleT>())); - } - - let drop_last = sole.drop_last(); - - // TODO: Reconsider this maybe? - #[allow(clippy::arc_with_non_send_sync)] - self.storage.insert( - sole_type_id, - ManuallyDrop::new(StoredSole { - sole: Arc::new(Lock::new(Box::new(sole))), - drop_last, - }), - ); - - Ok(()) - } -} - -impl Drop for SoleStorage -{ - fn drop(&mut self) - { - let mut soles_to_drop_last = Vec::new(); - - for sole in self.storage.values_mut() { - if sole.drop_last { - tracing::trace!( - "Sole {} pushed to dropping last queue", - sole.sole.read_nonblock().unwrap().type_name() - ); - - soles_to_drop_last.push(sole); - continue; - } - tracing::trace!( - "Dropping sole {}", - sole.sole.read_nonblock().unwrap().type_name() - ); - - unsafe { - ManuallyDrop::drop(sole); - } - } - - for sole in &mut soles_to_drop_last { - tracing::trace!( - "Dropping sole {} last", - sole.sole.read_nonblock().unwrap().type_name() - ); - - unsafe { - ManuallyDrop::drop(sole); - } - } - } -} diff --git a/ecs/src/lock.rs b/ecs/src/lock.rs deleted file mode 100644 index d6ed40e..0000000 --- a/ecs/src/lock.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::mem::forget; -use std::ops::{Deref, DerefMut}; - -use parking_lot::{ - MappedRwLockReadGuard, - MappedRwLockWriteGuard, - RwLock, - RwLockReadGuard, - RwLockWriteGuard, -}; - -use crate::type_name::TypeName; - -#[derive(Debug, Default)] -pub struct Lock<Value> -where - Value: TypeName, -{ - inner: RwLock<Value>, -} - -impl<Value> Lock<Value> -where - Value: TypeName, -{ - pub fn new(value: Value) -> Self - { - Self { inner: RwLock::new(value) } - } - - /// Tries to a acquire a handle to the resource with read access. - /// - /// # Errors - /// Returns `Err` if unavailable (A mutable handle is hold). - pub fn read_nonblock(&self) -> Result<ReadGuard<Value>, Error> - { - let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?; - - tracing::trace!("Acquired lock to value of type {}", guard.type_name()); - - Ok(ReadGuard { inner: guard }) - } - - /// Tries to a acquire a handle to the resource with mutable access. - /// - /// # Errors - /// Returns `Err` if unavailable (A mutable or immutable handle is hold). - pub fn write_nonblock(&self) -> Result<WriteGuard<Value>, Error> - { - let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?; - - tracing::trace!( - "Acquired mutable lock to value of type {}", - guard.type_name() - ); - - Ok(WriteGuard { inner: guard }) - } - - pub fn into_inner(self) -> Value - { - self.inner.into_inner() - } -} - -#[derive(Debug, thiserror::Error)] -pub enum Error -{ - #[error("Lock is unavailable for reading")] - ReadUnavailable, - - #[error("Lock is unavailable for writing")] - WriteUnavailable, -} - -#[derive(Debug)] -pub struct ReadGuard<'guard, Value> -where - Value: TypeName, -{ - inner: RwLockReadGuard<'guard, Value>, -} - -impl<'guard, Value> ReadGuard<'guard, Value> -where - Value: TypeName, -{ - pub fn map<NewValue>( - self, - func: impl FnOnce(&Value) -> &NewValue, - ) -> MappedReadGuard<'guard, NewValue> - where - NewValue: TypeName, - { - // The 'inner' field cannot be moved out of ReadGuard in a normal way since - // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&self.inner) }; - forget(self); - - MappedReadGuard { - inner: RwLockReadGuard::map(inner, func), - } - } -} - -impl<Value> Deref for ReadGuard<'_, Value> -where - Value: TypeName, -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> Drop for ReadGuard<'_, Value> -where - Value: TypeName, -{ - fn drop(&mut self) - { - tracing::trace!("Dropped lock to value of type {}", self.type_name()); - } -} - -#[derive(Debug)] -pub struct MappedReadGuard<'guard, Value> -where - Value: TypeName, -{ - inner: MappedRwLockReadGuard<'guard, Value>, -} - -impl<Value> Deref for MappedReadGuard<'_, Value> -where - Value: TypeName, -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> Drop for MappedReadGuard<'_, Value> -where - Value: TypeName, -{ - fn drop(&mut self) - { - tracing::trace!("Dropped mapped lock to value of type {}", self.type_name()); - } -} - -#[derive(Debug)] -pub struct WriteGuard<'guard, Value> -where - Value: TypeName, -{ - inner: RwLockWriteGuard<'guard, Value>, -} - -impl<'guard, Value> WriteGuard<'guard, Value> -where - Value: TypeName, -{ - pub fn map<NewValue>( - self, - func: impl FnOnce(&mut Value) -> &mut NewValue, - ) -> MappedWriteGuard<'guard, NewValue> - where - NewValue: TypeName, - { - // The 'inner' field cannot be moved out of ReadGuard in a normal way since - // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&self.inner) }; - forget(self); - - MappedWriteGuard { - inner: RwLockWriteGuard::map(inner, func), - } - } -} - -impl<Value> Deref for WriteGuard<'_, Value> -where - Value: TypeName, -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> DerefMut for WriteGuard<'_, Value> -where - Value: TypeName, -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -impl<Value> Drop for WriteGuard<'_, Value> -where - Value: TypeName, -{ - fn drop(&mut self) - { - tracing::trace!("Dropped mutable lock to value of type {}", self.type_name()); - } -} - -#[derive(Debug)] -pub struct MappedWriteGuard<'guard, Value> -where - Value: TypeName, -{ - inner: MappedRwLockWriteGuard<'guard, Value>, -} - -impl<Value> Deref for MappedWriteGuard<'_, Value> -where - Value: TypeName, -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> DerefMut for MappedWriteGuard<'_, Value> -where - Value: TypeName, -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -impl<Value> Drop for MappedWriteGuard<'_, Value> -where - Value: TypeName, -{ - fn drop(&mut self) - { - tracing::trace!( - "Dropped mapped mutable lock to value of type {}", - self.type_name() - ); - } -} diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs deleted file mode 100644 index b8660f2..0000000 --- a/ecs/src/phase.rs +++ /dev/null @@ -1,15 +0,0 @@ -use ecs_macros::Component; - -use crate::relationship::{ChildOf, Relationship}; -use crate::static_entity; - -#[derive(Debug, Default, Clone, Copy, Component)] -pub struct Phase; - -static_entity!(pub START, (Phase,)); - -static_entity!(pub PRE_UPDATE, (Phase,)); - -static_entity!(pub UPDATE, (Phase, <Relationship<ChildOf, Phase>>::new(*PRE_UPDATE))); - -static_entity!(pub PRESENT, (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE))); diff --git a/ecs/src/private.rs b/ecs/src/private.rs deleted file mode 100644 index 56a6552..0000000 --- a/ecs/src/private.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[doc(hidden)] -pub use {linkme, paste}; diff --git a/ecs/src/query.rs b/ecs/src/query.rs deleted file mode 100644 index b29db3d..0000000 --- a/ecs/src/query.rs +++ /dev/null @@ -1,498 +0,0 @@ -use std::any::type_name; -use std::marker::PhantomData; - -use seq_macro::seq; - -use crate::component::{Component, FromLockedOptional, Ref as ComponentRef}; -use crate::entity::Handle as EntityHandle; -use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; -use crate::system::{Param as SystemParam, System}; -use crate::uid::Uid; -use crate::util::array_vec::ArrayVec; -use crate::util::Array; -use crate::World; - -pub mod flexible; -pub mod term; - -#[derive(Debug)] -pub struct Query<'world, FieldTerms, FieldlessTerms = ()> -where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, -{ - world: &'world World, - // A term tuple type can have a maximum of 17 elements - inner: FlexibleQuery<'world, 17>, - _pd: PhantomData<(FieldTerms, FieldlessTerms)>, -} - -impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms> -where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, -{ - /// Iterates over the entities matching this query, the iterator item being the entity - /// components. - #[must_use] - pub fn iter<'query>( - &'query self, - ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> - { - tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - - Iter { - world: self.world, - iter: self.inner.iter(), - comps_pd: PhantomData, - } - } - - /// Iterates over the entities matching this query, the iterator item being the entity - /// [`Uid`] and the matching entity components. - #[must_use] - pub fn iter_with_euids<'query>( - &'query self, - ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> - { - tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - - ComponentAndEuidIter { - world: self.world, - iter: self.inner.iter(), - comps_pd: PhantomData, - } - } - - /// Iterates over the entities matching this query using the iterator returned by - /// `func`. - /// - /// This function exists so that a custom [`EntityHandle`] iterator can be given to - /// [`Iter`] without giving the user access to a reference to the [`World`]. - #[must_use] - pub fn iter_with<'query, OutIter>( - &'query self, - func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter, - ) -> Iter<'query, 'world, FieldTerms, OutIter> - where - OutIter: Iterator<Item = EntityHandle<'query>>, - { - tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - - Iter { - world: self.world, - iter: func(self.inner.iter()), - comps_pd: PhantomData, - } - } - - /// Returns the UID of the entity at the given query iteration index. - #[must_use] - pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid> - { - Some(self.inner.iter().nth(entity_index)?.uid()) - } - - pub(crate) fn new(world: &'world World) -> Self - { - let mut terms_builder = Terms::builder(); - - FieldTerms::apply_terms_to_builder(&mut terms_builder); - FieldlessTerms::apply_terms_to_builder(&mut terms_builder); - - Self { - world, - inner: world.flexible_query(terms_builder.build()), - _pd: PhantomData, - } - } -} - -impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator - for &'query Query<'world, FieldTerms, FieldlessTerms> -where - FieldTerms: TermWithFieldTuple + 'world, - FieldlessTerms: TermWithoutFieldTuple, -{ - type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; - type Item = FieldTerms::Fields<'query>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world> - for Query<'world, FieldTerms, FieldlessTerms> -where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, -{ - type Input = (); - - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self - { - Self::new(world) - } -} - -#[derive(Debug)] -pub struct Terms<const MAX_TERM_CNT: usize> -{ - required_components: ArrayVec<Uid, MAX_TERM_CNT>, - excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, -} - -impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT> -{ - pub fn builder() -> TermsBuilder<MAX_TERM_CNT> - { - TermsBuilder::default() - } -} - -#[derive(Debug, Default)] -pub struct TermsBuilder<const MAX_TERM_CNT: usize> -{ - required_components: ArrayVec<Uid, MAX_TERM_CNT>, - excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, -} - -pub trait TermsBuilderInterface -{ - fn with<ComponentT: Component>(self) -> Self; - - fn without<ComponentT: Component>(self) -> Self; - - fn with_required(self, ids: impl Array<Uid>) -> Self; -} - -macro_rules! impl_terms_builder { - ($($impl_content: tt)*) => { - impl<const MAX_TERM_CNT: usize> - TermsBuilderInterface for TermsBuilder<MAX_TERM_CNT> - { - $($impl_content)* - } - - impl<const MAX_TERM_CNT: usize> - TermsBuilderInterface for &mut TermsBuilder<MAX_TERM_CNT> - { - $($impl_content)* - } - }; -} - -impl_terms_builder! { - #[allow(unused_mut)] - fn with<ComponentT: Component>(mut self) -> Self - { - if ComponentT::is_optional() { - return self; - } - - let insert_index = self.required_components - .partition_point(|id| *id <= ComponentT::id()); - - self.required_components - .insert(insert_index, ComponentT::id()); - - self - } - - #[allow(unused_mut)] - fn without<ComponentT: Component>(mut self) -> Self - { - if ComponentT::is_optional() { - panic!( - "{}::without cannot take optional component", - type_name::<Self>() - ); - } - - let insert_index = self.excluded_components - .partition_point(|id| *id <= ComponentT::id()); - - self.excluded_components - .insert(insert_index, ComponentT::id()); - - self - } - - #[allow(unused_mut)] - fn with_required(mut self, mut ids: impl Array<Uid>) -> Self - { - if !ids.as_ref().is_sorted() { - ids.as_mut().sort(); - } - - if self.required_components.len() == 0 { - self.required_components.extend(ids); - return self; - } - - let mut id_iter = ids.into_iter(); - - while let Some(id) = id_iter.next() { - let insert_index = self.required_components - .partition_point(|other_id| *other_id <= id); - - if insert_index == self.required_components.len() { - self.required_components.extend([id].into_iter().chain(id_iter)); - - return self; - } - - self.required_components - .insert(insert_index, id); - - } - - self - } -} - -impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT> -{ - pub fn build(self) -> Terms<MAX_TERM_CNT> - { - assert!(self.required_components.is_sorted()); - assert!(self.excluded_components.is_sorted()); - - Terms { - required_components: self.required_components, - excluded_components: self.excluded_components, - } - } -} - -pub trait TermWithoutField -{ - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ); -} - -pub trait TermWithField -{ - type Field<'a>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ); - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world>; -} - -impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT -{ - type Field<'a> = ComponentRefT::Handle<'a>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with::<ComponentRefT::Component>(); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world> - { - Self::Field::from_locked_optional_component( - entity_handle - .get_component(ComponentRefT::Component::id()) - .map(|component| component.component()), - world, - ) - .unwrap_or_else(|err| { - panic!( - "Taking component {} lock failed: {err}", - type_name::<ComponentRefT::Component>() - ); - }) - } -} - -pub trait TermWithoutFieldTuple -{ - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ); -} - -pub trait TermWithFieldTuple -{ - type Fields<'component>; - - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ); - - fn get_fields<'component>( - entity_handle: &EntityHandle<'component>, - world: &'component World, - ) -> Self::Fields<'component>; -} - -pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, -{ - world: &'world World, - iter: EntityHandleIter, - comps_pd: PhantomData<FieldTerms>, -} - -impl<'query, 'world, FieldTerms, EntityHandleIter> - Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - /// Creates a new iterator from the given entity handle iterator. - /// - /// # Important - /// All of the yielded entities of the entity handle iterator should match the - /// terms `Terms`. The [`Self::next`] function will panic if it encounters a - /// entity that does not match the terms `Terms`. - pub fn new(world: &'world World, iter: EntityHandleIter) -> Self - { - Self { world, iter, comps_pd: PhantomData } - } -} - -impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - type Item = FieldTerms::Fields<'query>; - - fn next(&mut self) -> Option<Self::Item> - { - let entity_handle = self.iter.next()?; - - Some(FieldTerms::get_fields(&entity_handle, self.world)) - } -} - -pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, -{ - world: &'world World, - iter: EntityHandleIter, - comps_pd: PhantomData<FieldTerms>, -} - -impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - type Item = (Uid, FieldTerms::Fields<'query>); - - fn next(&mut self) -> Option<Self::Item> - { - let entity_handle = self.iter.next()?; - - Some(( - entity_handle.uid(), - FieldTerms::get_fields(&entity_handle, self.world), - )) - } -} - -macro_rules! impl_term_sequence { - ($c: tt) => { - seq!(I in 0..=$c { - impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*) - { - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT> - ) - { - #( - Term~I::apply_to_terms_builder(terms_builder); - )* - } - } - - impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*) - { - type Fields<'component> = (#(Term~I::Field<'component>,)*); - - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT> - ) - { - #( - Term~I::apply_to_terms_builder(terms_builder); - )* - } - - fn get_fields<'component>( - entity_handle: &EntityHandle<'component>, - world: &'component World, - ) -> Self::Fields<'component> - { - (#(Term~I::get_field(entity_handle, world),)*) - } - } - }); - }; -} - -seq!(C in 0..=16 { - impl_term_sequence!(C); -}); - -impl TermWithoutFieldTuple for () -{ - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - } -} - -impl TermWithFieldTuple for () -{ - type Fields<'component> = (); - - fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( - _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - } - - fn get_fields<'component>( - _entity_handle: &EntityHandle<'_>, - _world: &'component World, - ) -> Self::Fields<'component> - { - } -} diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs deleted file mode 100644 index 2f0b5e7..0000000 --- a/ecs/src/query/flexible.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! Low-level querying. -use std::iter::{repeat_n, FlatMap, RepeatN, Zip}; - -use crate::component::storage::archetype::{Archetype, EntityIter}; -use crate::component::storage::{ - ArchetypeRefIter, - ArchetypeSearchTerms, - Storage as ComponentStorage, -}; -use crate::entity::Handle as EntityHandle; -use crate::lock::ReadGuard; -use crate::query::Terms; -use crate::World; - -/// Low-level entity query structure. -#[derive(Debug)] -pub struct Query<'world, const MAX_TERM_CNT: usize> -{ - component_storage: ReadGuard<'world, ComponentStorage>, - terms: Terms<MAX_TERM_CNT>, -} - -impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> -{ - /// Iterates over the entities matching this query. - #[must_use] - pub fn iter(&self) -> Iter<'_> - { - Iter { - iter: self - .component_storage - .search_archetypes(ArchetypeSearchTerms { - required_components: &self.terms.required_components, - excluded_components: &self.terms.excluded_components, - }) - .flat_map( - (|archetype| { - repeat_n(archetype, archetype.entity_cnt()) - .zip(archetype.entities()) - }) as ComponentIterMapFn, - ), - } - } - - pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self - { - Self { - component_storage: world - .data - .component_storage - .read_nonblock() - .expect("Failed to acquire read-only component storage lock"), - terms, - } - } -} - -pub struct Iter<'query> -{ - iter: QueryEntityIter<'query>, -} - -impl<'query> Iterator for Iter<'query> -{ - type Item = EntityHandle<'query>; - - fn next(&mut self) -> Option<Self::Item> - { - let (archetype, entity) = self.iter.next()?; - - Some(EntityHandle::new(archetype, entity)) - } -} - -type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>; - -type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>; - -type QueryEntityIter<'query> = FlatMap< - ArchetypeRefIter<'query, 'query>, - ComponentIterMapFnOutput<'query>, - ComponentIterMapFn, ->; diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs deleted file mode 100644 index ce453f0..0000000 --- a/ecs/src/query/term.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::marker::PhantomData; - -use crate::component::Component; -use crate::query::{TermWithoutField, TermsBuilder, TermsBuilderInterface}; - -pub struct With<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> TermWithoutField for With<ComponentT> -where - ComponentT: Component, -{ - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with::<ComponentT>(); - } -} - -pub struct Without<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> TermWithoutField for Without<ComponentT> -where - ComponentT: Component, -{ - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.without::<ComponentT>(); - } -} diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs deleted file mode 100644 index 45fa265..0000000 --- a/ecs/src/relationship.rs +++ /dev/null @@ -1,466 +0,0 @@ -use std::any::type_name; -use std::marker::PhantomData; - -use ecs_macros::Component; - -use crate::component::storage::Storage as ComponentStorage; -use crate::component::{ - Component, - FromLockedOptional as FromLockedOptionalComponent, - Handle as ComponentHandle, - HandleMut as ComponentHandleMut, -}; -use crate::lock::{Error as LockError, Lock, ReadGuard}; -use crate::uid::{Kind as UidKind, Uid}; -use crate::World; - -/// A relationship to one or more targets. -#[derive(Debug, Component)] -#[component( - handle_type = Relation<'component, Kind, ComponentT>, - handle_mut_type = RelationMut<'component, Kind, ComponentT>, -)] -pub struct Relationship<Kind, ComponentT: Component> -where - Kind: 'static, -{ - entity_uid: SingleOrMultiple<Uid>, - _pd: PhantomData<(Kind, ComponentT)>, -} - -impl<Kind, ComponentT> Relationship<Kind, ComponentT> -where - ComponentT: Component, -{ - /// Creates a new `Relationship` with a single target. - #[must_use] - pub fn new(entity_uid: Uid) -> Self - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - Self { - entity_uid: SingleOrMultiple::Single(entity_uid), - _pd: PhantomData, - } - } - - /// Creates a new `Relationship` with multiple targets. - #[must_use] - pub fn new_multiple(entity_uids: impl IntoIterator<Item = Uid>) -> Self - { - let uids = entity_uids.into_iter().collect::<Vec<_>>(); - - for euid in &uids { - debug_assert_eq!(euid.kind(), UidKind::Entity); - } - - Self { - entity_uid: SingleOrMultiple::Multiple(uids), - _pd: PhantomData, - } - } -} - -pub struct RelationMut<'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - component_storage_lock: ReadGuard<'rel_comp, ComponentStorage>, - relationship_comp: ComponentHandleMut<'rel_comp, Relationship<Kind, ComponentT>>, -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp crate::lock::Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, LockError> - { - let relationship_comp_handle_from_locked_opt_comp = ComponentHandleMut::< - Relationship<Kind, ComponentT>, - >::from_locked_optional_component; - - let relationship_comp = - relationship_comp_handle_from_locked_opt_comp(optional_component, world)?; - - let component_storage_lock = world - .data - .component_storage - .read_nonblock() - .expect("Failed to aquire read-only component storage lock"); - - Ok(Self { - relationship_comp, - component_storage_lock, - }) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Option<RelationMut<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, crate::lock::Error> - { - optional_component - .map(|component| { - RelationMut::from_locked_optional_component(Some(component), world) - }) - .transpose() - } -} - -impl<'rel_comp, Kind, ComponentT> RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - /// Returns the component of the target at the specified index. - /// - /// # Panics - /// Will panic if the entity does not exist in the archetype it belongs to. This - /// should hopefully never happend. - #[must_use] - pub fn get(&self, index: usize) -> Option<ComponentHandleMut<'_, ComponentT>> - { - let target = self.get_target(index)?; - - let archetype = self.component_storage_lock.get_entity_archetype(*target)?; - - let entity = archetype - .get_entity_by_id(*target) - .expect("Target entity is gone from archetype"); - - let component_index = archetype.get_index_for_component(ComponentT::id())?; - - let component = ComponentHandleMut::new( - entity - .components() - .get(component_index)? - .component() - .write_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock of component {}", - type_name::<ComponentT>() - ) - }), - ); - - Some(component) - } - - /// Returns a reference to the target at the specified index. - #[must_use] - pub fn get_target(&self, index: usize) -> Option<&Uid> - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get(index), - SingleOrMultiple::Single(_) => None, - } - } - - /// Returns a mutable reference to the target at the specified index. - #[must_use] - pub fn get_target_mut(&mut self, index: usize) -> Option<&mut Uid> - { - match &mut self.relationship_comp.entity_uid { - SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get_mut(index), - SingleOrMultiple::Single(_) => None, - } - } - - /// Adds a target to the relationship. - pub fn add_target(&mut self, entity_uid: Uid) - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - match &mut self.relationship_comp.entity_uid { - SingleOrMultiple::Single(prev_entity_uid) => { - self.relationship_comp.entity_uid = - SingleOrMultiple::Multiple(vec![*prev_entity_uid, entity_uid]); - } - SingleOrMultiple::Multiple(entity_uids) => entity_uids.push(entity_uid), - } - } - - /// Removes a target to the relationship, returning it. - pub fn remove_target(&mut self, index: usize) -> Option<Uid> - { - match &mut self.relationship_comp.entity_uid { - SingleOrMultiple::Single(entity_uid) => { - let prev_entity_uid = *entity_uid; - - self.relationship_comp.entity_uid = - SingleOrMultiple::Multiple(Vec::new()); - - Some(prev_entity_uid) - } - SingleOrMultiple::Multiple(entity_uids) => { - if index >= entity_uids.len() { - return None; - } - - Some(entity_uids.remove(index)) - } - } - } - - #[must_use] - pub fn target_count(&self) -> usize - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(_) => 1, - SingleOrMultiple::Multiple(entity_uids) => entity_uids.len(), - } - } - - /// Returns a iterator of the components of the targets of this relationship. - #[must_use] - pub fn iter(&self) -> TargetComponentIterMut<'_, 'rel_comp, Kind, ComponentT> - { - TargetComponentIterMut { relation: self, index: 0 } - } -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> IntoIterator - for &'relationship RelationMut<'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - ComponentT: Component, -{ - type IntoIter = TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT>; - type Item = ComponentHandleMut<'rel_comp, ComponentT>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -/// Iterator of the components of the targets of a relationship. -pub struct TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - relation: &'relationship RelationMut<'rel_comp, Kind, ComponentT>, - index: usize, -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> Iterator - for TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - Kind: 'static, - ComponentT: Component, -{ - type Item = ComponentHandleMut<'rel_comp, ComponentT>; - - fn next(&mut self) -> Option<Self::Item> - { - let index = self.index; - - self.index += 1; - - self.relation.get(index) - } -} - -#[derive(Debug)] -enum SingleOrMultiple<Value> -{ - Single(Value), - Multiple(Vec<Value>), -} - -pub struct Relation<'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - component_storage_lock: ReadGuard<'rel_comp, ComponentStorage>, - relationship_comp: ComponentHandle<'rel_comp, Relationship<Kind, ComponentT>>, -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, LockError> - { - let relationship_comp_handle_from_locked_opt_comp = ComponentHandle::< - Relationship<Kind, ComponentT>, - >::from_locked_optional_component; - - let relationship_comp = - relationship_comp_handle_from_locked_opt_comp(optional_component, world)?; - - let component_storage_lock = world - .data - .component_storage - .read_nonblock() - .expect("Failed to aquire read-only component storage lock"); - - Ok(Self { - relationship_comp, - component_storage_lock, - }) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Option<Relation<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, crate::lock::Error> - { - optional_component - .map(|component| { - Relation::from_locked_optional_component(Some(component), world) - }) - .transpose() - } -} - -impl<'rel_comp, Kind, ComponentT> Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - /// Returns the component of the target at the specified index. - /// - /// # Panics - /// Will panic if the entity does not exist in the archetype it belongs to. This - /// should hopefully never happend. - #[must_use] - pub fn get(&self, index: usize) -> Option<ComponentHandle<'_, ComponentT>> - { - let target = self.get_target(index)?; - - let archetype = self.component_storage_lock.get_entity_archetype(*target)?; - - let entity = archetype - .get_entity_by_id(*target) - .expect("Target entity is gone from archetype"); - - let component_index = archetype.get_index_for_component(ComponentT::id())?; - - let component = ComponentHandle::new( - entity - .components() - .get(component_index)? - .component() - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock of component {}", - type_name::<ComponentT>() - ) - }), - ); - - Some(component) - } - - /// Returns a reference to the target at the specified index. - #[must_use] - pub fn get_target(&self, index: usize) -> Option<&Uid> - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get(index), - SingleOrMultiple::Single(_) => None, - } - } - - #[must_use] - pub fn target_count(&self) -> usize - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(_) => 1, - SingleOrMultiple::Multiple(entity_uids) => entity_uids.len(), - } - } - - pub fn target_uids(&self) -> impl Iterator<Item = Uid> + '_ - { - (0..self.target_count()) - .map_while(|target_index| self.get_target(target_index).copied()) - } - - /// Returns a iterator of the components of the targets of this relationship. - #[must_use] - pub fn iter(&self) -> TargetComponentIter<'_, 'rel_comp, Kind, ComponentT> - { - TargetComponentIter { relation: self, index: 0 } - } -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> IntoIterator - for &'relationship Relation<'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - ComponentT: Component, -{ - type IntoIter = TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT>; - type Item = ComponentHandle<'rel_comp, ComponentT>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -/// Iterator of the components of the targets of a relationship. -pub struct TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - relation: &'relationship Relation<'rel_comp, Kind, ComponentT>, - index: usize, -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> Iterator - for TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - Kind: 'static, - ComponentT: Component, -{ - type Item = ComponentHandle<'rel_comp, ComponentT>; - - fn next(&mut self) -> Option<Self::Item> - { - let index = self.index; - - self.index += 1; - - self.relation.get(index) - } -} - -/// Relationship kind denoting a dependency to another entity -#[derive(Debug, Default, Clone, Copy)] -pub struct DependsOn; - -/// Relationship kind denoting being the child of another entity. -#[derive(Debug, Default, Clone, Copy)] -pub struct ChildOf; diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs deleted file mode 100644 index 5af5ce3..0000000 --- a/ecs/src/sole.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::any::{type_name, Any}; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::sync::{Arc, Weak}; - -use crate::lock::{Lock, WriteGuard}; -use crate::system::{Param as SystemParam, System}; -use crate::type_name::TypeName; -use crate::World; - -/// A type which has a single instance and is shared globally. -pub trait Sole: Any + TypeName -{ - fn drop_last(&self) -> bool; - - fn as_any_mut(&mut self) -> &mut dyn Any; - - fn as_any(&self) -> &dyn Any; -} - -impl dyn Sole -{ - pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real> - { - self.as_any_mut().downcast_mut() - } - - pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real> - { - self.as_any().downcast_ref() - } -} - -impl Debug for dyn Sole -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - formatter.debug_struct("Sole").finish_non_exhaustive() - } -} - -impl TypeName for Box<dyn Sole> -{ - fn type_name(&self) -> &'static str - { - self.as_ref().type_name() - } -} - -/// Holds a reference to a globally shared singleton value. -#[derive(Debug)] -pub struct Single<'world, SoleT: Sole> -{ - sole: WriteGuard<'world, Box<dyn Sole>>, - sole_weak: Weak<Lock<Box<dyn Sole>>>, - _ph: PhantomData<SoleT>, -} - -impl<'world, SoleT> Single<'world, SoleT> -where - SoleT: Sole, -{ - /// Returns a struct which holds a weak reference to the [`World`] that `Single` - /// references and that can be used to aquire a new `Single` if the referenced - /// [`World`] is still alive. - #[must_use] - pub fn to_weak_ref(&self) -> SingleWeakRef<SoleT> - { - SingleWeakRef { - sole: self.sole_weak.clone(), - _ph: PhantomData, - } - } - - fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self - { - Self { - sole: sole.write_nonblock().unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock to single component {}", - type_name::<SoleT>() - ) - }), - sole_weak: Arc::downgrade(sole), - _ph: PhantomData, - } - } -} - -impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT> -where - SoleT: Sole, -{ - type Input = (); - - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self - { - let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| { - panic!("Sole {} was not found in world", type_name::<SoleT>()) - }); - - Self::new(sole) - } -} - -impl<SoleT> Deref for Single<'_, SoleT> -where - SoleT: Sole, -{ - type Target = SoleT; - - fn deref(&self) -> &Self::Target - { - self.sole.downcast_ref().unwrap() - } -} - -impl<SoleT> DerefMut for Single<'_, SoleT> -where - SoleT: Sole, -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - self.sole.downcast_mut().unwrap() - } -} - -#[derive(Debug, Clone)] -pub struct SingleWeakRef<SoleT> -where - SoleT: Sole, -{ - sole: Weak<Lock<Box<dyn Sole>>>, - _ph: PhantomData<SoleT>, -} - -impl<SoleT> SingleWeakRef<SoleT> -where - SoleT: Sole, -{ - /// Returns a struct which can be used to retrieve a [`Single`]. - /// - /// Returns [`None`] if the referenced [`World`] has been dropped. - #[must_use] - pub fn access(&self) -> Option<SingleRef<'_, SoleT>> - { - Some(SingleRef { - sole: self.sole.upgrade()?, - _pd: PhantomData, - }) - } -} - -/// Intermediate between [`Single`] and [`SingleWeakRef`]. Contains a strong reference to -/// a world which is not allowed direct access to. -#[derive(Debug, Clone)] -pub struct SingleRef<'weak_ref, SoleT> -where - SoleT: Sole, -{ - sole: Arc<Lock<Box<dyn Sole>>>, - _pd: PhantomData<&'weak_ref SoleT>, -} - -impl<SoleT> SingleRef<'_, SoleT> -where - SoleT: Sole, -{ - #[must_use] - pub fn to_single(&self) -> Single<'_, SoleT> - { - Single::new(&self.sole) - } -} diff --git a/ecs/src/stats.rs b/ecs/src/stats.rs deleted file mode 100644 index 56a5c32..0000000 --- a/ecs/src/stats.rs +++ /dev/null @@ -1,8 +0,0 @@ -use ecs_macros::Sole; - -#[derive(Debug, Default, Sole)] -#[non_exhaustive] -pub struct Stats -{ - pub current_tick: u64, -} diff --git a/ecs/src/system.rs b/ecs/src/system.rs deleted file mode 100644 index 603c015..0000000 --- a/ecs/src/system.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::any::Any; -use std::convert::Infallible; -use std::fmt::Debug; - -use ecs_macros::Component; -use seq_macro::seq; - -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::tuple::{ReduceElement as TupleReduceElement, Tuple}; -use crate::World; - -pub mod stateful; - -pub trait System<'world, Impl>: 'static -{ - type Input; - - #[must_use] - fn initialize(self, input: Self::Input) -> Self; - - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world; - - fn into_type_erased(self) -> TypeErased; - - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>>; - - fn set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ); -} - -macro_rules! impl_system { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*)> - for Func - where - Func: Fn(#(TParam~I,)*) + Copy + 'static, - #(TParam~I: Param<'world, Input = ()>,)* - { - type Input = Infallible; - - fn initialize(self, _input: Self::Input) -> Self - { - self - } - - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world - { - let func = *self; - - func(#({ - TParam~I::new(self, world) - },)*); - } - - fn into_type_erased(self) -> TypeErased - { - TypeErased { - data: Box::new(self), - run: Box::new(|data, world| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let data = unsafe { &*std::ptr::from_ref(data) }; - - let me = data - .downcast_ref::<Func>() - .expect("Function downcast failed"); - - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - me.run(world); - }), - } - } - - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>> - { - panic!("System does not have any local components"); - } - - fn set_local_component<LocalComponent: Component>( - &mut self, - _local_component: LocalComponent, - ) { - panic!("System does not have any local components"); - } - } - }); - }; -} - -seq!(C in 1..16 { - impl_system!(C); -}); - -pub trait Into<Impl> -{ - type System; - - fn into_system(self) -> Self::System; -} - -pub struct TypeErased -{ - data: Box<dyn Any>, - run: Box<TypeErasedRunFn>, -} - -impl TypeErased -{ - /// Runs the system. - /// - /// # Safety - /// `world_data` must live at least as long as the [`World`] the system belongs to. - pub unsafe fn run(&self, world: &World) - { - // You have to dereference for downcasting to work for some reason - let data = &*self.data; - - (self.run)(data, world); - } -} - -impl Debug for TypeErased -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - formatter.debug_struct("TypeErased").finish_non_exhaustive() - } -} - -/// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&dyn Any, &World); - -/// A parameter to a [`System`]. -pub trait Param<'world> -{ - type Input; - - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ); - - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self; -} - -/// A type which can be used as input to a [`System`]. -pub trait Input: 'static {} - -/// Component tuple reducing operation to get the parameters that takes input. -pub struct ParamWithInputFilter; - -impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> - for InputT -where - Accumulator: Tuple, -{ - type Return = Accumulator::WithElementAtEnd<Self>; -} - -impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () -{ - type Return = Accumulator; -} - -#[derive(Debug, Component)] -pub(crate) struct SystemComponent -{ - pub(crate) system: TypeErased, -} diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs deleted file mode 100644 index 9d911ee..0000000 --- a/ecs/src/system/stateful.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::any::{Any, TypeId}; -use std::panic::{RefUnwindSafe, UnwindSafe}; - -use hashbrown::HashMap; -use seq_macro::seq; - -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::lock::Lock; -use crate::system::{ - Into as IntoSystem, - Param, - ParamWithInputFilter, - System, - TypeErased, -}; -use crate::tuple::{ - Reduce as TupleReduce, - Tuple, - WithAllElemLtStatic as TupleWithAllElemLtStatic, -}; -use crate::uid::Uid; -use crate::World; - -/// A stateful system. -pub struct Stateful<Func> -{ - func: Func, - local_components: HashMap<Uid, Lock<Box<dyn Component>>>, -} - -macro_rules! impl_system { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)> - for Stateful<Func> - where - Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, - #(TParam~I: Param<'world>,)* - #(TParam~I::Input: 'static,)* - (#(TParam~I::Input,)*): TupleReduce< - ParamWithInputFilter, - Out: Tuple<InOptions: TupleWithAllElemLtStatic> - >, - { - type Input = - <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out; - - fn initialize(mut self, input: Self::Input) -> Self - { - let mut option_input = input.into_in_options(); - - let mut index = 0; - - #( - if TypeId::of::<TParam~I::Input>() != - TypeId::of::<()>() - { - let input = option_input - .get_mut::<Option<TParam~I::Input>>(index) - .expect("Input element index out of range") - .take() - .expect("Input element is already taken"); - - TParam~I::initialize( - &mut self, - input - ); - - #[allow(unused_assignments)] - { - index += 1; - } - } - )* - - self - } - - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world - { - let func = self.func; - - func(#({ - TParam~I::new(self, &world) - },)*); - } - - fn into_type_erased(self) -> TypeErased - { - TypeErased { - data: Box::new(self), - run: Box::new(|data, world| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let data = unsafe { &*std::ptr::from_ref::<dyn Any>(data) }; - - let me = data.downcast_ref::<Self>().unwrap(); - - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - me.run(world); - }), - } - } - - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>> - { - let local_component = self.local_components - .get(&LocalComponent::id())? - .write_nonblock() - .expect("Failed to aquire read-write local component lock"); - - Some(ComponentHandleMut::new(local_component)) - } - - fn set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ) - { - self.local_components - .insert( - LocalComponent::id(), - Lock::new(Box::new(local_component)) - ); - } - } - - impl<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)> - for Func - where - Func: Fn(#(TParam~I,)*) + Copy + 'static, - { - type System = Stateful<Func>; - - fn into_system(self) -> Self::System - { - Self::System { - func: self, - local_components: HashMap::new(), - } - } - } - }); - }; -} - -seq!(C in 1..16 { - impl_system!(C); -}); diff --git a/ecs/src/tuple.rs b/ecs/src/tuple.rs deleted file mode 100644 index def25a0..0000000 --- a/ecs/src/tuple.rs +++ /dev/null @@ -1,238 +0,0 @@ -use std::any::TypeId; - -use paste::paste; -use seq_macro::seq; -use util_macros::sub; - -pub trait Tuple: sealed::Sealed -{ - /// `Self` with the given type added as the last element. - /// - /// `(String, i32, u8)::WithElementAtEnd<Path> = (String, i32, u8, Path)` - /// - /// # Important note - /// If `Self` has 16 elements, this will be `()`. The reason for this is that the - /// `Tuple` trait is only implemented for tuples with up to and including 16 elements. - type WithElementAtEnd<NewElem>: Tuple; - - /// `Self` without the last element. - /// - /// `(u16, AtomicU8)::WithoutLastElement = (u16,)` - type WithoutLastElement: Tuple; - - /// The last element of `Self`. - type LastElement; - - /// Self with all elements wrapped in [`Option`]. - type InOptions: Tuple; - - /// Pops the last element from this tuple, returning the new tuple and the popped - /// element. - fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement); - - /// Converts this tuple so that all elements are wrapped in [`Option`]. - fn into_in_options(self) -> Self::InOptions; -} - -/// A tuple with element types that all have the lifetime `'static`. -pub trait WithAllElemLtStatic: Tuple + sealed::Sealed -{ - /// Returns the element at the given index. - fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>; -} - -/// Using the type system, reduces the elements of a tuple to a single one. Each element -/// determines itself how it is handled. -pub trait Reduce<Operation> -{ - type Out; -} - -pub trait ReduceElement<Accumulator, Operation> -{ - type Return; -} - -macro_rules! tuple_reduce_elem_tuple { - (overflow) => { - () - }; - - ($index: tt) => { - paste! { - [<Elem $index>]::Return - } - }; -} - -macro_rules! elem_type_by_index { - (overflow) => { - () - }; - - ($index: tt) => { - paste! { - [<Elem $index>] - } - }; -} - -macro_rules! elem_by_index { - (overflow) => { - () - }; - - ($index: tt, $self: ident) => { - $self.$index - }; -} - -macro_rules! all_except_last { - (start $($rest: tt)*) => { - all_except_last!(@[] $($rest)*) - }; - - (@[$($included_elem: ident,)*] $elem: ident $($rest: tt)+) => { - all_except_last!(@[$($included_elem,)* $elem,] $($rest)*) - }; - - (@[$($included_elem: expr,)*] ($elem: expr) $($rest: tt)+) => { - all_except_last!(@[$($included_elem,)* $elem,] $($rest)*) - }; - - (@[$($included_elem: ident,)*] $elem: ident) => { - ($($included_elem,)*) - }; - - (@[$($included_elem: expr,)*] $elem: expr) => { - ($($included_elem,)*) - }; - - (@[]) => { - () - }; -} - -macro_rules! impl_tuple_traits { - ($cnt: tt) => { - seq!(I in 0..$cnt { - impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*) - { - type WithElementAtEnd<NewElem> = (#(Elem~I,)* NewElem,); - - type WithoutLastElement = all_except_last!(start #(Elem~I)*); - - type LastElement = sub!($cnt - 1, elem_type_by_index); - - type InOptions = (#(Option<Elem~I>,)*); - - fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement) - { - ( - all_except_last!(start #((self.I))*), - sub!($cnt - 1, elem_by_index, (self)) - ) - } - - fn into_in_options(self) -> Self::InOptions - { - #![allow(clippy::unused_unit)] - (#(Some(self.I),)*) - } - } - - impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*) - { - fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element> - { - match index { - #( - I => { - assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>()); - - // SAFETY: It is checked above that the type is correct - Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() }) - } - )* - _ => None - } - } - } - - impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) - { - } - - paste! { - impl<Operation, #(Elem~I,)*> Reduce<Operation> for (#(Elem~I,)*) - where - #( - Elem~I: ReduceElement< - sub!(I - 1, tuple_reduce_elem_tuple), Operation - >, - )* - { - type Out = sub!($cnt - 1, tuple_reduce_elem_tuple); - } - } - }); - }; -} - -seq!(N in 0..16 { - impl_tuple_traits!(N); -}); - -seq!(I in 0..16 { - impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*) - { - type WithElementAtEnd<NewElem> = (); - - type WithoutLastElement = all_except_last!(start #(Elem~I)*); - - type LastElement = Elem15; - - type InOptions = (#(Option<Elem~I>,)*); - - fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement) - { - ( - all_except_last!(start #((self.I))*), - self.15 - ) - } - - fn into_in_options(self) -> Self::InOptions - { - #![allow(clippy::unused_unit)] - (#(Some(self.I),)*) - } - } - - impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*) - { - fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element> - { - match index { - #( - I => { - assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>()); - - // SAFETY: It is checked above that the type is correct - Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() }) - } - )* - _ => None - } - } - } - - impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) - { - } -}); - -mod sealed -{ - pub trait Sealed {} -} diff --git a/ecs/src/type_name.rs b/ecs/src/type_name.rs deleted file mode 100644 index 54179be..0000000 --- a/ecs/src/type_name.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::any::type_name; - -pub trait TypeName -{ - /// Returns the name of this type. - fn type_name(&self) -> &'static str; -} - -impl<Item> TypeName for Vec<Item> -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs deleted file mode 100644 index c3ed85b..0000000 --- a/ecs/src/uid.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::fmt::{Debug, Formatter}; -use std::mem::transmute; -use std::sync::atomic::{AtomicU32, Ordering}; - -use crate::util::{gen_mask_64, BitMask, NumberExt}; - -static NEXT: AtomicU32 = AtomicU32::new(1); - -const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63)); -const KIND_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(0..=1)); - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[repr(u8)] -pub enum Kind -{ - Entity = 2, - Component = 1, -} - -/// Unique entity/component ID. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Uid -{ - inner: u64, -} - -impl Uid -{ - /// Returns a new unique entity/component ID. - pub fn new_unique(kind: Kind) -> Self - { - let id = NEXT.fetch_add(1, Ordering::Relaxed); - - Self { - inner: ID_BITS.field_prep(u64::from(id)) | KIND_BITS.field_prep(kind as u64), - } - } - - #[must_use] - pub fn id(&self) -> u32 - { - let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else { - unreachable!("Uid id does not fit in u32"); - }; - - id - } - - #[must_use] - pub fn kind(&self) -> Kind - { - let Ok(kind) = u8::try_from(self.inner.field_get(KIND_BITS)) else { - unreachable!("Uid kind does not fit in u8"); - }; - - // SAFETY: The kind bits cannot be invalid since they are set using the Kind enum - // in the new_unique function - unsafe { transmute::<u8, Kind>(kind) } - } -} - -impl Debug for Uid -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("Uid") - .field("id", &self.id()) - .field("kind", &self.kind()) - .finish_non_exhaustive() - } -} diff --git a/ecs/src/util.rs b/ecs/src/util.rs deleted file mode 100644 index eb06ab4..0000000 --- a/ecs/src/util.rs +++ /dev/null @@ -1,333 +0,0 @@ -use std::hash::Hash; -use std::mem::transmute; -use std::ops::{BitAnd, Deref}; - -use hashbrown::HashMap; - -pub mod array_vec; - -pub trait VecExt<Item> -{ - fn insert_at_partition_point_by_key<Key>( - &mut self, - item: Item, - func: impl FnMut(&Item) -> Key, - ) where - Key: Ord; -} - -impl<Item> VecExt<Item> for Vec<Item> -{ - fn insert_at_partition_point_by_key<Key>( - &mut self, - item: Item, - mut func: impl FnMut(&Item) -> Key, - ) where - Key: Ord, - { - let key = func(&item); - - let insert_index = self.partition_point(|other_item| func(other_item) <= key); - - self.insert(insert_index, item); - } -} - -pub trait StreamingIterator -{ - type Item<'a> - where - Self: 'a; - - fn streaming_next(&mut self) -> Option<Self::Item<'_>>; - - fn streaming_map<NewItem, Func>(self, func: Func) -> StreamingMap<Self, Func> - where - Self: Sized, - Func: FnMut(Self::Item<'_>) -> NewItem, - { - StreamingMap { iter: self, func } - } - - fn streaming_find<'this, Predicate>( - &'this mut self, - mut predicate: Predicate, - ) -> Option<Self::Item<'this>> - where - Self: Sized, - Predicate: FnMut(&Self::Item<'this>) -> bool, - { - while let Some(item) = unsafe { - transmute::<Option<Self::Item<'_>>, Option<Self::Item<'_>>>( - self.streaming_next(), - ) - } { - if predicate(&item) { - return Some(item); - } - } - - None - } -} - -pub struct StreamingMap<Iter, Func> -{ - iter: Iter, - func: Func, -} - -impl<Iter, Func, Item> StreamingIterator for StreamingMap<Iter, Func> -where - Iter: StreamingIterator, - Func: FnMut(Iter::Item<'_>) -> Item, -{ - type Item<'a> - = Item - where - Iter: 'a, - Func: 'a; - - fn streaming_next(&mut self) -> Option<Self::Item<'_>> - { - Some((self.func)(self.iter.streaming_next()?)) - } -} - -#[derive(Debug)] -pub enum BorrowedOrOwned<'a, Value> -{ - Borrowned(&'a Value), - Owned(Value), -} - -impl<Value> Deref for BorrowedOrOwned<'_, Value> -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - match self { - Self::Borrowned(value) => value, - Self::Owned(value) => value, - } - } -} - -#[derive(Debug, Clone)] -pub enum Either<A, B> -{ - A(A), - B(B), -} - -impl<A, B> Iterator for Either<A, B> -where - A: Iterator, - B: Iterator<Item = A::Item>, -{ - type Item = A::Item; - - fn next(&mut self) -> Option<Self::Item> - { - match self { - Self::A(a) => a.next(), - Self::B(b) => b.next(), - } - } -} - -pub trait HashMapExt<Key, Value> -{ - /// Returns true if the keys are a subset of another [`HashMap`]'s keys, i.e., `other` - /// contains at least all the keys in `self`. - fn keys_is_subset(&self, other: &Self) -> bool; - - /// Returns true if the keys are a superset of another [`HashMap`]'s keys, i.e., - /// `self` contains at least all the keys in `other`. - fn keys_is_superset(&self, other: &Self) -> bool; -} - -impl<Key, Value> HashMapExt<Key, Value> for HashMap<Key, Value> -where - Key: Eq + Hash, -{ - fn keys_is_subset(&self, other: &Self) -> bool - { - if self.len() <= other.len() { - self.keys().all(|key| other.contains_key(key)) - } else { - false - } - } - - fn keys_is_superset(&self, other: &Self) -> bool - { - other.keys_is_subset(self) - } -} - -pub trait Array<Item>: - AsRef<[Item]> - + AsMut<[Item]> - + IntoIterator<Item = Item> - + Into<Vec<Item>> - + Sortable<Item = Item> - + sealed::Sealed -{ -} - -impl<Item, const CNT: usize> Array<Item> for [Item; CNT] {} - -impl<Item, const CNT: usize> sealed::Sealed for [Item; CNT] {} - -pub trait Sortable -{ - type Item; - - fn sort_by_key_b<Key, Func>(&mut self, func: Func) - where - Func: FnMut(&Self::Item) -> Key, - Key: Ord; -} - -impl<Item> Sortable for [Item] -{ - type Item = Item; - - fn sort_by_key_b<Key, Func>(&mut self, func: Func) - where - Func: FnMut(&Self::Item) -> Key, - Key: Ord, - { - self.sort_by_key(func); - } -} - -impl<Item, const LENGTH: usize> Sortable for [Item; LENGTH] -{ - type Item = Item; - - fn sort_by_key_b<Key, Func>(&mut self, func: Func) - where - Func: FnMut(&Self::Item) -> Key, - Key: Ord, - { - self.sort_by_key(func); - } -} - -impl<Item> Sortable for Vec<Item> -{ - type Item = Item; - - fn sort_by_key_b<Key, Func>(&mut self, func: Func) - where - Func: FnMut(&Self::Item) -> Key, - Key: Ord, - { - self.sort_by_key(func); - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BitMask<Value> -{ - mask: Value, -} - -impl BitMask<u64> -{ - #[must_use] - pub const fn new(mask: u64) -> Self - { - Self { mask } - } - - #[must_use] - pub const fn value(self) -> u64 - { - self.mask - } - - /// Prepares a bitfield value in the range of bits specified by this `BitMask`. - #[must_use] - pub const fn field_prep(self, field_value: u64) -> u64 - { - debug_assert!(field_value < 1 << self.mask.count_ones()); - - ((field_value) << self.mask.trailing_zeros()) & (self.mask) - } -} - -impl BitAnd<u64> for BitMask<u64> -{ - type Output = u64; - - fn bitand(self, rhs: u64) -> Self::Output - { - self.mask & rhs - } -} - -pub trait NumberExt: Sized -{ - /// Returns a range of bits (field) specified by the provided [`BitMask`]. - #[must_use] - fn field_get(self, field_mask: BitMask<Self>) -> Self; -} - -impl NumberExt for u64 -{ - fn field_get(self, field_mask: BitMask<Self>) -> Self - { - (field_mask & self) >> field_mask.value().trailing_zeros() - } -} - -macro_rules! gen_mask_64 { - ($low: literal..=$high: literal) => { - const { - if $high <= $low { - panic!("High bit index cannot be less than or equal to low bit index"); - } - - (((!0u64) - (1u64 << ($low)) + 1) - & (!0u64 >> (u64::BITS as u64 - 1 - ($high)))) - } - }; -} - -pub(crate) use gen_mask_64; - -mod sealed -{ - pub trait Sealed {} -} - -#[cfg(test)] -mod tests -{ - - use super::BitMask; - use crate::util::NumberExt; - - #[test] - fn field_get_works() - { - assert_eq!(0b11011u64.field_get(BitMask::new(0b11100)), 0b00110); - } - - #[test] - fn bitmask_field_prep_works() - { - assert_eq!(BitMask::new(0b11000).field_prep(3), 0b11000); - } - - #[test] - #[should_panic] - fn bitmask_field_prep_too_large_value_panics() - { - let _ = BitMask::new(0b001110).field_prep(9); - } -} diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs deleted file mode 100644 index 648c976..0000000 --- a/ecs/src/util/array_vec.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::mem::{transmute, MaybeUninit}; -use std::ops::{Deref, DerefMut}; - -#[derive(Debug)] -pub struct ArrayVec<Item, const CAPACITY: usize> -{ - items: [MaybeUninit<Item>; CAPACITY], - len: usize, -} - -impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY> -{ - pub fn new() -> Self - { - Self::default() - } - - #[inline] - #[must_use] - pub fn len(&self) -> usize - { - self.len - } - - pub fn push(&mut self, item: Item) - { - assert!(self.len < CAPACITY); - - self.items[self.len].write(item); - - self.len += 1; - } - - pub fn insert(&mut self, index: usize, item: Item) - { - assert!(index <= self.len); - assert!(self.len < CAPACITY); - - if index == self.len { - self.push(item); - return; - } - - unsafe { - std::ptr::copy( - &self.items[index], - &mut self.items[index + 1], - self.len - index, - ); - } - - self.items[index].write(item); - - self.len += 1; - } -} - -impl<Item, const CAPACITY: usize> Extend<Item> for ArrayVec<Item, CAPACITY> -{ - fn extend<IntoIter: IntoIterator<Item = Item>>(&mut self, iter: IntoIter) - { - for item in iter { - self.push(item); - } - } -} - -impl<Item, const CAPACITY: usize> AsRef<[Item]> for ArrayVec<Item, CAPACITY> -{ - fn as_ref(&self) -> &[Item] - { - unsafe { transmute::<&[MaybeUninit<Item>], &[Item]>(&self.items[..self.len]) } - } -} - -impl<Item, const CAPACITY: usize> AsMut<[Item]> for ArrayVec<Item, CAPACITY> -{ - fn as_mut(&mut self) -> &mut [Item] - { - unsafe { - transmute::<&mut [MaybeUninit<Item>], &mut [Item]>( - &mut self.items[..self.len], - ) - } - } -} - -impl<Item, const CAPACITY: usize> Deref for ArrayVec<Item, CAPACITY> -{ - type Target = [Item]; - - fn deref(&self) -> &Self::Target - { - self.as_ref() - } -} - -impl<Item, const CAPACITY: usize> DerefMut for ArrayVec<Item, CAPACITY> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - self.as_mut() - } -} - -impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY> -{ - fn default() -> Self - { - Self { - items: [const { MaybeUninit::uninit() }; CAPACITY], - len: 0, - } - } -} diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs deleted file mode 100644 index 0f02bd3..0000000 --- a/ecs/tests/query.rs +++ /dev/null @@ -1,322 +0,0 @@ -use ecs::component::Component; -use ecs::query::term::Without; -use ecs::query::{ - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, -}; -use ecs::uid::Uid; -use ecs::{Component, Query, World}; -use parking_lot::{Mutex, Once}; - -pub static SETUP: Once = Once::new(); - -pub static TEST_LOCK: Mutex<()> = Mutex::new(()); - -#[derive(Component)] -struct A; - -#[derive(Component)] -struct B; - -#[derive(Component)] -struct C; - -#[derive(Component)] -struct D; - -#[derive(Component)] -struct E; - -#[derive(Component)] -struct F; - -#[derive(Component)] -struct G; - -fn setup() -{ - SETUP.call_once_force(|_| { - assert_eq!(A::id().id(), 1); - assert_eq!(B::id().id(), 2); - assert_eq!(C::id().id(), 3); - assert_eq!(D::id().id(), 4); - assert_eq!(E::id().id(), 5); - assert_eq!(F::id().id(), 6); - assert_eq!(G::id().id(), 7); - }); -} - -fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>( - query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>, - mut expected_ent_ids: Vec<Uid>, -) where - QueryFieldTerms: QueryTermWithFieldTuple, - QueryFieldlessTerms: QueryTermWithoutFieldTuple, -{ - assert!( - query.iter_with_euids().all(|(ent_id, _)| { - let Some(index) = expected_ent_ids - .iter() - .position(|expected_id| *expected_id == ent_id) - else { - return false; - }; - - expected_ent_ids.remove(index); - - true - }), - "Unexpected entity was found. Expected entities left: {expected_ent_ids:?}" - ); - - assert_eq!( - expected_ent_ids.len(), - 0, - concat!( - "Not all entities expected to be found was found. ", - "Expected entities left: {:?}" - ), - expected_ent_ids - ); -} - -#[test] -fn query_archetype_exists_with_edges_to_next_archetypes() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C)); - let ent_2_id = world.create_entity((A, B, C, D, E)); - let ent_3_id = world.create_entity((A, B, C, E)); - let ent_4_id = world.create_entity((A, B, C, G, F)); - - assert_query_finds_ents( - world.query::<(&A, &B, &C), ()>(), - vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id], - ); -} - -#[test] -fn query_archetype_exists_with_2_comps_diff_to_next_archetype() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C, D, F)); - - let ent_2_id = world.create_entity((A, B, F)); - - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, F)); - - let ent_2_id = world.create_entity((A, B, C, D, F)); - - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_3_comps_diff_to_next_archetype() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C, D, E, F)); - - let ent_2_id = world.create_entity((A, B, F)); - - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, F)); - - let ent_2_id = world.create_entity((A, B, C, D, E, F)); - - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_4_comps_diff_to_next_archetype() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C, D, E, F, G)); - - let ent_2_id = world.create_entity((A, B, G)); - - assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, G)); - - let ent_2_id = world.create_entity((A, B, C, D, E, F, G)); - - assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); -} - -#[test] -fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C, D, E, F, G)); - - let ent_2_id = world.create_entity((A, B, G)); - - assert_query_finds_ents( - world.query::<(&A, &Option<E>, &G), ()>(), - vec![ent_1_id, ent_2_id], - ); -} - -#[test] -fn query_archetype_nonexistant() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - world.create_entity((A, B, C)); - - let ent_2_id = world.create_entity((A, B, C, D, E)); - let ent_3_id = world.create_entity((A, B, C, E)); - - world.create_entity((A, B, C, G, F)); - - assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]); -} - -#[test] -fn query_archetype_nonexistant_and_opt_comp() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - world.create_entity((A, B, C)); - let ent_2_id = world.create_entity((A, B, C, D, E)); - let ent_3_id = world.create_entity((A, B, C, E)); - world.create_entity((A, B, C, G, F)); - - assert_query_finds_ents( - world.query::<(&A, &E, &Option<D>), ()>(), - vec![ent_2_id, ent_3_id], - ); -} - -#[test] -fn query_without_comp_and_archetype_exists() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, B, C)); - - world.create_entity((A, B, C, E)); - world.create_entity((A, B, C, F, E)); - - let ent_2_id = world.create_entity((A, B, C, G)); - let ent_3_id = world.create_entity((A, B, C, G, F)); - - assert_query_finds_ents( - world.query::<(&A, &B, &C), (Without<E>,)>(), - vec![ent_1_id, ent_2_id, ent_3_id], - ); -} - -#[test] -fn query_without_required_comp_and_archetype_exists() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - world.create_entity((A, B, C)); - - world.create_entity((A, B, C, E)); - world.create_entity((A, B, C, F, E)); - - world.create_entity((A, B, C, G)); - world.create_entity((A, B, C, G, F)); - - assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]); -} - -#[test] -fn query_without_comp_and_archetype_nonexistant() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - world.create_entity((A, B, C)); - - let ent_1_id = world.create_entity((A, B, C, E)); - - world.create_entity((A, B, C, F, E)); - - let ent_2_id = world.create_entity((A, B, C, G, E)); - world.create_entity((A, B, C, G, F, E)); - - assert_query_finds_ents( - world.query::<(&A, &E), (Without<F>,)>(), - vec![ent_1_id, ent_2_id], - ); -} |
