diff options
| author | HampusM <hampus@hampusmat.com> | 2026-05-21 17:55:20 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-05-21 17:55:20 +0200 |
| commit | 8022e8998290b067b8aa0cb9cba8ba410826bdab (patch) | |
| tree | 7171e79ce530e03079046ee8fd12167160c45480 /ecs | |
| parent | 412cee02c252f91bcf0b70a3f5cc5fca6d2b4c62 (diff) | |
chore: rename ecs* crates to engine-ecs*
Diffstat (limited to 'ecs')
46 files changed, 0 insertions, 9142 deletions
diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml deleted file mode 100644 index 0a94ff0..0000000 --- a/ecs/Cargo.toml +++ /dev/null @@ -1,34 +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" -hashbrown = "0.15.2" -parking_lot = "0.12.3" -anyhow = "1.0.102" -backtrace = "0.3.76" -ecs-macros = { workspace = true } -util-macros = { workspace = true } -vizoxide = { version = "1.0.5", optional = true } - -[dev-dependencies.criterion] -version = "0.5.1" -default-features = false -features = ["cargo_bench_support"] - -[dev-dependencies.tracing-subscriber] -version = "0.3.17" -default-features = false -features = ["std", "ansi", "fmt", "smallvec", "env-filter", "chrono"] - -[[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/component_changed_event.rs b/ecs/examples/component_changed_event.rs deleted file mode 100644 index 2168df0..0000000 --- a/ecs/examples/component_changed_event.rs +++ /dev/null @@ -1,78 +0,0 @@ -use ecs::event::component::{Changed, EventMatchExt}; -use ecs::pair::Pair; -use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::system::observer::Observe; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct SomeData -{ - num: u64, -} - -#[derive(Component)] -struct Greeting -{ - greeting: String, -} - -fn say_hello(query: Query<(&SomeData, &mut Greeting)>) -{ - for (data, mut greeting) in &query { - println!("{}: {}", greeting.greeting, data.num); - - if greeting.greeting == "Good evening" { - greeting.greeting = "Good morning".to_string(); - greeting.set_changed(); - } - } -} - -fn print_changed_greetings(observe: Observe<'_, Pair<Changed, Greeting>>) -{ - println!("\nChanged greetings:"); - - for evt_match in &observe { - let greeting = evt_match.get_ent_target_comp(); - - println!("A greeting changed to {}", greeting.greeting); - } - - println!(""); -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*UPDATE_PHASE, say_hello); - - world.register_observer(print_changed_greetings); - - 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(); - - world.step(); - - for (mut greeting,) in &world.query::<(&mut Greeting,), ()>() { - if greeting.greeting == "Good afternoon" { - greeting.greeting = "Yo yo".to_string(); - greeting.set_changed(); - } - } - - world.step(); - - world.step(); -} diff --git a/ecs/examples/component_events.rs b/ecs/examples/component_events.rs deleted file mode 100644 index 06e7fab..0000000 --- a/ecs/examples/component_events.rs +++ /dev/null @@ -1,64 +0,0 @@ -use ecs::actions::Actions; -use ecs::component::Component; -use ecs::event::component::{Changed, EventMatchExt, Removed}; -use ecs::pair::Pair; -use ecs::phase::UPDATE; -use ecs::system::observer::Observe; -use ecs::{Component, Query, World}; - -#[derive(Debug, Component)] -struct CheeseCrumbs -{ - cnt: usize, -} - -#[derive(Debug, Component)] -struct Cheese -{ - name: &'static str, -} - -fn eat_cheese(query: Query<(&Cheese, &mut CheeseCrumbs)>, mut actions: Actions) -{ - for (cheese_ent_id, (_, mut cheese_crumbs)) in query.iter_with_euids() { - println!("Eating cheese!"); - - cheese_crumbs.cnt += 40; - cheese_crumbs.set_changed(); - - actions.remove_components(cheese_ent_id, [Cheese::id()]); - } -} - -fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>) -{ - for evt_match in &observe { - let cheese = evt_match.get_ent_target_comp(); - - println!("{} cheese was eaten", cheese.name); - } -} - -fn on_cheese_crumbs_changed(observe: Observe<Pair<Changed, CheeseCrumbs>>) -{ - for evt_match in &observe { - let cheese_crumbs = evt_match.get_ent_target_comp(); - - println!("Cheese crumbs count changed to {}", cheese_crumbs.cnt); - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*UPDATE, eat_cheese); - world.register_observer(on_cheese_removed); - world.register_observer(on_cheese_crumbs_changed); - - world.create_entity((Cheese { name: "Brie" }, CheeseCrumbs { cnt: 0 })); - world.create_entity((Cheese { name: "Parmesan" }, CheeseCrumbs { cnt: 0 })); - world.create_entity((Cheese { name: "Gouda" }, CheeseCrumbs { cnt: 0 })); - - world.step(); -} diff --git a/ecs/examples/component_relationship.rs b/ecs/examples/component_relationship.rs deleted file mode 100644 index e07b214..0000000 --- a/ecs/examples/component_relationship.rs +++ /dev/null @@ -1,65 +0,0 @@ -use ecs::pair::Pair; -use ecs::phase::START as START_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct Person -{ - name: String, -} - -fn print_dog_likers(query: Query<(&Person, Pair<Likes, &Dogs>)>) -{ - for (person, liked_dogs) in &query { - println!( - "{} likes {} dogs!", - person.name, - if liked_dogs.large { "large" } else { "small" }, - ); - } -} - -#[derive(Component)] -struct Likes; - -#[derive(Component)] -struct Cats; - -#[derive(Component)] -struct Dogs -{ - large: bool, -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*START_PHASE, print_dog_likers); - - world.create_entity(( - Person { name: "Irving".to_string() }, - Pair::builder() - .relation::<Likes>() - .target_as_data(Dogs { large: true }) - .build(), - )); - - world.create_entity(( - Person { name: "Mark".to_string() }, - Pair::builder() - .relation::<Likes>() - .target_as_data(Cats) - .build(), - )); - - world.create_entity(( - Person { name: "Helena".to_string() }, - Pair::builder() - .relation::<Likes>() - .target_as_data(Dogs { large: false }) - .build(), - )); - - world.step(); -} diff --git a/ecs/examples/component_removed_event.rs b/ecs/examples/component_removed_event.rs deleted file mode 100644 index 9b73b1a..0000000 --- a/ecs/examples/component_removed_event.rs +++ /dev/null @@ -1,46 +0,0 @@ -use ecs::actions::Actions; -use ecs::component::Component; -use ecs::event::component::{EventMatchExt, Removed}; -use ecs::pair::Pair; -use ecs::phase::UPDATE; -use ecs::system::observer::Observe; -use ecs::{Component, Query, World}; - -#[derive(Debug, Component)] -struct Cheese -{ - name: &'static str, -} - -fn eat_cheese(query: Query<(&Cheese,)>, mut actions: Actions) -{ - for (cheese_ent_id, (_,)) in query.iter_with_euids() { - println!("Eating cheese!"); - - actions.remove_components(cheese_ent_id, [Cheese::id()]); - } -} - -fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>) -{ - for evt_match in &observe { - let cheese = evt_match.get_ent_target_comp(); - - println!("{} cheese was eaten", cheese.name); - } -} - -fn main() -{ - let mut world = World::new(); - - world.register_system(*UPDATE, eat_cheese); - world.register_observer(on_cheese_removed); - - world.create_entity((Cheese { name: "Brie" },)); - world.create_entity((Cheese { name: "Parmesan" },)); - world.create_entity((Cheese { name: "Gouda" },)); - - world.step(); - world.step(); -} diff --git a/ecs/examples/error_handling.rs b/ecs/examples/error_handling.rs deleted file mode 100644 index dc34c5f..0000000 --- a/ecs/examples/error_handling.rs +++ /dev/null @@ -1,79 +0,0 @@ -use ecs::error::Error; -use ecs::event::component::{Changed, EventMatchExt}; -use ecs::pair::Pair; -use ecs::phase::UPDATE; -use ecs::query::Query; -use ecs::system::observer::Observe; -use ecs::{Component, World, error}; -use tracing::level_filters::LevelFilter; -use tracing_subscriber::EnvFilter; -use tracing_subscriber::fmt::time::ChronoLocal; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; - -#[derive(Component)] -struct State -{ - value: u32, -} - -fn do_something_fallible(query: Query<(&mut State,)>) -> Result<(), Error> -{ - for (mut state,) in &query { - state.value += 1; - - state.set_changed(); - - if state.value > 3 { - return Err(error!("Invalid state value {}", state.value)); - } - } - - Ok(()) -} - -fn handle_state_changed(observe: Observe<Pair<Changed, State>>) -> Result<(), Error> -{ - for evt_match in &observe { - let state = evt_match.get_ent_target_comp(); - - if state.value > 3 { - return Err(error!("Invalid state value {}", state.value)); - } - - tracing::info!("State has valid value {}", state.value); - } - - Ok(()) -} - -fn main() -{ - tracing_subscriber::registry() - .with( - tracing_subscriber::fmt::layer() - .with_timer(ChronoLocal::new("%T%.6f".to_string())), - ) - .with( - EnvFilter::builder() - .with_default_directive(LevelFilter::DEBUG.into()) - .from_env() - .unwrap(), - ) - .init(); - - let mut world = World::new(); - - world.set_err_handler(ecs::error::err_handler_log_error); - - world.create_entity((State { value: 0 },)); - - world.register_system(*UPDATE, do_something_fallible); - - world.register_observer(handle_state_changed); - - world.step(); - world.step(); - world.step(); - world.step(); -} diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs deleted file mode 100644 index bec2c00..0000000 --- a/ecs/examples/event_loop.rs +++ /dev/null @@ -1,120 +0,0 @@ -use ecs::actions::Actions; -use ecs::pair::{ChildOf, Pair}; -use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::{declare_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); - } -} - -declare_entity!( - SHEER_PHASE, - ( - Phase, - Pair::builder() - .relation::<ChildOf>() - .target_id(*UPDATE_PHASE) - .build() - ) -); - -declare_entity!( - FEED_PHASE, - ( - Phase, - Pair::builder() - .relation::<ChildOf>() - .target_id(*SHEER_PHASE) - .build() - ) -); - -declare_entity!( - AGE_PHASE, - ( - Phase, - Pair::builder() - .relation::<ChildOf>() - .target_id(*FEED_PHASE) - .build() - ) -); - -fn main() -{ - let mut world = World::new(); - - world.create_declared_entity(&SHEER_PHASE); - world.create_declared_entity(&FEED_PHASE); - world.create_declared_entity(&AGE_PHASE); - - 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 ebc9115..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 4e94151..0000000 --- a/ecs/examples/relationship.rs +++ /dev/null @@ -1,56 +0,0 @@ -use ecs::pair::{Pair, Wildcard}; -use ecs::phase::START as START_PHASE; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct Sword -{ - attack_strength: u32, -} - -#[derive(Component)] -struct Player; - -#[derive(Component)] -struct Health -{ - health: u32, -} - -#[derive(Component)] -struct Holding; - -fn print_player_stats(player_query: Query<(&Player, &Health, Pair<Holding, Wildcard>)>) -{ - for (_, health, target_sword) in &player_query { - println!("Player health: {}", health.health); - - if let Some(sword_ent) = target_sword.get_target_ent() { - let sword = sword_ent - .get::<Sword>() - .expect("Sword entity is missing sword component"); - - 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 }, - Pair::builder() - .relation::<Holding>() - .target_id(sword_uid) - .build(), - )); - - 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 7a36d0e..0000000 --- a/ecs/examples/with_local.rs +++ /dev/null @@ -1,70 +0,0 @@ -use ecs::component::local::Local; -use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::system::initializable::Initializable; -use ecs::system::Into; -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 7e89b0a..0000000 --- a/ecs/examples/with_sole.rs +++ /dev/null @@ -1,61 +0,0 @@ -use ecs::pair::{ChildOf, Pair}; -use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::sole::Single; -use ecs::{declare_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); -} - -declare_entity!( - PRINT_AMMO_COUNT_PHASE, - ( - Phase, - Pair::builder() - .relation::<ChildOf>() - .target_id(*UPDATE_PHASE) - .build() - ) -); - -fn main() -{ - let mut world = World::new(); - - world.create_declared_entity(&PRINT_AMMO_COUNT_PHASE); - - 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 3d8afe6..0000000 --- a/ecs/src/actions.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence}; -use crate::event::component::Removed; -use crate::pair::Pair; -use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; -use crate::uid::{Kind as UidKind, Uid, WithUidTuple}; -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, - world: Option<&'world World>, -} - -impl Actions<'_> -{ - /// Queues up a entity to spawn at the end of the current tick, returning the [`Uid`] - /// that the entity will have. - pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps) -> Uid - { - let new_entity_uid = Uid::new_unique(UidKind::Entity); - - self.action_queue.push(Action::Spawn( - new_entity_uid, - components.into_parts_array().into(), - )); - - new_entity_uid - } - - /// 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); - - let Some(world) = self.world else { - self.action_queue.push(Action::Despawn(entity_uid)); - return; - }; - - let Some(ent) = world.get_entity(entity_uid) else { - tracing::warn!("Cannot entity that doesn't exist"); - return; - }; - - // TODO: Submit all events with a single function call to reduce overhead - for comp_id in ent.component_ids() { - if comp_id.kind() == UidKind::Pair { - continue; - } - - world.event_submitter().submit_event( - &Pair::builder() - .relation::<Removed>() - .target_id(comp_id) - .build(), - entity_uid, - ); - } - - 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_parts_array().into(), - )); - } - - /// Queues up removing component(s) from a entity at the end of the current tick. - #[tracing::instrument(skip(self, component_ids))] - pub fn remove_components( - &mut self, - entity_uid: Uid, - component_ids: impl IntoIterator<Item = Uid>, - ) - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - let mut component_ids = component_ids.into_iter().peekable(); - - if component_ids.peek().is_none() { - return; - } - - let Some(world) = self.world else { - self.action_queue.push(Action::RemoveComponents( - entity_uid, - component_ids.collect(), - )); - return; - }; - - let Some(ent) = world.get_entity(entity_uid) else { - tracing::warn!("Cannot remove components from entity that doesn't exist"); - return; - }; - - let component_ids = component_ids - .filter(|comp_id| ent.has_component(*comp_id)) - .collect::<Vec<_>>(); - - if component_ids.is_empty() { - return; - } - - // TODO: Submit all events with a single function call to reduce overhead - for comp_id in &component_ids { - if comp_id.kind() == UidKind::Pair { - continue; - } - - world.event_submitter().submit_event( - &Pair::builder() - .relation::<Removed>() - .target_id(*comp_id) - .build(), - entity_uid, - ); - } - - self.action_queue - .push(Action::RemoveComponents(entity_uid, component_ids)); - } - - /// Queues up removing component(s) from a entity at the end of the current tick. - pub fn remove_comps<Ids: WithUidTuple>(&mut self, entity_uid: Uid) - { - self.remove_components(entity_uid, Ids::uids()); - } - - /// 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); - } -} - -impl<'world> SystemParam<'world> for Actions<'world> -{ - type Input = (); - - fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self - { - Self { - action_queue: &world.data.action_queue, - world: Some(world), - } - } -} - -/// A action for a [`System`] to perform. -#[derive(Debug)] -pub(crate) enum Action -{ - Spawn(Uid, Vec<ComponentParts>), - Despawn(Uid), - AddComponents(Uid, Vec<ComponentParts>), - RemoveComponents(Uid, Vec<Uid>), - Stop, -} diff --git a/ecs/src/component.rs b/ecs/src/component.rs deleted file mode 100644 index 17b279b..0000000 --- a/ecs/src/component.rs +++ /dev/null @@ -1,324 +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::Changed; -use crate::event::Submitter as EventSubmitter; -use crate::lock::{ - Error as LockError, - MappedReadGuard, - MappedWriteGuard, - ReadGuard, - WriteGuard, -}; -use crate::pair::Pair; -use crate::system::Input as SystemInput; -use crate::uid::Uid; -use crate::util::Array; -use crate::{EntityComponentRef, World}; - -pub mod local; - -pub(crate) mod storage; - -pub trait Component: SystemInput + Any -{ - /// Returns the ID of this component. - fn id() -> Uid - where - Self: Sized; - - /// Returns the name of this component. - fn name(&self) -> &'static str; -} - -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() - } -} - -/// A sequence of components. -pub trait Sequence -{ - /// The number of components in this component sequence. - const COUNT: usize; - - type PartsArray: Array<Parts>; - - fn into_parts_array(self) -> Self::PartsArray; -} - -#[derive(Debug)] -pub struct Handle<'a, DataT: 'static> -{ - inner: MappedReadGuard<'a, DataT>, -} - -impl<'comp, DataT: 'static> Handle<'comp, DataT> -{ - /// Creates a new handle instance from a [`EntityComponentRef`]. - /// - /// # Errors - /// Will return `Err` if acquiring the component's lock fails. - pub fn from_entity_component_ref( - entity_component_ref: &EntityComponentRef<'comp>, - ) -> Result<Self, HandleError> - { - Self::new( - entity_component_ref - .component() - .read_nonblock() - .map_err(AcquireLockError)?, - ) - } - - fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Result<Self, HandleError> - { - Ok(Self { - inner: ReadGuard::try_map(inner, |component| { - component.downcast_ref::<DataT>() - }) - .map_err(|_| HandleError::IncorrectType)?, - }) - } -} - -impl<DataT: 'static> Deref for Handle<'_, DataT> -{ - type Target = DataT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -#[derive(Debug)] -pub struct HandleMut<'a, DataT: 'static> -{ - entity_component_ref: EntityComponentRef<'a>, - inner: MappedWriteGuard<'a, DataT>, - event_submitter: EventSubmitter<'a>, -} - -impl<'comp, DataT: 'static> HandleMut<'comp, DataT> -{ - /// Creates a new handle instance from a [`EntityComponentRef`]. - /// - /// # Errors - /// Will return `Err` if acquiring the component's lock fails. - pub fn from_entity_component_ref( - entity_component_ref: &EntityComponentRef<'comp>, - world: &'comp World, - ) -> Result<Self, HandleError> - { - let inner = entity_component_ref - .component() - .write_nonblock() - .map_err(AcquireLockError)?; - - Ok(Self { - entity_component_ref: entity_component_ref.clone(), - inner: WriteGuard::try_map(inner, |component| { - component.downcast_mut::<DataT>() - }) - .map_err(|_| HandleError::IncorrectType)?, - event_submitter: world.event_submitter(), - }) - } - - pub fn set_changed(&self) - { - self.event_submitter.submit_event( - &Pair::builder() - .relation::<Changed>() - .target_id(self.entity_component_ref.id()) - .build(), - self.entity_component_ref.entity_id(), - ); - } -} - -impl<DataT: 'static> Deref for HandleMut<'_, DataT> -{ - type Target = DataT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<DataT: 'static> DerefMut for HandleMut<'_, DataT> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -#[derive(Debug, thiserror::Error)] -pub enum HandleError -{ - #[error(transparent)] - AcquireLockFailed(#[from] AcquireLockError), - - #[error("Incorrect component type")] - IncorrectType, -} - -#[derive(Debug, thiserror::Error)] -#[error("Failed to acquire component lock")] -pub struct AcquireLockError(#[source] LockError); - -macro_rules! inner { - ($c: tt) => { - seq!(I in 0..=$c { - impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*) - { - const COUNT: usize = $c + 1; - - type PartsArray = [Parts; $c + 1]; - - fn into_parts_array(self) -> Self::PartsArray - { - [#({ - self.I.into_parts() - },)*] - } - } - }); - }; -} - -seq!(C in 0..=16 { - inner!(C); -}); - -impl Sequence for () -{ - type PartsArray = [Parts; 0]; - - const COUNT: usize = 0; - - fn into_parts_array(self) -> Self::PartsArray - { - [] - } -} - -pub trait IntoParts -{ - fn into_parts(self) -> Parts; -} - -impl<ComponentT> IntoParts for ComponentT -where - ComponentT: Component, -{ - fn into_parts(self) -> Parts - { - Parts::builder() - .name(type_name::<Self>()) - .build(Self::id(), self) - } -} - -/// The parts of a component. -#[derive(Debug)] -#[non_exhaustive] -pub struct Parts -{ - id: Uid, - name: &'static str, - data: Box<dyn Any>, -} - -impl Parts -{ - #[must_use] - pub fn id(&self) -> Uid - { - self.id - } - - #[must_use] - pub fn name(&self) -> &'static str - { - self.name - } - - #[must_use] - pub fn builder() -> PartsBuilder - { - PartsBuilder::default() - } - - pub(crate) fn into_data(self) -> Box<dyn Any> - { - self.data - } -} - -#[derive(Debug)] -pub struct PartsBuilder -{ - name: &'static str, -} - -impl PartsBuilder -{ - #[must_use] - pub fn name(mut self, name: &'static str) -> Self - { - self.name = name; - self - } - - #[must_use] - pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts - { - Parts { - id, - name: self.name, - data: Box::new(data), - } - } - - #[must_use] - pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts - { - Parts { id, name: self.name, data } - } -} - -impl Default for PartsBuilder -{ - fn default() -> Self - { - Self { name: "(unspecified)" } - } -} diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs deleted file mode 100644 index b19a30b..0000000 --- a/ecs/src/component/local.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::any::type_name; -use std::ops::{Deref, DerefMut}; - -use ecs_macros::Component; - -use crate::component::{ - Component, - HandleMut as ComponentHandleMut, - IntoParts as _, - Parts as ComponentParts, -}; -use crate::pair::Pair; -use crate::system::initializable::Param as InitializableParam; -use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; -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 new(world: &'world World, system_metadata: &SystemMetadata) -> Self - { - let Some(system_ent) = world.get_entity(system_metadata.ent_id) else { - panic!( - "System entity with ID {} does not exist", - system_metadata.ent_id - ); - }; - - let Some(local_component) = system_ent.get_with_id_mut::<LocalComponent>( - Pair::builder() - .relation::<IsLocalComponent>() - .target::<LocalComponent>() - .build() - .id(), - ) else { - panic!( - "Local component {} of system with ID {} is uninitialized", - type_name::<LocalComponent>(), - system_metadata.ent_id - ); - }; - - Self { local_component } - } -} - -impl<'world, LocalComponent, SystemT> InitializableParam<'world, SystemT> - for Local<'world, LocalComponent> -where - LocalComponent: Component, - SystemT: SystemWithLocalComponents, - Self: SystemParam<'world, Input = LocalComponent>, -{ - fn initialize(system: &mut SystemT, input: Self::Input) - { - system.add_local_component( - Pair::builder() - .relation::<IsLocalComponent>() - .target_as_data(input) - .build() - .into_parts(), - ); - } -} - -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 - } -} - -pub trait SystemWithLocalComponents -{ - fn add_local_component(&mut self, component_parts: ComponentParts); -} - -#[derive(Component)] -struct IsLocalComponent; diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs deleted file mode 100644 index dc38b6a..0000000 --- a/ecs/src/component/storage.rs +++ /dev/null @@ -1,795 +0,0 @@ -use std::any::Any; -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::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, comp_id: Uid) -> bool - { - let comp_id_kind = comp_id.kind(); - - debug_assert!( - comp_id_kind == UidKind::Component - || (comp_id_kind == UidKind::Pair - && comp_id.target_component() != Uid::wildcard()) - ); - - let is_found = self.excluded_components.binary_search(&comp_id).is_ok(); - - if !is_found && comp_id_kind == UidKind::Pair { - return self.excluded_components.iter().any(|excluded_comp_id| { - excluded_comp_id.kind() == UidKind::Pair - && excluded_comp_id.has_same_relation_as(comp_id) - && excluded_comp_id.target_component() == Uid::wildcard() - }); - } - - is_found - } - - fn contains_conflicting(&self) -> bool - { - self.excluded_components.iter().any(|excluded_comp_id| { - self.required_components - .binary_search(excluded_comp_id) - .is_ok() - }) - } - - fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool - { - self.required_components - .iter() - .all(|comp_id| archetype.contains_matching_component(*comp_id)) - } -} - -#[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.contains_conflicting() { - 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: ArchetypeId::new(search_terms.required_components.iter().filter( - |required_comp_id| { - required_comp_id.kind() != UidKind::Pair - || required_comp_id.target_component() != Uid::wildcard() - }, - )), - component_ids: search_terms - .required_components - .iter() - .copied() - .filter(|required_comp_id| { - required_comp_id.kind() != UidKind::Pair - || required_comp_id.target_component() != Uid::wildcard() - }) - .collect(), - }); - - 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<(), EntityAlreadyExistsError> - { - debug_assert_eq!(uid.kind(), UidKind::Entity); - - if self.entity_archetype_lookup.contains_key(&uid) { - return Err(EntityAlreadyExistsError); - } - - let empty_archetype_id = ArchetypeId::new_empty(); - - 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_id, component_name, component): (Uid, &'static str, Box<dyn Any>), - ) -> 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() - .contains_component_with_exact_id(component_id) - { - return Err(Error::ComponentAlreadyInEntity { - entity: entity_uid, - component: component_id, - }); - } - - let add_edge_archetype_id = if let Some(add_edge_id) = archetype_node - .get_or_insert_edges(component_id, ArchetypeEdges::default) - .add - { - 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 - } else { - let archetype_node = self - .graph - .get_node_by_id(archetype_id) - .expect("Archetype should exist"); - - let (add_edge_id, add_edge_comp_ids) = - archetype_node.make_add_edge(component_id); - - if !self.graph.contains_archetype(add_edge_id) { - self.graph.create_node(add_edge_id, &add_edge_comp_ids); - } - - add_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"); - - 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, component_name), - 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() - .contains_component_with_exact_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); - - 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"); - - let removed_component = - 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); - - tracing::debug!( - entity_id = %entity_uid, - component_id = %component_id, - component_name = removed_component.name(), - "Removed component from entity" - ); - - 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_empty()) - else { - // If the root archetype doesn't exist, no other archetype can exist either - // - // TODO: The above comment is not true. Cases where imaginary archetypes have - // been created should be handled as well - 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.archetype_contains_all_required(node.archetype()) { - continue; - } - - found.push(node.archetype().id()); - - search_iter.pop(); - } - - found - } -} - -#[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> Iterator for ArchetypeRefIter<'component_storage, '_> -{ - 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_part_pt_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.clone(), - }, - ); - - 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(), - )); - } - _ => { - 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_part_pt_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:?} 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, thiserror::Error)] -#[error("Entity with already exists")] -pub struct EntityAlreadyExistsError; - -#[derive(Debug)] -struct ImaginaryArchetype -{ - id: ArchetypeId, - component_ids: Vec<Uid>, -} - -#[cfg(test)] -mod tests -{ - use crate::component::storage::Storage; - use crate::component::storage::archetype::Id as ArchetypeId; - 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::new_empty()) - .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::new_empty()) - ); - } -} diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs deleted file mode 100644 index a7fe7ed..0000000 --- a/ecs/src/component/storage/archetype.rs +++ /dev/null @@ -1,385 +0,0 @@ -use std::any::Any; -use std::array::IntoIter as ArrayIntoIter; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::iter::{Enumerate, Filter, Map, RepeatN, Zip}; -use std::option::IntoIter as OptionIntoIter; -use std::slice::Iter as SliceIter; - -use hashbrown::HashMap; - -use crate::lock::Lock; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::{Either, 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_matching_component_indices( - &self, - component_id: Uid, - ) -> MatchingComponentIter<'_> - { - assert!( - component_id.kind() == UidKind::Component - || component_id.kind() == UidKind::Pair - ); - - if component_id.kind() == UidKind::Pair - && component_id.target_component() == Uid::wildcard() - { - return MatchingComponentIter { - inner: Either::A( - self.component_ids - .iter() - .enumerate() - .zip(std::iter::repeat_n(component_id, self.component_ids.len())) - .filter( - (|((_, other_comp_id), component_id)| { - other_comp_id.kind() == UidKind::Pair - && other_comp_id.has_same_relation_as(*component_id) - }) - as MatchingComponentIterFilterFn, - ) - .map(|((index, other_comp_id), _)| (*other_comp_id, index)), - ), - }; - } - - MatchingComponentIter { - inner: Either::B( - [component_id] - .into_iter() - .zip(self.get_index_for_component(component_id)), - ), - } - } - - pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize> - { - assert!( - component_id.kind() == UidKind::Component - || (component_id.kind() == UidKind::Pair - && component_id.target_component() != Uid::wildcard()) - ); - - 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 contains_matching_component(&self, component_id: Uid) -> bool - { - let component_id_kind = component_id.kind(); - - debug_assert!( - component_id_kind == UidKind::Component || component_id_kind == UidKind::Pair - ); - - if component_id.kind() == UidKind::Pair - && component_id.target_component() == Uid::wildcard() - { - return self.component_ids.iter().any(|other_comp_id| { - other_comp_id.kind() == UidKind::Pair - && other_comp_id.has_same_relation_as(component_id) - }); - } - - self.contains_component_with_exact_id(component_id) - } - - pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool - { - let component_id_kind = component_id.kind(); - - debug_assert!( - component_id_kind == UidKind::Component - || (component_id_kind == UidKind::Pair - && component_id.target_component() != Uid::wildcard()) - ); - - self.component_index_lookup.contains_key(&component_id) - } -} - -type MatchingComponentIterFilterFn = fn(&((usize, &Uid), Uid)) -> bool; - -type MatchingComponentIterMapFn = fn(((usize, &Uid), Uid)) -> (Uid, usize); - -type InnerMatchingComponentIterA<'archetype> = Map< - Filter< - Zip<Enumerate<SliceIter<'archetype, Uid>>, RepeatN<Uid>>, - MatchingComponentIterFilterFn, - >, - MatchingComponentIterMapFn, ->; - -type InnerMatchingComponentIterB = Zip<ArrayIntoIter<Uid, 1>, OptionIntoIter<usize>>; - -#[derive(Debug)] -pub struct MatchingComponentIter<'archetype> -{ - inner: Either<InnerMatchingComponentIterA<'archetype>, InnerMatchingComponentIterB>, -} - -impl Iterator for MatchingComponentIter<'_> -{ - type Item = (Uid, usize); - - fn next(&mut self) -> Option<Self::Item> - { - self.inner.next() - } -} - -#[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, - ) -> EntityComponent - { - 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 -{ - component: Lock<Box<dyn Any>>, - name: &'static str, -} - -impl EntityComponent -{ - pub fn new(component: Box<dyn Any>, component_name: &'static str) -> Self - { - Self { - component: Lock::new(component, component_name), - name: component_name, - } - } - - pub fn component(&self) -> &Lock<Box<dyn Any>> - { - &self.component - } - - pub fn name(&self) -> &str - { - self.name - } -} - -/// Archetype ID. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Id -{ - hash: u64, -} - -impl Id -{ - pub fn new_empty() -> Self - { - Self { hash: 0 } - } - - pub fn new<'a>(component_ids: impl IntoIterator<Item = &'a Uid>) -> Self - { - let mut hasher = DefaultHasher::new(); - - let mut prev_component_id: Option<Uid> = None; - - let mut component_id_iter = component_ids.into_iter().peekable(); - - if component_id_iter.peek().is_none() { - return Self::new_empty(); - } - - for comp_id in component_id_iter { - assert!( - prev_component_id.is_none_or(|prev_comp_id| *comp_id >= prev_comp_id), - "Cannot create archetype ID from a unsorted component metadata list" - ); - - prev_component_id = Some(*comp_id); - - comp_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 76200f9..0000000 --- a/ecs/src/component/storage/graph.rs +++ /dev/null @@ -1,432 +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: &mut ArchetypeNode, - subset_node: &mut ArchetypeNode, - ) - { - let uniq_comp_id = target_node - .archetype() - .component_ids_sorted() - .find(|id| { - !subset_node - .archetype() - .contains_component_with_exact_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); - - if target_node.archetype().component_cnt() - == subset_node.archetype().component_cnt() + 1 - { - target_node - .get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default) - .remove = Some(subset_node.archetype().id()); - } - } - - 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() - .contains_component_with_exact_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() - .contains_component_with_exact_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!(matches!( - component_id.kind(), - UidKind::Component | UidKind::Pair - )); - - 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 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 ad9f179..0000000 --- a/ecs/src/entity.rs +++ /dev/null @@ -1,295 +0,0 @@ -use std::any::type_name; -use std::ops::Deref; -use std::sync::LazyLock; - -use crate::component::storage::archetype::{ - Archetype, - Entity as ArchetypeEntity, - MatchingComponentIter as ArchetypeMatchingComponentIter, -}; -use crate::component::{ - Component, - Handle as ComponentHandle, - HandleMut as ComponentHandleMut, -}; -use crate::pair::{ - ComponentOrWildcard, - MultipleWithWildcard as PairMultipleWithWildcard, - Pair, - WithWildcard as PairWithWildcard, -}; -use crate::uid::{Kind as UidKind, Uid}; -use crate::{EntityComponentRef, World}; - -pub mod obtainer; - -/// A handle to a entity. -#[derive(Debug, Clone)] -pub struct Handle<'a> -{ - archetype: &'a Archetype, - entity: &'a ArchetypeEntity, - world: &'a World, -} - -impl<'a> Handle<'a> -{ - /// Returns the [`Uid`] of this entity. - #[inline] - #[must_use] - pub fn uid(&self) -> Uid - { - self.entity.uid() - } - - /// Returns a reference to the specified component in this entity. `None` is - /// returned if the component isn't found in the entity. - /// - /// # Panics - /// Will panic if: - /// - The component's ID is not a component ID - /// - The component is mutably borrowed elsewhere - #[must_use] - pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'a, ComponentT>> - { - assert_eq!(ComponentT::id().kind(), UidKind::Component); - - let component = self.get_matching_components(ComponentT::id()).next()?; - - Some( - ComponentHandle::from_entity_component_ref(&component).unwrap_or_else( - |err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }, - ), - ) - } - - /// Returns a mutable reference to the specified component in this entity. `None` is - /// returned if the component isn't found in the entity. - /// - /// # Panics - /// Will panic if: - /// - The component's ID is not a component ID - /// - The component is borrowed elsewhere - #[must_use] - pub fn get_mut<ComponentT: Component>( - &self, - ) -> Option<ComponentHandleMut<'a, ComponentT>> - { - assert_eq!(ComponentT::id().kind(), UidKind::Component); - - let component = self.get_matching_components(ComponentT::id()).next()?; - - Some( - ComponentHandleMut::from_entity_component_ref(&component, self.world) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }), - ) - } - - /// Returns a reference to the component with the ID `id` in this entity. - /// `None` is returned if the component isn't found. - /// - /// # Panics - /// Will panic if: - /// - The ID is not a component/pair ID - /// - The component is borrowed mutably elsewhere - /// - The component type is incorrect - #[must_use] - pub fn get_with_id<ComponentDataT: 'static>( - &self, - id: Uid, - ) -> Option<ComponentHandle<'a, ComponentDataT>> - { - assert!( - matches!(id.kind(), UidKind::Component | UidKind::Pair), - "ID {id:?} is not a component/pair ID" - ); - - let component = self.get_matching_components(id).next()?; - - Some( - ComponentHandle::from_entity_component_ref(&component).unwrap_or_else( - |err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentDataT>() - ); - }, - ), - ) - } - - /// Returns a mutable reference to the component with the ID `id` in this entity. - /// `None` is returned if the component isn't found. - /// - /// # Panics - /// Will panic if: - /// - The ID is not a component/pair ID - /// - The component is borrowed elsewhere - /// - The component type is incorrect - #[must_use] - pub fn get_with_id_mut<ComponentDataT: 'static>( - &self, - id: Uid, - ) -> Option<ComponentHandleMut<'a, ComponentDataT>> - { - assert!( - matches!(id.kind(), UidKind::Component | UidKind::Pair), - "ID {id:?} is not a component/pair ID" - ); - - let component = self.get_matching_components(id).next()?; - - Some( - ComponentHandleMut::from_entity_component_ref(&component, self.world) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentDataT>() - ); - }), - ) - } - - #[must_use] - pub fn get_first_wildcard_pair_match<Relation, Target>( - &self, - ) -> Option<PairWithWildcard<'a, Relation, Target>> - where - Relation: ComponentOrWildcard, - Target: ComponentOrWildcard, - { - let mut matching_comps = self.get_matching_components( - Pair::builder() - .relation_id(Relation::uid()) - .target_id(Target::uid()) - .build() - .id(), - ); - - Some(PairWithWildcard::new(self.world, matching_comps.next()?)) - } - - #[must_use] - pub fn get_wildcard_pair_matches<Relation, Target>( - &self, - ) -> PairMultipleWithWildcard<'a, Relation, Target> - where - Relation: ComponentOrWildcard, - Target: ComponentOrWildcard, - { - PairMultipleWithWildcard::new(self.world, self.clone()) - } - - #[inline] - #[must_use] - pub fn get_matching_components(&self, component_uid: Uid) - -> MatchingComponentIter<'a> - { - MatchingComponentIter { - inner: self.archetype.get_matching_component_indices(component_uid), - entity: self.entity, - } - } - - /// Returns whether or not this entity contains a component with the specified `Uid`. - #[must_use] - pub fn has_component(&self, component_uid: Uid) -> bool - { - self.archetype - .contains_component_with_exact_id(component_uid) - } - - /// Returns the `Uids`s of the components this entity has. - pub fn component_ids(&self) -> impl Iterator<Item = Uid> + '_ - { - self.archetype.component_ids_sorted() - } - - pub(crate) fn new( - archetype: &'a Archetype, - entity: &'a ArchetypeEntity, - world: &'a World, - ) -> Self - { - Self { archetype, entity, world } - } -} - -#[derive(Debug)] -pub struct MatchingComponentIter<'a> -{ - inner: ArchetypeMatchingComponentIter<'a>, - entity: &'a ArchetypeEntity, -} - -impl<'a> Iterator for MatchingComponentIter<'a> -{ - type Item = EntityComponentRef<'a>; - - fn next(&mut self) -> Option<Self::Item> - { - let (matching_component_id, index) = self.inner.next()?; - - Some(EntityComponentRef::new( - matching_component_id, - self.entity.components().get(index).unwrap(), - self.entity.uid(), - )) - } -} - -/// The data type of a declaration of a entity. -#[derive(Debug)] -pub struct Declaration -{ - uid: LazyLock<Uid>, - create_func: fn(&mut World), -} - -impl Declaration -{ - pub(crate) fn create(&self, world: &mut World) - { - (self.create_func)(world); - } - - #[doc(hidden)] - pub const fn new(create_func: fn(&mut World)) -> Self - { - Self { - uid: LazyLock::new(|| Uid::new_unique(UidKind::Entity)), - create_func, - } - } -} - -impl Deref for Declaration -{ - type Target = Uid; - - fn deref(&self) -> &Self::Target - { - &self.uid - } -} - -#[allow(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! declare_entity { - ($visibility: vis $ident: ident, $components: expr) => { - $visibility static $ident: $crate::entity::Declaration = - $crate::entity::Declaration::new(|world| { - world.create_entity_with_uid(*$ident, $components); - }); - } -} diff --git a/ecs/src/entity/obtainer.rs b/ecs/src/entity/obtainer.rs deleted file mode 100644 index 6c2ea96..0000000 --- a/ecs/src/entity/obtainer.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::entity::Handle as EntityHandle; -use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; -use crate::uid::Uid; -use crate::World; - -#[derive(Debug)] -pub struct Obtainer<'world> -{ - world: &'world World, -} - -impl<'world> SystemParam<'world> for Obtainer<'world> -{ - type Input = (); - - fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self - { - Self { world } - } -} - -impl Obtainer<'_> -{ - #[must_use] - pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> - { - self.world.get_entity(entity_id) - } -} diff --git a/ecs/src/error.rs b/ecs/src/error.rs deleted file mode 100644 index 185b706..0000000 --- a/ecs/src/error.rs +++ /dev/null @@ -1,270 +0,0 @@ -use std::fmt::{Debug, Display, Write as _}; - -use backtrace::Backtrace; - -#[macro_export] -macro_rules! error { - ($lit: literal) => { - $crate::error::Error::from($lit) - }; - - ($lit: literal, $($tt: tt)*) => { - $crate::error::Error::from(std::format!($lit, $($tt)*)) - }; - - ($err: expr) => { - $crate::error::Error::from($err) - }; -} - -pub struct Error -{ - inner: Box<dyn std::error::Error + Send + Sync>, - backtrace: Backtrace, -} - -impl Error -{ - pub fn resolve_backtrace(&mut self) - { - self.backtrace.resolve(); - } - - fn is_backtrace_resolved(&self) -> bool - { - let Some(first_frame) = self.backtrace.frames().first() else { - return false; - }; - - !first_frame.symbols().is_empty() - } -} - -impl Debug for Error -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - let error = &*self.inner; - - write!(formatter, "{error}")?; - - if let Some(cause) = error.source() { - write!(formatter, "\n\nCaused by:")?; - let multiple = cause.source().is_some(); - for (n, error) in anyhow::Chain::new(cause).enumerate() { - writeln!(formatter)?; - - let mut indented = Indented { - inner: formatter, - number: if multiple { Some(n) } else { None }, - started: false, - }; - write!(indented, "{error}")?; - } - } - - if std::env::var_os("ENGINE_ECS_BACKTRACE") - .is_none_or(|backtrace_enabled| backtrace_enabled != "1") - { - write!( - formatter, - concat!( - "\n\nnote: run with `ENGINE_ECS_BACKTRACE=1` environment variable ", - "to display a engine backtrace" - ) - )?; - - return Ok(()); - } - - let mut cloned_backtrace; - - let backtrace = if self.is_backtrace_resolved() { - &self.backtrace - } else { - cloned_backtrace = self.backtrace.clone(); - cloned_backtrace.resolve(); - &cloned_backtrace - }; - - write!( - formatter, - "\n\nStack backtrace:\n{:?}", - std::fmt::from_fn(|backtrace_formatter| fmt_backtrace( - backtrace, - backtrace_formatter - )) - )?; - - Ok(()) - } -} - -impl Display for Error -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - let error = &*self.inner; - - write!(formatter, "{error}")?; - - if formatter.alternate() { - let chain = anyhow::Chain::new(error); - for cause in chain.skip(1) { - write!(formatter, ": {}", cause)?; - } - } - - Ok(()) - } -} - -impl<Err: Send + Sync + 'static> From<Err> for Error -where - Box<dyn std::error::Error + Send + Sync>: From<Err>, -{ - fn from(err: Err) -> Self - { - Self { - inner: err.into(), - backtrace: Backtrace::new_unresolved(), - } - } -} - -pub type ErrorHandler = fn(Error, Metadata); - -/// Error metadata. -#[derive(Debug)] -pub struct Metadata -{ - pub source_name: &'static str, - pub source_kind: SourceKind, -} - -/// Error source kind. -#[derive(Debug)] -#[non_exhaustive] -pub enum SourceKind -{ - System, - Observer, -} - -impl Display for SourceKind -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - match self { - SourceKind::System => formatter.write_str("system"), - SourceKind::Observer => formatter.write_str("observer"), - } - } -} - -pub fn err_handler_panic(mut err: Error, err_metadata: Metadata) -{ - err.resolve_backtrace(); - - panic!( - "Error occurred in {} '{}': {err:?}", - err_metadata.source_kind, err_metadata.source_name - ); -} - -pub fn err_handler_log_error(err: Error, err_metadata: Metadata) -{ - tracing::error!( - "Error occurred in {} '{}': {err:#}", - err_metadata.source_kind, - err_metadata.source_name - ); -} - -fn fmt_backtrace( - backtrace: &Backtrace, - fmt: &mut std::fmt::Formatter<'_>, -) -> std::fmt::Result -{ - let style = if fmt.alternate() { - backtrace::PrintFmt::Full - } else { - backtrace::PrintFmt::Short - }; - - // When printing paths we try to strip the cwd if it exists, otherwise - // we just print the path as-is. Note that we also only do this for the - // short format, because if it's full we presumably want to print - // everything. - let cwd = std::env::current_dir(); - let mut print_path = - move |fmt: &mut std::fmt::Formatter<'_>, - path: backtrace::BytesOrWideString<'_>| { - let path = path.into_path_buf(); - if style != backtrace::PrintFmt::Full { - if let Ok(cwd) = &cwd { - if let Ok(suffix) = path.strip_prefix(cwd) { - return std::fmt::Display::fmt(&suffix.display(), fmt); - } - } - } - std::fmt::Display::fmt(&path.display(), fmt) - }; - - let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path); - - f.add_context()?; - - for frame in backtrace.frames() { - if frame.symbols().iter().all(|symbol| { - symbol.name().is_some_and(|symbol_name| { - let symbol_name = symbol_name.to_string(); - - symbol_name - .contains("<ecs::error::Error as core::convert::From<Err>>::from") - }) - }) { - continue; - } - - f.frame().backtrace_frame(frame)?; - } - f.finish()?; - Ok(()) -} - -struct Indented<'a, D> -{ - inner: &'a mut D, - number: Option<usize>, - started: bool, -} - -impl<T> std::fmt::Write for Indented<'_, T> -where - T: std::fmt::Write, -{ - fn write_str(&mut self, s: &str) -> std::fmt::Result - { - for (i, line) in s.split('\n').enumerate() { - if !self.started { - self.started = true; - match self.number { - Some(number) => write!(self.inner, "{: >5}: ", number)?, - None => self.inner.write_str(" ")?, - } - } else if i > 0 { - self.inner.write_char('\n')?; - if self.number.is_some() { - self.inner.write_str(" ")?; - } else { - self.inner.write_str(" ")?; - } - } - - self.inner.write_str(line)?; - } - - Ok(()) - } -} diff --git a/ecs/src/event.rs b/ecs/src/event.rs deleted file mode 100644 index 15455b6..0000000 --- a/ecs/src/event.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::lock::Lock; -use crate::pair::Pair; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::VecExt; - -pub mod component; - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Emitted<'a> -{ - pub event: Uid, - pub match_ids: &'a [Uid], -} - -#[derive(Debug)] -pub struct Submitter<'world> -{ - new_events: &'world Lock<NewEvents>, -} - -impl<'world> Submitter<'world> -{ - /// Submits a event to be handled later. - /// - /// # Panics - /// Will panic if unable to acquire a read-write lock to the event store. - pub fn submit_event(&self, event: &Pair<Uid, Uid>, match_id: Uid) - { - let mut new_events_lock = self - .new_events - .write_nonblock() - .expect("Failed to acquire read-write lock to new events"); - - new_events_lock.push_event_match(event, match_id); - } - - pub(crate) fn new(new_events: &'world Lock<NewEvents>) -> Self - { - Self { new_events } - } -} - -#[derive(Debug, Default)] -pub(crate) struct NewEvents -{ - events: Vec<(Uid, Matches)>, -} - -impl NewEvents -{ - pub fn push_event_match(&mut self, event: &Pair<Uid, Uid>, match_id: Uid) - { - let event_id = event.id(); - - assert_eq!(event_id.kind(), UidKind::Pair); - - if let Ok(event_index) = self - .events - .binary_search_by_key(&event_id, |(other_event_id, _)| *other_event_id) - { - let Some((_, matches)) = self.events.get_mut(event_index) else { - unreachable!(); - }; - - matches.sorted_push(match_id); - - return; - } - - self.events.insert_at_part_pt_by_key( - (event_id, Matches { match_ids: Vec::from([match_id]) }), - |(other_event_id, _)| other_event_id, - ); - } - - pub fn take(&mut self) -> Vec<(Uid, Matches)> - { - std::mem::take(&mut self.events) - } - - pub fn is_empty(&self) -> bool - { - self.events.is_empty() - } -} - -#[derive(Debug)] -pub(crate) struct Matches -{ - pub match_ids: Vec<Uid>, -} - -impl Matches -{ - fn sorted_push(&mut self, match_id: Uid) - { - if self.match_ids.binary_search(&match_id).is_ok() { - return; - } - - self.match_ids - .insert_at_part_pt_by_key(match_id, |other_match_id| other_match_id); - } -} diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs deleted file mode 100644 index 70ea3e5..0000000 --- a/ecs/src/event/component.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Component events. - -use std::convert::Infallible; - -use crate::Component; -use crate::component::{Handle as ComponentHandle, HandleMut as ComponentHandleMut}; -use crate::entity::Handle as EntityHandle; -use crate::pair::Pair; -use crate::system::observer::{EventMatch, Observed}; - -/// Implemented by the relations of component event pairs -pub trait EventRelation: Component {} - -/// Pair relation for events emitted when: -/// a) A entity with the target component is spawned. -/// b) The target component is added to a entity. -#[derive(Debug, Component)] -pub struct Added(Infallible); - -impl EventRelation for Added {} - -/// Pair relation for events emitted **before**: -/// a) The target component is removed from a entity. -/// b) A entity with the target component is despawned. -#[derive(Debug, Component)] -pub struct Removed(Infallible); - -impl EventRelation for Removed {} - -#[derive(Debug, Component)] -pub struct Changed(Infallible); - -impl EventRelation for Changed {} - -/// [`EventMatch`] extension trait for component event matches. -pub trait EventMatchExt<Target>: sealed::Sealed -{ - #[must_use] - fn get_entity(&self) -> EntityHandle<'_>; - - #[must_use] - fn get_ent_target_comp(&self) -> ComponentHandle<'_, Target> - where - Target: Component; - - #[must_use] - fn get_ent_target_comp_mut(&self) -> ComponentHandleMut<'_, Target> - where - Target: Component; -} - -impl<ComponentEventRelation: EventRelation, Target> EventMatchExt<Target> - for EventMatch<'_, Pair<ComponentEventRelation, Target>> -where - Pair<ComponentEventRelation, Target>: Observed, -{ - fn get_entity(&self) -> EntityHandle<'_> - { - let Some(ent) = self.try_get_entity() else { - unreachable!(); - }; - - ent - } - - fn get_ent_target_comp(&self) -> ComponentHandle<'_, Target> - where - Target: Component, - { - let ent = self.get_entity(); - - let Some(comp) = ent.get::<Target>() else { - unreachable!(); - }; - - comp - } - - fn get_ent_target_comp_mut(&self) -> ComponentHandleMut<'_, Target> - where - Target: Component, - { - let ent = self.get_entity(); - - let Some(comp) = ent.get_mut::<Target>() else { - unreachable!(); - }; - - comp - } -} - -impl<ComponentEventRelation: EventRelation, Target> sealed::Sealed - for EventMatch<'_, Pair<ComponentEventRelation, Target>> -where - Pair<ComponentEventRelation, Target>: Observed, -{ -} - -mod sealed -{ - pub trait Sealed {} -} diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs deleted file mode 100644 index 9c6614b..0000000 --- a/ecs/src/extension.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::component::Sequence as ComponentSequence; -use crate::entity::Declaration as EntityDeclaration; -use crate::sole::Sole; -use crate::system::observer::Observer; -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 observer system to the [`World`]. - pub fn add_observer<'this, SystemImpl>( - &'this mut self, - observer: impl Observer<'this, SystemImpl>, - ) - { - self.world.register_observer(observer); - } - - /// 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 declared entity to the [`World`]. - pub fn add_declared_entity(&mut self, entity_decl: &EntityDeclaration) - { - self.world.create_declared_entity(entity_decl); - } - - /// 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 667aa0e..0000000 --- a/ecs/src/lib.rs +++ /dev/null @@ -1,773 +0,0 @@ -#![deny(clippy::all, clippy::pedantic)] - -use std::any::{Any, TypeId, type_name}; -use std::fmt::Debug; -use std::hint::cold_path; -use std::mem::ManuallyDrop; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; - -use hashbrown::HashMap; - -use crate::actions::Action; -use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; -use crate::component::storage::{EntityAlreadyExistsError, Storage as ComponentStorage}; -use crate::component::{ - Component, - IntoParts as IntoComponentParts, - Parts as ComponentParts, - Sequence as ComponentSequence, -}; -use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle}; -use crate::error::{ - ErrorHandler, - Metadata as ErrorMetadata, - SourceKind as ErrorSourceKind, - err_handler_panic, -}; -use crate::event::component::Added; -use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter}; -use crate::extension::{Collector as ExtensionCollector, Extension}; -use crate::lock::Lock; -use crate::pair::{ChildOf, Pair, Wildcard}; -use crate::phase::{ - HasSystem as PhaseHasSystem, - POST_UPDATE as POST_UPDATE_PHASE, - PRE_UPDATE as PRE_UPDATE_PHASE, - Phase, - START as START_PHASE, - UPDATE as UPDATE_PHASE, -}; -use crate::query::flexible::Query as FlexibleQuery; -use crate::query::{ - MAX_TERM_CNT as QUERY_MAX_TERM_CNT, - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, - Terms as QueryTerms, - TermsBuilderInterface, -}; -use crate::sole::{Single, Sole}; -use crate::stats::Stats; -use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent}; -use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent}; -use crate::uid::{Kind as UidKind, Uid}; - -pub mod actions; -pub mod component; -pub mod entity; -pub mod error; -pub mod event; -pub mod extension; -pub mod pair; -pub mod phase; -pub mod query; -pub mod sole; -pub mod stats; -pub mod system; -pub mod tuple; -pub mod uid; -pub mod util; - -mod lock; - -pub use ecs_macros::{Component, Sole}; - -pub use crate::query::Query; - -#[derive(Debug)] -pub struct World -{ - data: WorldData, - stop: AtomicBool, - is_first_tick: AtomicBool, - error_handler: ErrorHandler, -} - -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), - error_handler: err_handler_panic, - }; - - crate::phase::spawn_entities(&mut world); - - world.add_sole(Stats::default()).ok(); - - world - } - - pub fn set_err_handler(&mut self, err_handler: ErrorHandler) - { - self.error_handler = err_handler; - } - - /// Creates a entity with the given components. A new unique [`Uid`] will be generated - /// for this entity. - 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(entity_uid, components); - - entity_uid - } - - /// Creates a entity with the given components. The entity will have the specified - /// [`Uid`]. - #[tracing::instrument(skip_all)] - pub fn create_entity_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps) - where - Comps: ComponentSequence, - { - self.create_ent(entity_uid, components.into_parts_array()); - } - - pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts) - { - Self::add_entity_components( - entity_id, - [component_parts], - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - - pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration) - { - entity_decl.create(self); - } - - /// 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_observer<'this, SystemImpl, ObserverT>( - &'this mut self, - observer: ObserverT, - ) where - ObserverT: Observer<'this, SystemImpl>, - { - let (wrapper_comp, mut system_callbacks) = observer.finish_observer(); - - let ent_id = Uid::new_unique(UidKind::Entity); - - self.create_ent( - ent_id, - [wrapper_comp.into_parts()].into_iter().chain( - ObserverT::observed_events() - .into_iter() - .map(IntoComponentParts::into_parts), - ), - ); - - system_callbacks.on_created(self, SystemMetadata { ent_id }); - } - - pub fn register_system<'this, SystemImpl>( - &'this mut self, - phase_euid: Uid, - system: impl System<'this, SystemImpl>, - ) - { - let (type_erased_system, mut system_callbacks) = system.finish(); - - let system_ent_id = - self.create_entity((SystemComponent { system: type_erased_system },)); - - system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); - - self.create_entity_with_uid( - phase_euid, - (Pair::builder() - .relation::<PhaseHasSystem>() - .target_id(system_ent_id) - .build(),), - ); - } - - /// Adds a extensions. - 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) - } - - pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> - { - let archetype = self - .data - .component_storage - .get_entity_archetype(entity_id)?; - - let Some(entity) = archetype.get_entity_by_id(entity_id) else { - unreachable!("Should exist since archetype was found by entity id"); - }; - - Some(EntityHandle::new(archetype, entity, self)) - } - - pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>> - { - Some(Single::new(self.data.sole_storage.get::<SoleT>()?)) - } - - pub fn event_submitter(&self) -> EventSubmitter<'_> - { - EventSubmitter::new(&self.data.new_events) - } - - /// Performs a single tick. - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. - pub fn step(&mut 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() - { - let Some(start_phase_entity) = self.get_entity(*START_PHASE) else { - unreachable!(); - }; - - self.run_phase_systems(&start_phase_entity); - } - - self.perform_phases(); - - self.emit_new_events(); - - self.data.component_storage.create_imaginary_archetypes(); - - self.perform_queued_actions(); - - if self.stop.load(Ordering::Relaxed) { - return StepResult::Stop; - } - - let Some(mut stats) = self.get_sole::<Stats>() else { - unreachable!(); // Reason: is added in World::new - }; - - stats.current_tick += 1; - - StepResult::Continue - } - - /// Starts a loop which calls [`Self::step`] until the world is stopped. - pub fn start_loop(&mut 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, - }; - - self.data.component_storage.create_vizoxide_archetype_graph( - name, - VizoxideArchetypeGraphParams { - create_node_name: |archetype, _| { - Cow::Owned(format!( - "[{}]", - archetype - .component_ids_sorted() - .into_iter() - .map(|comp_id| comp_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", - }, - ) - }, - }, - ) - } - - #[tracing::instrument(skip_all)] - fn create_ent( - &mut self, - entity_uid: Uid, - components: impl IntoIterator<Item = ComponentParts>, - ) - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - if let Err(EntityAlreadyExistsError) = - self.data.component_storage.create_entity(entity_uid) - { - // This is fine - } - - Self::add_entity_components( - entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - - fn run_phase_systems(&self, phase_entity: &EntityHandle<'_>) - { - // The phase's systems are retrieved this way so that the order they are - // run is the same order as they were registered, even if they have local - // components. - for system_entity in phase_entity - .get_wildcard_pair_matches::<PhaseHasSystem, Wildcard>() - .into_iter() - .filter_map(|phase_has_system| phase_has_system.get_target_ent()) - { - let Some(system) = system_entity.get::<SystemComponent>() else { - cold_path(); - continue; - }; - - // SAFETY: The world lives long enough - if let Err(err) = unsafe { - system - .system - .run(self, SystemMetadata { ent_id: system_entity.uid() }) - } { - cold_path(); - - (self.error_handler)( - err, - ErrorMetadata { - source_name: system.system.name(), - source_kind: ErrorSourceKind::System, - }, - ) - } - } - } - - fn perform_child_phases(&self, parent_phase_euid: Uid) - { - let phase_query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([ - Phase::id(), - Pair::builder() - .relation::<ChildOf>() - .target_id(parent_phase_euid) - .build() - .id(), - ]) - .build(), - ); - - for child_phase_entity in &phase_query { - self.run_phase_systems(&child_phase_entity); - self.perform_child_phases(child_phase_entity.uid()); - } - } - - fn perform_single_phase(&self, phase_entity_id: Uid) - { - let Some(phase_entity) = self.get_entity(phase_entity_id) else { - unreachable!(); - }; - - self.run_phase_systems(&phase_entity); - self.perform_child_phases(phase_entity_id); - } - - fn perform_phases(&self) - { - self.perform_single_phase(*PRE_UPDATE_PHASE); - self.perform_single_phase(*UPDATE_PHASE); - self.perform_single_phase(*POST_UPDATE_PHASE); - } - - fn emit_new_events(&self) - { - loop { - let new_events = { - let mut new_events_lock = self - .data - .new_events - .write_nonblock() - .expect("Failed to acquire read-write lock to new events"); - - if new_events_lock.is_empty() { - break; - } - - new_events_lock.take() - }; - - for (event_id, event_matches) in new_events { - self.emit_event_observers( - event_id, - &EmittedEvent { - event: event_id, - match_ids: &event_matches.match_ids, - }, - ); - } - } - } - - #[tracing::instrument(skip_all)] - fn perform_queued_actions(&mut self) - { - let mut action_queue_lock = self - .data - .action_queue - .queue - .write_nonblock() - .unwrap_or_else(|err| { - panic!("Failed to take read-write action queue lock: {err}",); - }); - - for action in action_queue_lock.drain(..) { - match action { - Action::Spawn(new_entity_uid, components) => { - if let Err(err) = - self.data.component_storage.create_entity(new_entity_uid) - { - tracing::warn!("Failed to create entity: {err}"); - continue; - } - - Self::add_entity_components( - new_entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - Action::Despawn(entity_uid) => { - if let Err(err) = - self.data.component_storage.remove_entity(entity_uid) - { - tracing::error!("Failed to despawn entity: {err}"); - } - } - Action::AddComponents(entity_uid, components) => { - Self::add_entity_components( - entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - Action::RemoveComponents(entity_uid, component_ids) => { - Self::remove_entity_components( - entity_uid, - component_ids, - &mut self.data.component_storage, - ); - } - Action::Stop => { - self.stop.store(true, Ordering::Relaxed); - } - } - } - } - - fn add_entity_components( - entity_uid: Uid, - components: impl IntoIterator<Item = ComponentParts>, - component_storage: &mut ComponentStorage, - event_submitter: &EventSubmitter<'_>, - ) - { - let component_iter = components.into_iter(); - - for component_parts in component_iter { - let comp_id = component_parts.id(); - - let comp_name = component_parts.name(); - - if let Err(err) = component_storage.add_entity_component( - entity_uid, - (comp_id, comp_name, component_parts.into_data()), - ) { - tracing::error!("Failed to add component {comp_name} to entity: {err}"); - continue; - } - - if comp_id.kind() == UidKind::Pair { - continue; - } - - event_submitter.submit_event( - &Pair::builder() - .relation::<Added>() - .target_id(comp_id) - .build(), - entity_uid, - ); - } - } - - fn remove_entity_components( - entity_uid: Uid, - component_ids: impl IntoIterator<Item = Uid>, - component_storage: &mut ComponentStorage, - ) - { - let component_id_iter = component_ids.into_iter(); - - for component_id in component_id_iter { - 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_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>) - { - assert_eq!(event_id.kind(), UidKind::Pair); - - let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query( - self.flexible_query( - QueryTerms::<QUERY_MAX_TERM_CNT>::builder() - .with_required([ObserverWrapperComponent::id(), event_id]) - .build(), - ), - ); - - for (observer_ent_id, (observer,)) in query.iter_with_euids() { - if let Err(err) = unsafe { - observer.run( - self, - SystemMetadata { ent_id: observer_ent_id }, - emitted_event.clone(), - ) - } { - cold_path(); - - (self.error_handler)( - err, - ErrorMetadata { - source_name: observer.name(), - source_kind: ErrorSourceKind::Observer, - }, - ) - } - } - } -} - -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)] -struct WorldData -{ - component_storage: ComponentStorage, - sole_storage: SoleStorage, - action_queue: Rc<ActionQueue>, - new_events: Lock<NewEvents>, -} - -#[derive(Debug, Clone)] -pub struct EntityComponentRef<'a> -{ - component_id: Uid, - component: &'a ArchetypeEntityComponent, - entity_id: Uid, -} - -impl<'a> EntityComponentRef<'a> -{ - fn component(&self) -> &'a Lock<Box<dyn Any>> - { - self.component.component() - } - - #[must_use] - pub fn id(&self) -> Uid - { - self.component_id - } - - #[must_use] - pub fn entity_id(&self) -> Uid - { - self.entity_id - } - - fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid) - -> Self - { - Self { - component_id, - component: comp, - entity_id, - } - } -} - -#[derive(Debug, Default)] -struct ActionQueue -{ - queue: Lock<Vec<Action>>, -} - -impl ActionQueue -{ - fn push(&self, action: Action) - { - self.queue - .write_nonblock() - .expect("Failed to aquire read-write lock to action queue") - .push(action); - } -} - -#[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), type_name::<SoleT>())), - 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 { - soles_to_drop_last.push(sole); - continue; - } - - unsafe { - ManuallyDrop::drop(sole); - } - } - - for sole in &mut soles_to_drop_last { - unsafe { - ManuallyDrop::drop(sole); - } - } - } -} diff --git a/ecs/src/lock.rs b/ecs/src/lock.rs deleted file mode 100644 index fe4e08b..0000000 --- a/ecs/src/lock.rs +++ /dev/null @@ -1,259 +0,0 @@ -use std::any::type_name; -use std::mem::forget; -use std::ops::{Deref, DerefMut}; - -use parking_lot::{ - MappedRwLockReadGuard, - MappedRwLockWriteGuard, - RwLock, - RwLockReadGuard, - RwLockWriteGuard, -}; - -#[derive(Debug)] -pub struct Lock<Value> -{ - inner: RwLock<Value>, - value_type_name: &'static str, -} - -impl<Value> Lock<Value> -{ - pub fn new(value: Value, value_type_name: &'static str) -> Self - { - Self { - inner: RwLock::new(value), - value_type_name, - } - } - - /// 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 {}", self.value_type_name); - - Ok(ReadGuard { - inner: guard, - value_type_name: self.value_type_name, - }) - } - - /// 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 {}", - self.value_type_name - ); - - Ok(WriteGuard { - inner: guard, - value_type_name: self.value_type_name, - }) - } -} - -impl<Value: Default + 'static> Default for Lock<Value> -{ - fn default() -> Self - { - Self::new(Value::default(), type_name::<Value>()) - } -} - -#[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> -{ - inner: RwLockReadGuard<'guard, Value>, - value_type_name: &'static str, -} - -impl<'guard, Value> ReadGuard<'guard, Value> -{ - pub fn try_map<NewValue>( - this: Self, - func: impl FnOnce(&Value) -> Option<&NewValue>, - ) -> Result<MappedReadGuard<'guard, NewValue>, Self> - { - let value_type_name = this.value_type_name; - - // The 'inner' field cannot be moved out of ReadGuard in a normal way since - // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&raw const this.inner) }; - forget(this); - - match RwLockReadGuard::try_map(inner, func) { - Ok(mapped_guard) => { - Ok(MappedReadGuard { inner: mapped_guard, value_type_name }) - } - Err(unmapped_guard) => Err(Self { - inner: unmapped_guard, - value_type_name, - }), - } - } -} - -impl<Value> Deref for ReadGuard<'_, Value> -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> Drop for ReadGuard<'_, Value> -{ - fn drop(&mut self) - { - tracing::trace!("Dropped lock to value of type {}", self.value_type_name); - } -} - -#[derive(Debug)] -pub struct MappedReadGuard<'guard, Value> -{ - inner: MappedRwLockReadGuard<'guard, Value>, - value_type_name: &'static str, -} - -impl<Value> Deref for MappedReadGuard<'_, Value> -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> Drop for MappedReadGuard<'_, Value> -{ - fn drop(&mut self) - { - tracing::trace!( - "Dropped mapped lock to value of type {}", - self.value_type_name - ); - } -} - -#[derive(Debug)] -pub struct WriteGuard<'guard, Value> -{ - inner: RwLockWriteGuard<'guard, Value>, - value_type_name: &'static str, -} - -impl<'guard, Value> WriteGuard<'guard, Value> -{ - pub fn try_map<NewValue>( - this: Self, - func: impl FnOnce(&mut Value) -> Option<&mut NewValue>, - ) -> Result<MappedWriteGuard<'guard, NewValue>, Self> - { - let value_type_name = this.value_type_name; - - // The 'inner' field cannot be moved out of ReadGuard in a normal way since - // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&raw const this.inner) }; - forget(this); - - match RwLockWriteGuard::try_map(inner, func) { - Ok(mapped_guard) => { - Ok(MappedWriteGuard { inner: mapped_guard, value_type_name }) - } - Err(unmapped_guard) => Err(Self { - inner: unmapped_guard, - value_type_name, - }), - } - } -} - -impl<Value> Deref for WriteGuard<'_, Value> -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> DerefMut for WriteGuard<'_, Value> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -impl<Value> Drop for WriteGuard<'_, Value> -{ - fn drop(&mut self) - { - tracing::trace!( - "Dropped mutable lock to value of type {}", - self.value_type_name - ); - } -} - -#[derive(Debug)] -pub struct MappedWriteGuard<'guard, Value> -{ - inner: MappedRwLockWriteGuard<'guard, Value>, - value_type_name: &'static str, -} - -impl<Value> Deref for MappedWriteGuard<'_, Value> -{ - type Target = Value; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<Value> DerefMut for MappedWriteGuard<'_, Value> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -impl<Value> Drop for MappedWriteGuard<'_, Value> -{ - fn drop(&mut self) - { - tracing::trace!( - "Dropped mapped mutable lock to value of type {}", - self.value_type_name - ); - } -} diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs deleted file mode 100644 index 0d353e3..0000000 --- a/ecs/src/pair.rs +++ /dev/null @@ -1,687 +0,0 @@ -use std::any::type_name; -use std::convert::Infallible; -use std::marker::PhantomData; - -use crate::component::{ - Handle as ComponentHandle, - HandleError as ComponentHandleError, - HandleMut as ComponentHandleMut, - IntoParts as IntoComponentParts, - Parts as ComponentParts, -}; -use crate::entity::{ - Handle as EntityHandle, - MatchingComponentIter as EntityMatchingComponentIter, -}; -use crate::query::{ - TermWithField as QueryTermWithField, - TermsBuilder as QueryTermsBuilder, - TermsBuilderInterface, -}; -use crate::uid::{Kind as UidKind, PairParams as UidPairParams, Uid, With as WithUid}; -use crate::util::impl_multiple; -use crate::{Component, EntityComponentRef, World}; - -/// Pair builder. -#[derive(Debug)] -pub struct Builder<Relation, Target> -{ - relation: Relation, - target: Target, -} - -impl<Relation, Target> Builder<Relation, Target> -{ - pub fn relation<NewRelation: Component>(self) -> Builder<Uid, Target> - { - Builder { - relation: NewRelation::id(), - target: self.target, - } - } - - pub fn relation_id(self, id: Uid) -> Builder<Uid, Target> - { - Builder { relation: id, target: self.target } - } - - pub fn target<NewTarget: Component>(self) -> Builder<Relation, Uid> - { - Builder { - relation: self.relation, - target: NewTarget::id(), - } - } - - pub fn target_id(self, id: Uid) -> Builder<Relation, Uid> - { - Builder { relation: self.relation, target: id } - } -} - -impl_multiple!( - Builder, - (impl<Target> _<><Uid, Target>, impl<Target> _<><(), Target>) - cb=(type_params=(ty_param_1, ty_param_2)) => { - pub fn target_as_data<NewTarget: Component>( - self, - data: NewTarget, - ) -> Builder<$ty_param_1, NewTarget> - { - Builder { - relation: self.relation, - target: data, - } - } - } -); - -impl_multiple!( - Builder, - (impl<Relation> _<><Relation, Uid>, impl<Relation> _<><Relation, ()>) - cb=(type_params=(ty_param_1, ty_param_2)) => { - pub fn relation_as_data<NewRelation: Component>( - self, - data: NewRelation, - ) -> Builder<NewRelation, $ty_param_2> - { - Builder { - relation: data, - target: self.target, - } - } - } -); - -impl_multiple!( - Builder, - ( - impl _<><Uid, Uid>, - impl<Relation: Component> _<><Relation, Uid>, - impl<Target: Component> _<><Uid, Target>, - impl<Relation: Component, Target: Component> _<><Relation, Target> - ) - cb=(type_params=(ty_param_1, ty_param_2)) => { - #[must_use] - pub fn build(self) -> Pair<$ty_param_1, $ty_param_2> - { - Pair { - relation: self.relation, - target: self.target - } - } - } -); - -impl Default for Builder<(), ()> -{ - fn default() -> Self - { - Self { relation: (), target: () } - } -} - -#[derive(Debug)] -pub struct Pair<Relation, Target> -{ - relation: Relation, - target: Target, -} - -impl Pair<(), ()> -{ - #[must_use] - pub fn builder() -> Builder<(), ()> - { - Builder { relation: (), target: () } - } -} - -impl Pair<Uid, Uid> -{ - #[must_use] - pub fn id(&self) -> Uid - { - Uid::new_pair(&UidPairParams { - relation: self.relation, - target: self.target, - }) - } -} - -impl IntoComponentParts for Pair<Uid, Uid> -{ - fn into_parts(self) -> ComponentParts - { - ComponentParts::builder().name("Pair").build(self.id(), ()) - } -} - -impl<Target> IntoComponentParts for Pair<Uid, Target> -where - Target: Component, -{ - fn into_parts(self) -> ComponentParts - { - let id = Uid::new_pair(&UidPairParams { - relation: self.relation, - target: Target::id(), - }); - - ComponentParts::builder() - .name("Pair") - .build(id, self.target) - } -} - -impl<Relation> IntoComponentParts for Pair<Relation, Uid> -where - Relation: Component, -{ - fn into_parts(self) -> ComponentParts - { - let id = Uid::new_pair(&UidPairParams { - relation: Relation::id(), - target: self.target, - }); - - ComponentParts::builder() - .name("Pair") - .build(id, self.relation) - } -} - -impl<Relation, Target> QueryTermWithField for Pair<Relation, &Target> -where - Relation: Component, - Target: Component, -{ - type Field<'a> = ComponentHandle<'a, Target>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with_required([Pair::<Relation, Target>::uid()]); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - _world: &'world World, - ) -> Self::Field<'world> - { - let target_component = entity_handle - .get_matching_components(Pair::<Relation, Target>::uid()) - .next() - .expect("Not possible"); - - Self::Field::from_entity_component_ref(&target_component).unwrap_or_else(|err| { - panic!( - "Creating handle to target component {} failed: {err}", - type_name::<Target>() - ); - }) - } -} - -impl<Relation, Target> QueryTermWithField for Pair<Relation, &mut Target> -where - Relation: Component, - Target: Component, -{ - type Field<'a> = ComponentHandleMut<'a, Target>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with_required([Pair::<Relation, Target>::uid()]); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world> - { - let target_component = entity_handle - .get_matching_components(Pair::<Relation, Target>::uid()) - .next() - .expect("Not possible"); - - Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else( - |err| { - panic!( - "Creating handle to target component {} failed: {err}", - type_name::<Target>() - ); - }, - ) - } -} - -// TODO: implement QueryTermWithField for Pair<&Relation, Target> (or equivalent) -// TODO: implement QueryTermWithField for Pair<&mut Relation, Target> (or equivalent) - -impl<Relation> QueryTermWithField for Pair<Relation, Wildcard> -where - Relation: Component, -{ - type Field<'a> = WithWildcard<'a, Relation, Wildcard>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with_required([Self::uid()]); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world> - { - let first_matching_comp = entity_handle - .get_matching_components(Self::uid()) - .next() - .expect("Not possible"); - - WithWildcard { - world, - component_ref: first_matching_comp, - _pd: PhantomData, - } - } -} - -impl<Relation, Target> WithUid for Pair<Relation, Target> -where - Relation: Component, - Target: Component, -{ - fn uid() -> Uid - { - Uid::new_pair(&UidPairParams { - relation: Relation::id(), - target: Target::id(), - }) - } -} - -impl<Relation> WithUid for Pair<Relation, Wildcard> -where - Relation: Component, -{ - fn uid() -> Uid - { - Uid::new_pair(&UidPairParams { - relation: Relation::id(), - target: Wildcard::uid(), - }) - } -} - -impl<Relation> QueryTermWithField for &'_ [Pair<Relation, Wildcard>] -where - Relation: Component, -{ - type Field<'a> = MultipleWithWildcard<'a, Relation, Wildcard>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - _terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, - ) - { - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world> - { - MultipleWithWildcard { - entity_handle: entity_handle.clone(), - world, - _pd: PhantomData, - } - } -} - -/// Reference to a pair with a wildcard relation/target. -#[derive(Debug)] -pub struct WithWildcard<'world, Relation, Target> -{ - world: &'world World, - component_ref: EntityComponentRef<'world>, - _pd: PhantomData<(Relation, Target)>, -} - -impl<'world, Relation, Target> WithWildcard<'world, Relation, Target> -{ - /// Returns a new `WithWildcard`. - /// - /// # Panics - /// This function will panic if: - /// - The given component's ID is not a pair ID. - /// - `Relation::uid()` is not wildcard and does not equal to the relation of the - /// given component's ID - /// - `Target::uid()` is not wildcard and does not equal to the target of the given - /// component's ID - /// - Both `Relation::uid()` and `Target::uid()` are wildcards - /// - Neither `Relation::uid()` or `Target::uid()` are wildcards - pub fn new(world: &'world World, component_ref: EntityComponentRef<'world>) -> Self - where - Relation: ComponentOrWildcard, - Target: ComponentOrWildcard, - { - let component_id = component_ref.id(); - - assert!(component_id.kind() == UidKind::Pair); - - assert!( - Relation::uid() == Wildcard::uid() - || component_id.relation_component() == Relation::uid() - ); - - assert!( - Target::uid() == Wildcard::uid() - || component_id.target_component() == Target::uid() - ); - - assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid()); - - assert!( - !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid()) - ); - - WithWildcard { - world, - component_ref, - _pd: PhantomData, - } - } - - /// Returns the [`Uid`] of the pair. - #[must_use] - pub fn id(&self) -> Uid - { - self.component_ref.id() - } - - /// Attempts to get the component data of this pair, returning `None` if the `Data` - /// type is incorrect. - /// - /// # Panics - /// Will panic if the component data is mutably borrowed elsewhere. - #[must_use] - pub fn get_data<Data>(&self) -> Option<ComponentHandle<'_, Data>> - where - Data: 'static, - { - ComponentHandle::<Data>::from_entity_component_ref(&self.component_ref) - .map_or_else( - |err| match err { - ComponentHandleError::IncorrectType => None, - err @ ComponentHandleError::AcquireLockFailed(_) => { - panic!( - "Creating handle to pair data as component {} failed: {err}", - type_name::<Data>() - ); - } - }, - Some, - ) - } - - /// Attempts to get the component data of this pair, returning `None` if the `Data` - /// type is incorrect. - /// - /// # Panics - /// Will panic if the component data is borrowed elsewhere. - #[must_use] - pub fn get_data_mut<Data>(&self) -> Option<ComponentHandleMut<'_, Data>> - where - Data: 'static, - { - ComponentHandleMut::<Data>::from_entity_component_ref( - &self.component_ref, - self.world, - ) - .map_or_else( - |err| match err { - ComponentHandleError::IncorrectType => None, - err @ ComponentHandleError::AcquireLockFailed(_) => { - panic!( - "Creating handle to pair data as component {} failed: {err}", - type_name::<Data>() - ); - } - }, - Some, - ) - } -} - -impl<'world, Relation> WithWildcard<'world, Relation, Wildcard> -where - Relation: Component, -{ - /// Attempts to retrieve the target as a entity, returning `None` if not found. - #[must_use] - pub fn get_target_ent(&self) -> Option<EntityHandle<'world>> - { - let archetype = self - .world - .data - .component_storage - .get_entity_archetype(self.component_ref.id().target_entity())?; - - let Some(archetype_entity) = - archetype.get_entity_by_id(self.component_ref.id().target_entity()) - else { - unreachable!(); - }; - - Some(EntityHandle::new(archetype, archetype_entity, self.world)) - } - - /// Attempts to get the component data of this pair, returning `None` if the - /// `Relation` type is incorrect. - /// - /// # Panics - /// Will panic if the component data is mutably borrowed elsewhere. - #[must_use] - pub fn get_data_as_relation(&self) -> Option<ComponentHandle<'_, Relation>> - { - ComponentHandle::<Relation>::from_entity_component_ref(&self.component_ref) - .map_or_else( - |err| match err { - ComponentHandleError::IncorrectType => None, - err @ ComponentHandleError::AcquireLockFailed(_) => { - panic!( - "Creating handle to pair data as component {} failed: {err}", - type_name::<Relation>() - ); - } - }, - Some, - ) - } - - /// Attempts to get the component data of this pair, returning `None` if the - /// `Relation` type is incorrect. - /// - /// # Panics - /// Will panic if the component data is borrowed elsewhere. - #[must_use] - pub fn get_data_as_relation_mut(&self) -> Option<ComponentHandleMut<'_, Relation>> - { - ComponentHandleMut::<Relation>::from_entity_component_ref( - &self.component_ref, - self.world, - ) - .map_or_else( - |err| match err { - ComponentHandleError::IncorrectType => None, - err @ ComponentHandleError::AcquireLockFailed(_) => { - panic!( - "Creating handle to pair data as component {} failed: {err}", - type_name::<Relation>() - ); - } - }, - Some, - ) - } -} - -/// Used to access matching pairs in a entity containing zero or more matching pairs. -#[derive(Debug)] -pub struct MultipleWithWildcard<'a, Relation, Target> -{ - entity_handle: EntityHandle<'a>, - world: &'a World, - _pd: PhantomData<(Relation, Target)>, -} - -impl<'world, Relation, Target> MultipleWithWildcard<'world, Relation, Target> -{ - /// Returns a new `MultipleWithWildcard`. - /// - /// # Panics - /// This function will panic if: - /// - Both `Relation::uid()` and `Target::uid()` are wildcards - /// - Neither `Relation::uid()` or `Target::uid()` are wildcards - pub fn new(world: &'world World, entity_handle: EntityHandle<'world>) -> Self - where - Relation: ComponentOrWildcard, - Target: ComponentOrWildcard, - { - assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid()); - - assert!( - !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid()) - ); - - MultipleWithWildcard { - entity_handle, - world, - _pd: PhantomData, - } - } -} - -impl<'a, Relation: Component> MultipleWithWildcard<'a, Relation, Wildcard> -{ - #[must_use] - pub fn get_with_target_id( - &self, - target_id: Uid, - ) -> Option<WithWildcard<'a, Relation, Wildcard>> - { - Some(WithWildcard { - world: self.world, - component_ref: self - .entity_handle - .get_matching_components( - Pair::builder() - .relation::<Relation>() - .target_id(target_id) - .build() - .id(), - ) - .next()?, - _pd: PhantomData, - }) - } -} - -impl<'a, Relation: Component> IntoIterator - for MultipleWithWildcard<'a, Relation, Wildcard> -{ - type IntoIter = WithWildcardIter<'a, Relation, Wildcard>; - type Item = <Self::IntoIter as Iterator>::Item; - - fn into_iter(self) -> Self::IntoIter - { - WithWildcardIter { - inner: self - .entity_handle - .get_matching_components(Pair::<Relation, Wildcard>::uid()), - world: self.world, - _pd: PhantomData, - } - } -} - -/// Iterator of matching pairs in a entity. -pub struct WithWildcardIter<'a, Relation, Target> -{ - inner: EntityMatchingComponentIter<'a>, - world: &'a World, - _pd: PhantomData<(Relation, Target)>, -} - -impl<'a, Relation, Target> Iterator for WithWildcardIter<'a, Relation, Target> -{ - type Item = WithWildcard<'a, Relation, Target>; - - fn next(&mut self) -> Option<Self::Item> - { - let matching_comp = self.inner.next()?; - - Some(WithWildcard { - world: self.world, - component_ref: matching_comp, - _pd: PhantomData, - }) - } -} - -/// Relation denoting a dependency to another entity -#[derive(Debug, Default, Clone, Copy, Component)] -pub struct DependsOn; - -/// Relation denoting being the child of another entity. -#[derive(Debug, Default, Clone, Copy, Component)] -pub struct ChildOf; - -#[derive(Debug)] -pub struct Wildcard(Infallible); - -impl Wildcard -{ - #[must_use] - pub fn uid() -> Uid - { - Uid::wildcard() - } -} - -pub trait ComponentOrWildcard: sealed::Sealed -{ - fn uid() -> Uid; -} - -impl<ComponentT: Component> ComponentOrWildcard for ComponentT -{ - fn uid() -> Uid - { - ComponentT::id() - } -} - -impl<ComponentT: Component> sealed::Sealed for ComponentT {} - -impl ComponentOrWildcard for Wildcard -{ - fn uid() -> Uid - { - Wildcard::uid() - } -} - -impl sealed::Sealed for Wildcard {} - -mod sealed -{ - pub trait Sealed {} -} diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs deleted file mode 100644 index e8d9b71..0000000 --- a/ecs/src/phase.rs +++ /dev/null @@ -1,22 +0,0 @@ -use ecs_macros::Component; - -use crate::{World, declare_entity}; - -#[derive(Debug, Default, Clone, Copy, Component)] -pub struct Phase; - -declare_entity!(pub START, (Phase,)); -declare_entity!(pub PRE_UPDATE, (Phase,)); -declare_entity!(pub UPDATE, (Phase,)); -declare_entity!(pub POST_UPDATE, (Phase,)); - -pub(crate) fn spawn_entities(world: &mut World) -{ - world.create_declared_entity(&START); - world.create_declared_entity(&PRE_UPDATE); - world.create_declared_entity(&UPDATE); - world.create_declared_entity(&POST_UPDATE); -} - -#[derive(Debug, Component)] -pub(crate) struct HasSystem; diff --git a/ecs/src/query.rs b/ecs/src/query.rs deleted file mode 100644 index 5f13579..0000000 --- a/ecs/src/query.rs +++ /dev/null @@ -1,569 +0,0 @@ -use std::any::type_name; -use std::marker::PhantomData; - -use seq_macro::seq; - -use crate::component::{ - Component, - Handle as ComponentHandle, - HandleMut as ComponentHandleMut, -}; -use crate::entity::Handle as EntityHandle; -use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; -use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; -use crate::uid::{Kind as UidKind, Uid, With as WithUid}; -use crate::util::array_vec::ArrayVec; -use crate::util::Array; -use crate::World; - -pub mod flexible; -pub mod term; - -// A term tuple type can have a maximum of 17 elements -pub const MAX_TERM_CNT: usize = 17; - -#[derive(Debug)] -pub struct Query<'world, FieldTerms, FieldlessTerms = ()> -where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, -{ - inner: FlexibleQuery<'world, MAX_TERM_CNT>, - _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.inner.world(), - inner: 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.inner.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.inner.world(), - inner: 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()) - } - - /// Returns a new `Query` created from a [`FlexibleQuery`]. - /// - /// # Important notes - /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms - /// in the given [`FlexibleQuery`], otherwise any method call or iterating might - /// panic. - #[must_use] - pub fn from_flexible_query( - flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>, - ) -> Self - { - // TODO: Check compatability of terms - - Self { - inner: flexible_query, - _pd: PhantomData, - } - } - - 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 { - inner: world.flexible_query(terms_builder.build()), - _pd: PhantomData, - } - } -} - -impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator - for &'query Query<'world, FieldTerms, FieldlessTerms> -where - FieldTerms: TermWithFieldTuple, - 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 new(world: &'world World, _system_metadata: &SystemMetadata) -> 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)] -#[must_use] -pub struct TermsBuilder<const MAX_TERM_CNT: usize> -{ - required_components: ArrayVec<Uid, MAX_TERM_CNT>, - excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, -} - -#[allow(clippy::return_self_not_must_use)] -pub trait TermsBuilderInterface -{ - fn with<WithUidT: WithUid>(self) -> Self; - - fn without<WithUidT: WithUid>(self) -> Self; - - fn with_required(self, ids: impl Array<Uid>) -> Self; - - fn without_ids(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<WithUidT: WithUid>(mut self) -> Self - { - let insert_index = self.required_components - .partition_point(|id| *id <= WithUidT::uid()); - - self.required_components - .insert(insert_index, WithUidT::uid()); - - self - } - - #[allow(unused_mut)] - fn without<WithUidT: WithUid>(mut self) -> Self - { - let insert_index = self.excluded_components - .partition_point(|id| *id <= WithUidT::uid()); - - self.excluded_components - .insert(insert_index, WithUidT::uid()); - - 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.is_empty() { - 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 - } - - #[allow(unused_mut)] - fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self - { - if !ids.as_ref().is_sorted() { - ids.as_mut().sort(); - } - - if self.excluded_components.is_empty() { - self.excluded_components.extend(ids); - return self; - } - - let mut id_iter = ids.into_iter(); - - while let Some(id) = id_iter.next() { - let insert_index = self.excluded_components - .partition_point(|other_id| *other_id <= id); - - if insert_index == self.excluded_components.len() { - self.excluded_components.extend([id].into_iter().chain(id_iter)); - - return self; - } - - self.excluded_components - .insert(insert_index, id); - - } - - self - } -} - -impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT> -{ - #[must_use] - pub fn build(self) -> Terms<MAX_TERM_CNT> - { - debug_assert!(self.required_components.is_sorted()); - debug_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<ComponentT: Component> TermWithField for &ComponentT -{ - type Field<'a> = ComponentHandle<'a, ComponentT>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with::<ComponentT>(); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - _world: &'world World, - ) -> Self::Field<'world> - { - assert_eq!(ComponentT::id().kind(), UidKind::Component); - - let Some(component) = entity_handle - .get_matching_components(ComponentT::id()) - .next() - else { - panic!( - concat!( - "Component {} was not found in entity {}. There ", - "is most likely a bug in the entity querying" - ), - type_name::<ComponentT>(), - entity_handle.uid() - ); - }; - - Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }) - } -} - -impl<ComponentT: Component> TermWithField for &mut ComponentT -{ - type Field<'a> = ComponentHandleMut<'a, ComponentT>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with::<ComponentT>(); - } - - fn get_field<'world>( - entity_handle: &EntityHandle<'world>, - world: &'world World, - ) -> Self::Field<'world> - { - assert_eq!(ComponentT::id().kind(), UidKind::Component); - - let Some(component) = entity_handle - .get_matching_components(ComponentT::id()) - .next() - else { - panic!( - concat!( - "Component {} was not found in entity {}. There ", - "is most likely a bug in the entity querying" - ), - type_name::<ComponentT>(), - entity_handle.uid() - ); - }; - - Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }) - } -} - -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, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, -{ - world: &'world World, - inner: EntityHandleIter, - comps_pd: PhantomData<FieldTerms>, -} - -impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - type Item = FieldTerms::Fields<'query>; - - fn next(&mut self) -> Option<Self::Item> - { - let entity_handle = self.inner.next()?; - - Some(FieldTerms::get_fields(&entity_handle, self.world)) - } -} - -pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple, - 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, - 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 936ab82..0000000 --- a/ecs/src/query/flexible.rs +++ /dev/null @@ -1,92 +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}; -use crate::entity::Handle as EntityHandle; -use crate::query::Terms; -use crate::World; - -/// Low-level entity query structure. -#[derive(Debug)] -pub struct Query<'world, const MAX_TERM_CNT: usize> -{ - world: &'world World, - 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 - .world - .data - .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, - ), - world: self.world, - } - } - - #[must_use] - pub fn world(&self) -> &'world World - { - self.world - } - - pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self - { - Self { world, terms } - } -} - -impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT> -{ - type IntoIter = Iter<'query>; - type Item = EntityHandle<'query>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -pub struct Iter<'query> -{ - iter: QueryEntityIter<'query>, - world: &'query World, -} - -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, self.world)) - } -} - -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 0683918..0000000 --- a/ecs/src/query/term.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::any::type_name; -use std::marker::PhantomData; - -use crate::component::{ - Component, - Handle as ComponentHandle, - HandleMut as ComponentHandleMut, -}; -use crate::query::{ - TermWithField, - TermWithoutField, - TermsBuilder, - TermsBuilderInterface, -}; -use crate::uid::With as WithUid; - -pub struct With<WithUidT> -where - WithUidT: WithUid, -{ - _pd: PhantomData<WithUidT>, -} - -impl<WithUidT> TermWithoutField for With<WithUidT> -where - WithUidT: WithUid, -{ - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.with::<WithUidT>(); - } -} - -pub struct Without<WithUidT> -where - WithUidT: WithUid, -{ - _pd: PhantomData<WithUidT>, -} - -impl<WithUidT> TermWithoutField for Without<WithUidT> -where - WithUidT: WithUid, -{ - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - terms_builder.without::<WithUidT>(); - } -} - -impl<ComponentT: Component> TermWithField for Option<&ComponentT> -{ - type Field<'a> = Option<ComponentHandle<'a, ComponentT>>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - } - - fn get_field<'world>( - entity_handle: &crate::entity::Handle<'world>, - _world: &'world crate::World, - ) -> Self::Field<'world> - { - Some( - ComponentHandle::<'world, ComponentT>::from_entity_component_ref( - &entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }), - ) - } -} - -impl<ComponentT: Component> TermWithField for Option<&mut ComponentT> -{ - type Field<'a> = Option<ComponentHandleMut<'a, ComponentT>>; - - fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( - _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, - ) - { - } - - fn get_field<'world>( - entity_handle: &crate::entity::Handle<'world>, - world: &'world crate::World, - ) -> Self::Field<'world> - { - Some( - ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref( - &entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - world, - ) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::<ComponentT>() - ); - }), - ) - } -} diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs deleted file mode 100644 index 82e5e0f..0000000 --- a/ecs/src/sole.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::any::{Any, type_name}; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -use crate::World; -use crate::lock::{Lock, WriteGuard}; -use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; - -/// A type which has a single instance and is shared globally. -pub trait Sole: Any -{ - 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() - } -} - -/// Holds a reference to a globally shared singleton value. -#[derive(Debug)] -pub struct Single<'world, SoleT: Sole> -{ - sole: WriteGuard<'world, Box<dyn Sole>>, - _ph: PhantomData<SoleT>, -} - -impl<'world, SoleT> Single<'world, SoleT> -where - SoleT: Sole, -{ - pub(crate) 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>() - ) - }), - _ph: PhantomData, - } - } -} - -impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT> -where - SoleT: Sole, -{ - type Input = (); - - fn new(world: &'world World, _system_metadata: &SystemMetadata) -> 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() - } -} 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 5d3e0bf..0000000 --- a/ecs/src/system.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::fmt::Debug; - -use ecs_macros::Component; -use seq_macro::seq; - -use crate::World; -use crate::error::Error; -use crate::uid::Uid; - -pub mod initializable; -pub mod observer; -pub mod stateful; - -/// Metadata of a system. -#[derive(Debug)] -#[non_exhaustive] -pub struct Metadata -{ - pub ent_id: Uid, -} - -pub trait System<'world, Impl>: 'static -{ - type Callbacks: Callbacks; - - fn finish(self) -> (TypeErased, Self::Callbacks); -} - -macro_rules! impl_system { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret> - for Func - where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world, Input = ()>,)* - { - type Callbacks = NoCallbacks; - - fn finish(self) -> (TypeErased, Self::Callbacks) - { - #![allow(unused)] - - let type_erased = TypeErased { - run: Box::new(move |world, metadata| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - self(#({ - TParam~I::new(world, &metadata) - },)*).into_result() - }), - name: std::any::type_name::<Self>() - }; - - (type_erased, NoCallbacks) - } - } - }); - }; -} - -seq!(C in 0..16 { - impl_system!(C); -}); - -pub trait Into<'world, Impl> -{ - type System; - - fn into_system(self) -> Self::System; -} - -pub struct TypeErased -{ - run: Box<TypeErasedRunFn>, - name: &'static str, -} - -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, metadata: Metadata) -> Result<(), Error> - { - (self.run)(world, metadata) - } - - pub fn name(&self) -> &'static str - { - self.name - } -} - -impl Debug for TypeErased -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - formatter.debug_struct("TypeErased").finish_non_exhaustive() - } -} - -pub trait ReturnValue -{ - fn into_result(self) -> Result<(), Error>; -} - -impl ReturnValue for () -{ - fn into_result(self) -> Result<(), Error> - { - Ok(()) - } -} - -impl ReturnValue for Result<(), Error> -{ - fn into_result(self) -> Result<(), Error> - { - self - } -} - -/// A parameter to a [`System`]. -pub trait Param<'world> -{ - type Input; - - fn new(world: &'world World, system_metadata: &Metadata) -> Self; -} - -/// A type which can be used as input to a [`System`]. -pub trait Input: 'static {} - -pub trait Callbacks -{ - fn on_created(&mut self, world: &mut World, metadata: Metadata); -} - -pub struct NoCallbacks; - -impl Callbacks for NoCallbacks -{ - fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {} -} - -#[derive(Debug, Component)] -pub(crate) struct SystemComponent -{ - pub(crate) system: TypeErased, -} - -/// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&World, Metadata) -> Result<(), Error>; diff --git a/ecs/src/system/initializable.rs b/ecs/src/system/initializable.rs deleted file mode 100644 index b6ec8e8..0000000 --- a/ecs/src/system/initializable.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::marker::PhantomData; - -use seq_macro::seq; - -use crate::system::{Input, Param as SystemParam, System}; -use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple}; - -/// A initializable system. -pub trait Initializable<'world, Impl>: System<'world, Impl> -{ - type Inputs; - - #[must_use] - fn initialize(self, inputs: Self::Inputs) -> Self; -} - -pub trait Param<'world, SystemT>: SystemParam<'world> -{ - fn initialize(system: &mut SystemT, input: Self::Input); -} - -pub struct ParamTupleFilter<'world, SystemT> -{ - _pd: PhantomData<(&'world (), SystemT)>, -} - -impl<'world, SystemT, ParamT, Accumulator> - TupleReduceElement<Accumulator, ParamTupleFilter<'world, SystemT>> for ParamT -where - ParamT: SystemParam< - 'world, - Input: AppendInitializableParam<'world, Accumulator, ParamT, SystemT>, - >, - Accumulator: Tuple, -{ - type Return = <ParamT::Input as AppendInitializableParam< - 'world, - Accumulator, - ParamT, - SystemT, - >>::Return; -} - -pub trait AppendInitializableParam<'world, Accumulator, ParamT, SystemT> -{ - type Return; -} - -impl<'world, InputT, ParamT, Accumulator, SystemT> - AppendInitializableParam<'world, Accumulator, ParamT, SystemT> for InputT -where - InputT: Input, - Accumulator: Tuple, - ParamT: Param<'world, SystemT>, -{ - type Return = Accumulator::WithElementAtEnd<ParamT>; -} - -impl<ParamT, Accumulator, SystemT> - AppendInitializableParam<'_, Accumulator, ParamT, SystemT> for () -where - Accumulator: Tuple, -{ - type Return = Accumulator; -} - -pub trait ParamTuple<'world, SystemT> -{ - type Inputs; - - fn initialize_all(system: &mut SystemT, inputs: Self::Inputs); -} - -macro_rules! impl_initializable_param_tuple { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, SystemT, #(Param~I,)*> ParamTuple<'world, SystemT> - for (#(Param~I,)*) - where - #(Param~I: Param<'world, SystemT>,)* - { - type Inputs = (#(Param~I::Input,)*); - - fn initialize_all( - system: &mut SystemT, - inputs: Self::Inputs, - ) { - #( - <Param~I as Param<'world, SystemT>>::initialize( - system, - inputs.I - ); - )* - } - } - }); - }; -} - -seq!(C in 1..16 { - impl_initializable_param_tuple!(C); -}); - -impl<SystemT> ParamTuple<'_, SystemT> for () -{ - type Inputs = (); - - fn initialize_all(_system: &mut SystemT, _inputs: Self::Inputs) {} -} - -/// A tuple of system parameters that may or may not be initializable. -pub trait MaybeInitializableParamTuple<'world, SystemT> -{ - /// A tuple of the inputs of the initializable system parameters in this tuple. - type Inputs; - - fn init_initializable(system: &mut SystemT, inputs: Self::Inputs); -} - -impl<'world, SystemT, Params> MaybeInitializableParamTuple<'world, SystemT> for Params -where - Params: - TupleReduce<ParamTupleFilter<'world, SystemT>, Out: ParamTuple<'world, SystemT>>, -{ - type Inputs = <Params::Out as ParamTuple<'world, SystemT>>::Inputs; - - fn init_initializable(system: &mut SystemT, inputs: Self::Inputs) - { - Params::Out::initialize_all(system, inputs); - } -} diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs deleted file mode 100644 index 6893b0f..0000000 --- a/ecs/src/system/observer.rs +++ /dev/null @@ -1,280 +0,0 @@ -use std::any::type_name; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::mem::transmute; -use std::slice::Iter as SliceIter; - -use ecs_macros::Component; -use seq_macro::seq; - -use crate::World; -use crate::component::Component; -use crate::entity::Handle as EntityHandle; -use crate::error::Error; -use crate::event::Emitted as EmittedEvent; -use crate::pair::Pair; -use crate::system::{ - Metadata, - NoCallbacks, - Param, - ReturnValue as SystemReturnValue, - System, - TypeErased as TypeErasedSystem, -}; -use crate::uid::Uid; -use crate::util::Array; - -pub trait Observed -{ - type Events: Array<Pair<Uid, Uid>>; - - fn events() -> Self::Events; -} - -impl<Relation, Target> Observed for Pair<Relation, Target> -where - Relation: Component, - Target: Component, -{ - type Events = [Pair<Uid, Uid>; 1]; - - fn events() -> Self::Events - { - [Pair::builder() - .relation::<Relation>() - .target::<Target>() - .build()] - } -} - -/// Observer system. -pub trait Observer<'world, Impl>: System<'world, Impl> -{ - type ObservedEvents: Array<Pair<Uid, Uid>>; - - fn observed_events() -> Self::ObservedEvents; - - fn finish_observer(self) -> (WrapperComponent, Self::Callbacks); -} - -pub struct Observe<'world, ObservedT: Observed> -{ - _pd: PhantomData<ObservedT>, - world: &'world World, - emitted_event: EmittedEvent<'world>, -} - -impl<'world, ObservedT: Observed> Observe<'world, ObservedT> -{ - pub fn new(world: &'world World, emitted_event: EmittedEvent<'world>) -> Self - { - Self { - _pd: PhantomData, - world, - emitted_event, - } - } - - #[must_use] - pub fn event(&self) -> Uid - { - self.emitted_event.event - } -} - -impl<ObservedT: Observed> Observe<'_, ObservedT> -{ - #[must_use] - pub fn iter(&self) -> ObserveIter<'_, ObservedT> - { - ObserveIter { - world: self.world, - inner: self.emitted_event.match_ids.iter(), - _pd: PhantomData, - } - } -} - -impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT> -{ - type IntoIter = ObserveIter<'a, ObservedT>; - type Item = <Self::IntoIter as Iterator>::Item; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -pub struct ObserveIter<'observe, ObservedT: Observed> -{ - world: &'observe World, - inner: SliceIter<'observe, Uid>, - _pd: PhantomData<ObservedT>, -} - -impl<'observe, ObservedT: Observed> Iterator for ObserveIter<'observe, ObservedT> -{ - type Item = EventMatch<'observe, ObservedT>; - - fn next(&mut self) -> Option<Self::Item> - { - let match_id = *self.inner.next()?; - - Some(EventMatch { - world: self.world, - id: match_id, - _pd: PhantomData, - }) - } -} - -/// A event match. -#[derive(Debug)] -pub struct EventMatch<'world, ObservedT: Observed> -{ - world: &'world World, - id: Uid, - _pd: PhantomData<ObservedT>, -} - -impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT> -{ - #[must_use] - pub fn entity_id(&self) -> Uid - { - self.id - } - - /// Attempts to get the entity with the id of this match. - #[must_use] - pub fn try_get_entity(&self) -> Option<EntityHandle<'world>> - { - self.world.get_entity(self.id) - } -} - -macro_rules! impl_observer { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Func - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: SystemReturnValue, - #(TParam~I: Param<'world, Input = ()>,)* - { - type Callbacks = NoCallbacks; - - fn finish(self) -> (TypeErasedSystem, NoCallbacks) - { - const { - panic!("Observers cannot be used as regular systems"); - } - } - } - - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Func - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: SystemReturnValue, - #(TParam~I: Param<'world, Input = ()>,)* - { - type ObservedEvents = ObservedT::Events; - - fn observed_events() -> Self::ObservedEvents - { - ObservedT::events() - } - - fn finish_observer(self) -> (WrapperComponent, NoCallbacks) - { - #[allow(unused)] - - let wrapper_comp = WrapperComponent::new( - move |world, metadata, emitted_event| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let emitted_event = unsafe { - transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( - emitted_event - ) - }; - - self(Observe::new(world, emitted_event), #({ - TParam~I::new(world, &metadata) - },)*).into_result() - }, - type_name::<Func>() - ); - - (wrapper_comp, NoCallbacks) - } - } - }); - }; -} - -seq!(C in 0..16 { - impl_observer!(C); -}); - -#[derive(Component)] -pub struct WrapperComponent -{ - run: Box<RunFn>, - name: &'static str, -} - -impl WrapperComponent -{ - pub fn new( - run: impl Fn(&World, Metadata, EmittedEvent<'_>) -> Result<(), Error> + 'static, - name: &'static str, - ) -> Self - { - Self { run: Box::new(run), name } - } - - /// Runs the observer system. - /// - /// # Safety - /// `world` must live at least as long as the [`World`] the system belongs to. - pub unsafe fn run( - &self, - world: &World, - metadata: Metadata, - emitted_event: EmittedEvent<'_>, - ) -> Result<(), Error> - { - (self.run)(world, metadata, emitted_event) - } - - pub fn name(&self) -> &'static str - { - self.name - } -} - -impl Debug for WrapperComponent -{ - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("WrapperComponent") - .finish_non_exhaustive() - } -} - -type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>) -> Result<(), Error>; diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs deleted file mode 100644 index 3e0076a..0000000 --- a/ecs/src/system/stateful.rs +++ /dev/null @@ -1,269 +0,0 @@ -use std::any::type_name; -use std::mem::transmute; - -use seq_macro::seq; - -use crate::World; -use crate::component::Parts as ComponentParts; -use crate::component::local::SystemWithLocalComponents; -use crate::event::Emitted as EmittedEvent; -use crate::system::initializable::{Initializable, MaybeInitializableParamTuple}; -use crate::system::observer::{ - Observe, - Observed, - Observer, - WrapperComponent as ObserverWrapperComponent, -}; -use crate::system::{ - Into as IntoSystem, - Metadata, - Param, - ReturnValue, - System, - TypeErased, -}; - -/// A stateful system. -pub struct Stateful<Func> -{ - func: Func, - local_components: Vec<ComponentParts>, -} - -macro_rules! impl_system { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, Func, Ret, #(TParam~I,)*> - System<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> - where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world, Input: 'static>,)* - { - type Callbacks = Callbacks; - - fn finish(self) -> (TypeErased, Self::Callbacks) - { - let Self { func, local_components } = self; - - let callbacks = Callbacks { local_components }; - - let type_erased = TypeErased { - run: Box::new(move |world, metadata| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - func(#({ - TParam~I::new(&world, &metadata) - },)*); - - Ok(()) - }), - name: type_name::<Func>() - }; - - - (type_erased, callbacks) - } - } - - impl<'world, Func, Ret, #(TParam~I,)*> - Initializable<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> - where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world, Input: 'static>,)* - (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> - { - type Inputs = < - (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> - >::Inputs; - - fn initialize(mut self, inputs: Self::Inputs) -> Self - { - init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); - - self - } - } - - impl<'world, Func, Ret, #(TParam~I,)*> - IntoSystem<'world, fn(#(TParam~I,)*) -> Ret> for Func - where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - { - type System = Stateful<Func>; - - fn into_system(self) -> Self::System - { - Self::System { - func: self, - local_components: Vec::new(), // TODO: Use Vec::with_capacity - } - } - } - }); - }; -} - -seq!(C in 1..16 { - impl_system!(C); -}); - -impl<Func> SystemWithLocalComponents for Stateful<Func> -{ - fn add_local_component(&mut self, component_parts: ComponentParts) - { - self.local_components.push(component_parts); - } -} - -#[derive(Debug)] -pub struct Callbacks -{ - local_components: Vec<ComponentParts>, -} - -impl crate::system::Callbacks for Callbacks -{ - fn on_created(&mut self, world: &mut World, metadata: Metadata) - { - for local_comp_parts in self.local_components.drain(..) { - world.add_component(metadata.ent_id, local_comp_parts); - } - } -} - -fn init_initializable_params<'world, SystemT, Params>( - system: &mut SystemT, - inputs: Params::Inputs, -) where - Params: MaybeInitializableParamTuple<'world, SystemT>, -{ - Params::init_initializable(system, inputs); -} - -macro_rules! impl_observer { - ($c: tt) => { - seq!(I in 0..$c { - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Stateful<Func> - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - { - type Callbacks = Callbacks; - - fn finish(self) -> (TypeErased, Callbacks) - { - unimplemented!(); - } - } - - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Initializable< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Stateful<Func> - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> - { - type Inputs = < - (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> - >::Inputs; - - fn initialize(mut self, inputs: Self::Inputs) -> Self - { - init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); - - self - } - } - - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Stateful<Func> - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - { - type ObservedEvents = ObservedT::Events; - - fn observed_events() -> Self::ObservedEvents - { - ObservedT::events() - } - - fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks) - { - #![allow(unused)] - - let Self { func, local_components } = self; - - let callbacks = Callbacks { local_components }; - - let wrapper_comp = ObserverWrapperComponent::new( - move |world, metadata, emitted_event| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let emitted_event = unsafe { - transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( - emitted_event - ) - }; - - func(Observe::new(world, emitted_event), #({ - TParam~I::new(world, &metadata) - },)*).into_result() - }, - type_name::<Func>() - ); - - (wrapper_comp, callbacks) - } - } - - impl<'world, Func, Ret, ObservedT, #(TParam~I,)*> IntoSystem< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Func - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - { - type System = Stateful<Func>; - - fn into_system(self) -> Stateful<Func> - { - Stateful { - func: self, - local_components: Vec::new(), // TODO: Use Vec::with_capacity - } - } - } - }); - }; -} - -seq!(C in 0..16 { - impl_observer!(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/uid.rs b/ecs/src/uid.rs deleted file mode 100644 index bb393a1..0000000 --- a/ecs/src/uid.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::fmt::{Debug, Display, Formatter}; -use std::mem::transmute; -use std::sync::atomic::{AtomicU32, Ordering}; - -use seq_macro::seq; - -use crate::component::Component; -use crate::util::{gen_mask_64, Array, BitMask, NumberExt}; - -static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID); - -static WILDCARD_ID: u32 = 1; - -const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63)); -const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31)); -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 -{ - Pair = 3, - Entity = 2, - Component = 1, -} - -/// A unique identifier. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Uid -{ - inner: u64, -} - -impl Uid -{ - /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are - /// reserved. - pub const FIRST_UNIQUE_ID: u32 = 5; - - /// 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 wildcard() -> Self - { - Self { - inner: ID_BITS.field_prep(u64::from(WILDCARD_ID)) - | KIND_BITS.field_prep(Kind::Component as u64), - } - } - - /// Returns a new pair UID. - /// - /// # Panics - /// Will panic if either the given relation or target is a pair UID. - #[must_use] - pub fn new_pair(params: &PairParams) -> Self - { - assert_ne!( - params.relation.kind(), - Kind::Pair, - "Pair relation cannot be a pair" - ); - - assert_ne!( - params.target.kind(), - Kind::Pair, - "Pair target cannot be a pair" - ); - - Self { - inner: ID_BITS.field_prep(u64::from(params.target.id())) - | RELATION_BITS.field_prep(u64::from(params.relation.id())) - | KIND_BITS.field_prep(Kind::Pair 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) } - } - - /// If this `Uid` is a pair, returns the relation as a component `Uid`. - /// - /// # Panics - /// Will panic if this `Uid` is not a pair. - #[must_use] - pub fn relation_component(&self) -> Self - { - assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); - - Self { - inner: ID_BITS.field_prep(u64::from(self.relation())) - | KIND_BITS.field_prep(Kind::Component as u64), - } - } - - #[must_use] - pub fn has_same_relation_as(&self, other: Self) -> bool - { - self.relation() == other.relation() - } - - /// If this `Uid` is a pair, returns the relation as a entity `Uid`. - /// - /// # Panics - /// Will panic if this `Uid` is not a pair. - #[must_use] - pub fn relation_entity(&self) -> Self - { - assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); - - Self { - inner: ID_BITS.field_prep(u64::from(self.relation())) - | KIND_BITS.field_prep(Kind::Entity as u64), - } - } - - /// If this `Uid` is a pair, returns the target as a component `Uid`. - /// - /// # Panics - /// Will panic if this `Uid` is not a pair. - #[must_use] - pub fn target_component(&self) -> Self - { - assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); - - Self { - inner: ID_BITS.field_prep(u64::from(self.id())) - | KIND_BITS.field_prep(Kind::Component as u64), - } - } - - /// If this `Uid` is a pair, returns the target as a entity `Uid`. - /// - /// # Panics - /// Will panic if this `Uid` is not a pair. - #[must_use] - pub fn target_entity(&self) -> Self - { - assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); - - Self { - inner: ID_BITS.field_prep(u64::from(self.id())) - | KIND_BITS.field_prep(Kind::Entity as u64), - } - } - - fn relation(self) -> u32 - { - let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else { - unreachable!("Uid relation does not fit in u32"); - }; - - relation - } -} - -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() - } -} - -impl Display for Uid -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - if self.kind() == Kind::Pair { - return write!( - formatter, - "({}, {})", - self.relation(), - self.target_component() - ); - } - - if *self == Uid::wildcard() { - return write!(formatter, "*"); - } - - write!(formatter, "{}", self.id()) - } -} - -#[derive(Debug, Clone)] -pub struct PairParams -{ - pub relation: Uid, - pub target: Uid, -} - -pub trait With -{ - fn uid() -> Uid; -} - -impl<ComponentT: Component> With for ComponentT -{ - fn uid() -> Uid - { - Self::id() - } -} - -pub trait WithUidTuple -{ - type UidsArray: Array<Uid>; - - fn uids() -> Self::UidsArray; -} - -macro_rules! impl_with_uid_tuple { - ($c: tt) => { - seq!(I in 0..=$c { - impl<#(WithUid~I: With,)*> WithUidTuple for (#(WithUid~I,)*) - { - type UidsArray = [Uid; $c + 1]; - - fn uids() -> Self::UidsArray - { - [#(WithUid~I::uid(),)*] - } - } - }); - }; -} - -seq!(C in 0..=16 { - impl_with_uid_tuple!(C); -}); diff --git a/ecs/src/util.rs b/ecs/src/util.rs deleted file mode 100644 index 27e9748..0000000 --- a/ecs/src/util.rs +++ /dev/null @@ -1,415 +0,0 @@ -use std::hash::Hash; -use std::mem::transmute; -use std::ops::{BitAnd, Deref}; - -use hashbrown::HashMap; - -pub(crate) mod array_vec; - -pub trait VecExt<Item> -{ - fn insert_at_part_pt_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_part_pt_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; - -macro_rules! impl_multiple { - ( - $type: ident, - ($( - impl$(<$($generic: tt$(: $bound: ident)?),*>)? - _<$($lt_param: lifetime),*><$($type_param: ty),*> - $(($($extra_cb_arg: expr),*))? - ),*) - cb=( - type_params=($($ty_param_matcher: ident),*) - $(, $($extra_matcher: ident),+)? - ) => { - $($item_tt: tt)* - } - ) => { - const _: () = { - $crate::util::impl_multiple!( - @(make_gen_item_macro) - _gen_multiple_impl_item, - ($($ty_param_matcher),*), - ($($($extra_matcher),+)?), - ($($item_tt)*) - ); - - $( - impl $(<$($generic$(: $bound)?,)*>)? $type<$($lt_param,)* $($type_param),*> - { - _gen_multiple_impl_item!( - type_params=($($type_param),*), - $($($extra_cb_arg),*)? - ); - } - )* - }; - }; - - ( - @(make_gen_item_macro) - $name: ident, - ($($ty_param_matcher: ident),*), - ($($extra_matcher: ident),*), - ($($transcriber: tt)*) - ) => { - $crate::util::impl_multiple!( - @(make_gen_item_macro) - ($), - $name, - ($($ty_param_matcher),*), - ($($extra_matcher),*), - ($($transcriber)*) - ); - }; - - ( - @(make_gen_item_macro) - ($dollar: tt), - $name: ident, - ($($ty_param_matcher: ident),*), - ($($extra_matcher: ident),*), - ($($transcriber: tt)*) - ) => { - $crate::util::impl_multiple!( - @(make_gen_item_macro) - $name, - ( - type_params=($($dollar$ty_param_matcher: ty),*), - $($dollar$extra_matcher: expr),* - ) => { - $($transcriber)* - } - ); - }; - - (@(make_gen_item_macro) $name: ident, $($rule: tt)*) => { - macro_rules! $name { - $($rule)* - } - }; -} - -pub(crate) use impl_multiple; - -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 5d0aac9..0000000 --- a/ecs/src/util/array_vec.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::mem::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> -{ - #[inline] - #[must_use] - pub fn len(&self) -> usize - { - self.len - } - - #[inline] - #[must_use] - pub fn is_empty(&self) -> bool - { - self.len == 0 - } - - 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( - &raw const self.items[index], - &raw 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] - { - let ptr = &raw const self.items[..self.len]; - - unsafe { &*(ptr as *const [Item]) } - } -} - -impl<Item, const CAPACITY: usize> AsMut<[Item]> for ArrayVec<Item, CAPACITY> -{ - fn as_mut(&mut self) -> &mut [Item] - { - let ptr = &raw mut self.items[..self.len]; - - unsafe { &mut *(ptr as *mut [Item]) } - } -} - -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, - } - } -} - -impl<Item, const CAPACITY: usize> Drop for ArrayVec<Item, CAPACITY> -{ - fn drop(&mut self) - { - for item in &mut self.items[..self.len] { - // SAFETY: The items from index 0 to the length index will always be - // initialized and satisfy all the invariants of the Item type. - unsafe { - item.assume_init_drop(); - } - } - } -} diff --git a/ecs/tests/phase.rs b/ecs/tests/phase.rs deleted file mode 100644 index af2646b..0000000 --- a/ecs/tests/phase.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; - -use ecs::component::local::Local; -use ecs::phase::UPDATE; -use ecs::system::Into; -use ecs::system::initializable::Initializable; -use ecs::{Component, World}; - -#[derive(Component)] -struct Thing; - -#[test] -fn system_run_order_correct_when_one_has_local_comp() -{ - static COUNTER: AtomicUsize = AtomicUsize::new(0); - - fn first_system(_thing: Local<Thing>) - { - assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 0); - } - - fn second_system() - { - assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 1); - } - - let mut world = World::new(); - - world.register_system(*UPDATE, first_system.into_system().initialize((Thing,))); - - world.register_system(*UPDATE, second_system); - - world.step(); - - assert_eq!(COUNTER.load(Ordering::Relaxed), 2); -} diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs deleted file mode 100644 index 7b218e3..0000000 --- a/ecs/tests/query.rs +++ /dev/null @@ -1,413 +0,0 @@ -use ecs::component::Component; -use ecs::pair::{Pair, Wildcard}; -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(), Uid::FIRST_UNIQUE_ID); - assert_eq!(B::id().id(), Uid::FIRST_UNIQUE_ID + 1); - assert_eq!(C::id().id(), Uid::FIRST_UNIQUE_ID + 2); - assert_eq!(D::id().id(), Uid::FIRST_UNIQUE_ID + 3); - assert_eq!(E::id().id(), Uid::FIRST_UNIQUE_ID + 4); - assert_eq!(F::id().id(), Uid::FIRST_UNIQUE_ID + 5); - assert_eq!(G::id().id(), Uid::FIRST_UNIQUE_ID + 6); - }); -} - -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], - ); -} - -#[test] -fn query_with_wildcard_target_pair() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, C)); - - world.create_entity((B,)); - - let ent_2_id = world.create_entity(( - B, - Pair::builder().relation::<G>().target_id(ent_1_id).build(), - )); - - world.create_entity(( - B, - Pair::builder().relation::<F>().target_id(ent_1_id).build(), - )); - world.create_entity(( - B, - A, - C, - Pair::builder().relation::<F>().target_id(ent_1_id).build(), - )); - - let ent_3_id = world.create_entity(( - B, - Pair::builder().relation::<G>().target_id(ent_2_id).build(), - )); - - let ent_4_id = world.create_entity(( - B, - E, - Pair::builder().relation::<G>().target_as_data(D).build(), - )); - - assert_query_finds_ents( - world.query::<(&B, Pair<G, Wildcard>), ()>(), - vec![ent_2_id, ent_3_id, ent_4_id], - ); -} - -#[test] -fn query_with_component_target_pair() -{ - setup(); - - let _test_lock = TEST_LOCK.lock(); - - let mut world = World::new(); - - let ent_1_id = world.create_entity((A, C)); - - world.create_entity((B,)); - - world.create_entity(( - B, - Pair::builder().relation::<G>().target_id(ent_1_id).build(), - )); - - world.create_entity(( - B, - Pair::builder().relation::<F>().target_id(ent_1_id).build(), - )); - world.create_entity(( - B, - A, - C, - Pair::builder().relation::<F>().target_id(ent_1_id).build(), - )); - - let ent_2_id = world - .create_entity((B, Pair::builder().relation::<G>().target_as_data(F).build())); - - let ent_3_id = world.create_entity(( - B, - E, - Pair::builder().relation::<G>().target_as_data(F).build(), - )); - - assert_query_finds_ents( - world.query::<(&B, Pair<G, &F>), ()>(), - vec![ent_2_id, ent_3_id], - ); -} |
