summaryrefslogtreecommitdiff
path: root/ecs
diff options
context:
space:
mode:
Diffstat (limited to 'ecs')
-rw-r--r--ecs/Cargo.toml34
-rw-r--r--ecs/benches/query.rs141
-rw-r--r--ecs/examples/component_changed_event.rs78
-rw-r--r--ecs/examples/component_events.rs64
-rw-r--r--ecs/examples/component_relationship.rs65
-rw-r--r--ecs/examples/component_removed_event.rs46
-rw-r--r--ecs/examples/error_handling.rs79
-rw-r--r--ecs/examples/event_loop.rs120
-rw-r--r--ecs/examples/extension.rs70
-rw-r--r--ecs/examples/multiple_queries.rs85
-rw-r--r--ecs/examples/optional_component.rs81
-rw-r--r--ecs/examples/relationship.rs56
-rw-r--r--ecs/examples/simple.rs42
-rw-r--r--ecs/examples/with_local.rs70
-rw-r--r--ecs/examples/with_sole.rs61
-rw-r--r--ecs/src/actions.rs174
-rw-r--r--ecs/src/component.rs324
-rw-r--r--ecs/src/component/local.rs104
-rw-r--r--ecs/src/component/storage.rs795
-rw-r--r--ecs/src/component/storage/archetype.rs385
-rw-r--r--ecs/src/component/storage/graph.rs432
-rw-r--r--ecs/src/entity.rs295
-rw-r--r--ecs/src/entity/obtainer.rs29
-rw-r--r--ecs/src/error.rs270
-rw-r--r--ecs/src/event.rs105
-rw-r--r--ecs/src/event/component.rs103
-rw-r--r--ecs/src/extension.rs72
-rw-r--r--ecs/src/lib.rs773
-rw-r--r--ecs/src/lock.rs259
-rw-r--r--ecs/src/pair.rs687
-rw-r--r--ecs/src/phase.rs22
-rw-r--r--ecs/src/query.rs569
-rw-r--r--ecs/src/query/flexible.rs92
-rw-r--r--ecs/src/query/term.rs116
-rw-r--r--ecs/src/sole.rs104
-rw-r--r--ecs/src/stats.rs8
-rw-r--r--ecs/src/system.rs158
-rw-r--r--ecs/src/system/initializable.rs131
-rw-r--r--ecs/src/system/observer.rs280
-rw-r--r--ecs/src/system/stateful.rs269
-rw-r--r--ecs/src/tuple.rs238
-rw-r--r--ecs/src/uid.rs261
-rw-r--r--ecs/src/util.rs415
-rw-r--r--ecs/src/util/array_vec.rs131
-rw-r--r--ecs/tests/phase.rs36
-rw-r--r--ecs/tests/query.rs413
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,
- &params,
- )?;
-
- 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,
- &params,
- )?;
-
- 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],
- );
-}