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