diff options
author | HampusM <hampus@hampusmat.com> | 2025-04-09 20:50:14 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-04-10 17:04:40 +0200 |
commit | 94e5e592baea2935af7c94ad44805a09d0e30740 (patch) | |
tree | 358f7496ac40e2a7d7cae10cf04bf25246debdec | |
parent | b982b205373f445db9ced7f3cf13c1156ad8a40a (diff) |
feat(ecs): replace Relationship component with pair UID support
-rw-r--r-- | ecs/examples/event_loop.rs | 17 | ||||
-rw-r--r-- | ecs/examples/relationship.rs | 16 | ||||
-rw-r--r-- | ecs/examples/with_sole.rs | 4 | ||||
-rw-r--r-- | ecs/src/component/storage.rs | 63 | ||||
-rw-r--r-- | ecs/src/component/storage/archetype.rs | 110 | ||||
-rw-r--r-- | ecs/src/component/storage/graph.rs | 19 | ||||
-rw-r--r-- | ecs/src/entity.rs | 94 | ||||
-rw-r--r-- | ecs/src/lib.rs | 80 | ||||
-rw-r--r-- | ecs/src/pair.rs | 201 | ||||
-rw-r--r-- | ecs/src/phase.rs | 6 | ||||
-rw-r--r-- | ecs/src/query.rs | 70 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 6 | ||||
-rw-r--r-- | ecs/src/query/term.rs | 31 | ||||
-rw-r--r-- | ecs/src/relationship.rs | 434 | ||||
-rw-r--r-- | ecs/src/uid.rs | 168 | ||||
-rw-r--r-- | ecs/tests/query.rs | 14 |
16 files changed, 766 insertions, 567 deletions
diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs index 2365eb0..61d7ba4 100644 --- a/ecs/examples/event_loop.rs +++ b/ecs/examples/event_loop.rs @@ -1,6 +1,6 @@ use ecs::actions::Actions; +use ecs::pair::{ChildOf, Pair}; use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::relationship::{ChildOf, Relationship}; use ecs::{static_entity, Component, Query, World}; #[derive(Component)] @@ -65,20 +65,11 @@ fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions) } } -static_entity!( - SHEER_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE)) -); +static_entity!(SHEER_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE))); -static_entity!( - FEED_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*SHEER_PHASE)) -); +static_entity!(FEED_PHASE, (Phase, Pair::new::<ChildOf>(*SHEER_PHASE))); -static_entity!( - AGE_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*FEED_PHASE)) -); +static_entity!(AGE_PHASE, (Phase, Pair::new::<ChildOf>(*FEED_PHASE))); fn main() { diff --git a/ecs/examples/relationship.rs b/ecs/examples/relationship.rs index 240884a..b607398 100644 --- a/ecs/examples/relationship.rs +++ b/ecs/examples/relationship.rs @@ -1,5 +1,6 @@ +use ecs::pair::Pair; use ecs::phase::START as START_PHASE; -use ecs::relationship::Relationship; +use ecs::uid::Wildcard; use ecs::{Component, Query, World}; #[derive(Component)] @@ -17,16 +18,19 @@ struct Health health: u32, } +#[derive(Component)] struct Holding; -fn print_player_stats( - player_query: Query<(&Player, &Health, &Relationship<Holding, Sword>)>, -) +fn print_player_stats(player_query: Query<(&Player, &Health, Pair<Holding, Wildcard>)>) { for (_, health, sword_relationship) in &player_query { println!("Player health: {}", health.health); - if let Some(sword) = sword_relationship.get(0) { + if let Some(sword_ent) = sword_relationship.get_target_entity() { + let sword = sword_ent + .get::<Sword>() + .expect("Sword entity is missing sword component"); + println!("Player sword attack strength: {}", sword.attack_strength); } } @@ -43,7 +47,7 @@ fn main() world.create_entity(( Player, Health { health: 180 }, - Relationship::<Holding, Sword>::new(sword_uid), + Pair::new::<Holding>(sword_uid), )); world.step(); diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs index 689e562..c3feaab 100644 --- a/ecs/examples/with_sole.rs +++ b/ecs/examples/with_sole.rs @@ -1,5 +1,5 @@ +use ecs::pair::{ChildOf, Pair}; 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}; @@ -33,7 +33,7 @@ fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>) static_entity!( PRINT_AMMO_COUNT_PHASE, - (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE)) + (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE)) ); fn main() diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 53f51f2..9cf4433 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -33,14 +33,43 @@ pub struct ArchetypeSearchTerms<'a> impl ArchetypeSearchTerms<'_> { - fn excluded_contains(&self, uid: Uid) -> bool + fn excluded_contains(&self, comp_id: Uid) -> bool { - self.excluded_components.binary_search(&uid).is_ok() + let comp_id_kind = comp_id.kind(); + + debug_assert!( + comp_id_kind == UidKind::Component + || (comp_id_kind == UidKind::Pair + && comp_id.target_component() != Uid::wildcard()) + ); + + let is_found = self.excluded_components.binary_search(&comp_id).is_ok(); + + if !is_found && comp_id_kind == UidKind::Pair { + return self.excluded_components.iter().any(|excluded_comp_id| { + excluded_comp_id.kind() == UidKind::Pair + && excluded_comp_id.has_same_relation_as(comp_id) + && excluded_comp_id.target_component() == Uid::wildcard() + }); + } + + is_found } - fn required_contains(&self, uid: Uid) -> bool + fn contains_conflicting(&self) -> bool { - self.required_components.binary_search(&uid).is_ok() + self.excluded_components.iter().any(|excluded_comp_id| { + self.required_components + .binary_search(excluded_comp_id) + .is_ok() + }) + } + + fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool + { + self.required_components + .iter() + .all(|comp_id| archetype.contains_matching_component(*comp_id)) } } @@ -61,11 +90,7 @@ impl Storage { 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)) - { + if search_terms.contains_conflicting() { return ArchetypeRefIter { storage: self, pre_iter: Either::B(Vec::new().into_iter()), @@ -81,7 +106,15 @@ impl Storage .borrow_mut() .push(ImaginaryArchetype { id: archetype_id, - component_ids: search_terms.required_components.to_vec(), + component_ids: search_terms + .required_components + .iter() + .copied() + .filter(|required_comp_id| { + required_comp_id.kind() != UidKind::Pair + || required_comp_id.target_component() != Uid::wildcard() + }) + .collect(), }); let found_archetypes = self.find_all_archetype_with_comps(&search_terms); @@ -175,7 +208,7 @@ impl Storage if archetype_node .archetype() - .has_component_with_id(component_id) + .contains_component_with_exact_id(component_id) { return Err(Error::ComponentAlreadyInEntity { entity: entity_uid, @@ -266,7 +299,7 @@ impl Storage if !archetype_node .archetype() - .has_component_with_id(component_id) + .contains_component_with_exact_id(component_id) { return Err(Error::ComponentNotFoundInEntity { entity: entity_uid, @@ -367,11 +400,7 @@ impl Storage continue; } - if !search_terms - .required_components - .iter() - .all(|comp_id| node.archetype().has_component_with_id(*comp_id)) - { + if !search_terms.archetype_contains_all_required(node.archetype()) { continue; } diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs index b8ac826..10a665e 100644 --- a/ecs/src/component/storage/archetype.rs +++ b/ecs/src/component/storage/archetype.rs @@ -1,5 +1,8 @@ use std::any::Any; +use std::array::IntoIter as ArrayIntoIter; use std::hash::{DefaultHasher, Hash, Hasher}; +use std::iter::{Enumerate, Filter, Map, RepeatN, Zip}; +use std::option::IntoIter as OptionIntoIter; use std::slice::Iter as SliceIter; use hashbrown::HashMap; @@ -7,7 +10,7 @@ use hashbrown::HashMap; use crate::component::Metadata as ComponentMetadata; use crate::lock::Lock; use crate::uid::{Kind as UidKind, Uid}; -use crate::util::HashMapExt; +use crate::util::{Either, HashMapExt}; #[derive(Debug)] pub struct Archetype @@ -117,8 +120,54 @@ impl Archetype self.component_index_lookup.len() } + pub fn get_matching_component_indices( + &self, + component_id: Uid, + ) -> MatchingComponentIter + { + assert!( + component_id.kind() == UidKind::Component + || component_id.kind() == UidKind::Pair + ); + + if component_id.kind() == UidKind::Pair + && component_id.target_component() == Uid::wildcard() + { + return MatchingComponentIter { + inner: Either::A( + self.component_ids + .iter() + .enumerate() + .zip(std::iter::repeat_n(component_id, self.component_ids.len())) + .filter( + (|((_, other_comp_id), component_id)| { + other_comp_id.kind() == UidKind::Pair + && other_comp_id.has_same_relation_as(*component_id) + }) + as MatchingComponentIterFilterFn, + ) + .map(|((index, other_comp_id), _)| (*other_comp_id, index)), + ), + }; + } + + MatchingComponentIter { + inner: Either::B( + [component_id] + .into_iter() + .zip(self.get_index_for_component(component_id).into_iter()), + ), + } + } + pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize> { + assert!( + component_id.kind() == UidKind::Component + || (component_id.kind() == UidKind::Pair + && component_id.target_component() != Uid::wildcard()) + ); + self.component_index_lookup.get(&component_id).copied() } @@ -132,14 +181,69 @@ impl Archetype self.component_ids.iter().copied() } - pub fn has_component_with_id(&self, component_id: Uid) -> bool + pub fn contains_matching_component(&self, component_id: Uid) -> bool + { + let component_id_kind = component_id.kind(); + + debug_assert!( + component_id_kind == UidKind::Component || component_id_kind == UidKind::Pair + ); + + if component_id.kind() == UidKind::Pair + && component_id.target_component() == Uid::wildcard() + { + return self.component_ids.iter().any(|other_comp_id| { + other_comp_id.kind() == UidKind::Pair + && other_comp_id.has_same_relation_as(component_id) + }); + } + + self.contains_component_with_exact_id(component_id) + } + + pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool { - debug_assert_eq!(component_id.kind(), UidKind::Component); + let component_id_kind = component_id.kind(); + + debug_assert!( + component_id_kind == UidKind::Component + || (component_id_kind == UidKind::Pair + && component_id.target_component() != Uid::wildcard()) + ); self.component_index_lookup.contains_key(&component_id) } } +type MatchingComponentIterFilterFn = fn(&((usize, &Uid), Uid)) -> bool; + +type MatchingComponentIterMapFn = fn(((usize, &Uid), Uid)) -> (Uid, usize); + +#[derive(Debug)] +pub struct MatchingComponentIter<'archetype> +{ + inner: Either< + Map< + Filter< + Zip<Enumerate<SliceIter<'archetype, Uid>>, RepeatN<Uid>>, + MatchingComponentIterFilterFn, + >, + MatchingComponentIterMapFn, + >, + Zip<ArrayIntoIter<Uid, 1>, OptionIntoIter<usize>>, + >, +} + +impl Iterator for MatchingComponentIter<'_> +{ + type Item = (Uid, usize); + + fn next(&mut self) -> Option<Self::Item> + { + self.inner.next() + } +} + #[derive(Debug)] pub struct EntityIter<'archetype> { diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs index d38223a..29fa937 100644 --- a/ecs/src/component/storage/graph.rs +++ b/ecs/src/component/storage/graph.rs @@ -147,7 +147,11 @@ impl Graph let uniq_comp_id = target_node .archetype() .component_ids_sorted() - .find(|id| !subset_node.archetype().has_component_with_id(*id)) + .find(|id| { + !subset_node + .archetype() + .contains_component_with_exact_id(*id) + }) .unwrap(); subset_node @@ -177,7 +181,7 @@ impl Graph .find(|other_archetype_comp_id| { !target_node .archetype() - .has_component_with_id(*other_archetype_comp_id) + .contains_component_with_exact_id(*other_archetype_comp_id) }) .or_else(|| { if target_node.archetype().component_cnt() != 0 { @@ -204,7 +208,11 @@ impl Graph let extra_comp_id = superset_node .archetype() .component_ids_unsorted() - .find(|comp_id| !target_node.archetype().has_component_with_id(*comp_id)) + .find(|comp_id| { + !target_node + .archetype() + .contains_component_with_exact_id(*comp_id) + }) .expect("Archetype should contain one extra component ID"); superset_node @@ -242,7 +250,10 @@ impl ArchetypeNode insert_fn: impl FnOnce() -> ArchetypeEdges, ) -> &mut ArchetypeEdges { - debug_assert_eq!(component_id.kind(), UidKind::Component); + debug_assert!(matches!( + component_id.kind(), + UidKind::Component | UidKind::Pair + )); self.edges.entry(component_id).or_insert_with(insert_fn) } diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index a43f9ce..3f5a3d3 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -1,12 +1,26 @@ +use std::any::type_name; + use linkme::distributed_slice; -use crate::component::storage::archetype::{Archetype, Entity as ArchetypeEntity}; -use crate::uid::Uid; +use crate::component::storage::archetype::{ + Archetype, + Entity as ArchetypeEntity, + MatchingComponentIter as ArchetypeMatchingComponentIter, +}; +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleFromEntityComponentRef, + HandleMut as ComponentHandleMut, +}; +use crate::uid::{Kind as UidKind, Uid}; use crate::{EntityComponentRef, World}; /// A handle to a entity. +#[derive(Debug)] pub struct Handle<'a> { + world: &'a World, archetype: &'a Archetype, entity: &'a ArchetypeEntity, } @@ -21,20 +35,82 @@ impl<'a> Handle<'a> self.entity.uid() } + pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'_, ComponentT>> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let component = self.get_matching_components(ComponentT::id()).next()?; + + Some( + ComponentHandle::from_entity_component_ref(Some(component), self.world) + .unwrap_or_else(|err| { + panic!( + "Taking component {} lock failed: {err}", + type_name::<ComponentT>() + ); + }), + ) + } + + pub fn get_mut<ComponentT: Component>( + &self, + ) -> Option<ComponentHandleMut<'_, ComponentT>> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let component = self.get_matching_components(ComponentT::id()).next()?; + + Some( + ComponentHandleMut::from_entity_component_ref(Some(component), self.world) + .unwrap_or_else(|err| { + panic!( + "Taking component {} lock failed: {err}", + type_name::<ComponentT>() + ); + }), + ) + } + #[inline] #[must_use] - pub fn get_component(&self, component_uid: Uid) -> Option<EntityComponentRef<'a>> + pub fn get_matching_components(&self, component_uid: Uid) + -> MatchingComponentIter<'a> { - let index = self.archetype.get_index_for_component(component_uid)?; + MatchingComponentIter { + inner: self.archetype.get_matching_component_indices(component_uid), + entity: self.entity, + } + } - Some(EntityComponentRef::new( - self.entity.components().get(index).unwrap(), - )) + pub(crate) fn new( + world: &'a World, + archetype: &'a Archetype, + entity: &'a ArchetypeEntity, + ) -> Self + { + Self { world, archetype, entity } } +} + +#[derive(Debug)] +pub struct MatchingComponentIter<'a> +{ + inner: ArchetypeMatchingComponentIter<'a>, + entity: &'a ArchetypeEntity, +} + +impl<'a> Iterator for MatchingComponentIter<'a> +{ + type Item = EntityComponentRef<'a>; - pub(crate) fn new(archetype: &'a Archetype, entity: &'a ArchetypeEntity) -> Self + fn next(&mut self) -> Option<Self::Item> { - Self { archetype, entity } + let (matching_component_id, index) = self.inner.next()?; + + Some(EntityComponentRef::new( + matching_component_id, + self.entity.components().get(index).unwrap(), + )) } } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 962c542..3b89dac 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -20,6 +20,7 @@ use crate::component::{ use crate::entity::CREATE_STATIC_ENTITIES; use crate::extension::{Collector as ExtensionCollector, Extension}; use crate::lock::{Lock, WriteGuard}; +use crate::pair::{ChildOf, DependsOn, Pair}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; use crate::query::term::Without; @@ -30,21 +31,19 @@ use crate::query::{ 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::uid::{Kind as UidKind, Uid}; +use crate::uid::{Kind as UidKind, Uid, Wildcard}; pub mod actions; pub mod component; pub mod entity; pub mod event; pub mod extension; -mod lock; +pub mod pair; pub mod phase; pub mod query; -pub mod relationship; pub mod sole; pub mod stats; pub mod system; @@ -55,6 +54,8 @@ pub mod util; #[doc(hidden)] pub mod private; +mod lock; + pub use ecs_macros::{Component, Sole}; pub use crate::query::Query; @@ -151,7 +152,7 @@ impl World { self.create_entity(( SystemComponent { system: system.into_type_erased() }, - Relationship::<DependsOn, Phase>::new(phase_euid), + Pair::new::<DependsOn>(phase_euid), )); } @@ -278,7 +279,7 @@ impl World archetype .component_ids_sorted() .into_iter() - .map(|comp_id| comp_id.id().to_string()) + .map(|comp_id| comp_id.to_string()) .collect::<Vec<_>>() .join(", ") )) @@ -305,16 +306,18 @@ impl World 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) - }); + let system_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + SystemComponent::id(), + Pair::new::<DependsOn>(phase_euid).id(), + ]) + .build(), + ); - for (system_component, _) in system_iter { + for (system_component,) in + QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter()) + { // SAFETY: The world lives long enough unsafe { system_component.system.run(self); @@ -324,33 +327,32 @@ impl World 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; - } + let phase_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + Phase::id(), + Pair::new::<ChildOf>(parent_phase_euid).id(), + ]) + .build(), + ); - self.query_and_run_systems(child_phase_euid); - self.perform_child_phases(child_phase_euid); + for child_phase_entity in phase_query.iter() { + self.query_and_run_systems(child_phase_entity.uid()); + self.perform_child_phases(child_phase_entity.uid()); } } fn perform_phases(&self) { - let phase_query = - self.query::<(&Phase,), (Without<Relationship<ChildOf, Phase>>,)>(); + let phase_query = self.query::<(&Phase,), (Without<Pair<ChildOf, Wildcard>>,)>(); - for (phase_euid, (_,)) in phase_query.iter_with_euids() { - if phase_euid == *START_PHASE { + for (phase_entity_id, _) in phase_query.iter_with_euids() { + if phase_entity_id == *START_PHASE { continue; } - self.query_and_run_systems(phase_euid); - self.perform_child_phases(phase_euid); + self.query_and_run_systems(phase_entity_id); + self.perform_child_phases(phase_entity_id); } } @@ -604,19 +606,25 @@ impl Default for WorldData #[derive(Debug)] pub struct EntityComponentRef<'a> { - comp: &'a ArchetypeEntityComponent, + component_id: Uid, + component: &'a ArchetypeEntityComponent, } impl<'a> EntityComponentRef<'a> { fn component(&self) -> &'a Lock<Box<dyn Any>> { - self.comp.component() + self.component.component() + } + + pub fn id(&self) -> Uid + { + self.component_id } - fn new(comp: &'a ArchetypeEntityComponent) -> Self + fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self { - Self { comp } + Self { component_id, component: comp } } } diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs new file mode 100644 index 0000000..27e535e --- /dev/null +++ b/ecs/src/pair.rs @@ -0,0 +1,201 @@ +use std::convert::Infallible; + +use crate::component::storage::Storage as ComponentStorage; +use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts}; +use crate::entity::{ + Handle as EntityHandle, + MatchingComponentIter as EntityMatchingComponentIter, +}; +use crate::lock::ReadGuard; +use crate::query::{ + TermWithField as QueryTermWithField, + TermsBuilder as QueryTermsBuilder, + TermsBuilderInterface, +}; +use crate::uid::{PairParams as UidPairParams, Uid, Wildcard, With as WithUid}; +use crate::{Component, World}; + +#[derive(Debug)] +pub struct Pair<RelationElem: Element, TargetElem: Element> +{ + relation: RelationElem, + target: TargetElem, +} + +impl Pair<Uid, Uid> +{ + pub fn new<Relation: WithUid>(target: Uid) -> Self + { + Self { relation: Relation::uid(), target } + } + + pub fn id(&self) -> Uid + { + Uid::new_pair(UidPairParams { + relation: self.relation, + target: self.target, + }) + } +} + +impl IntoComponentParts for Pair<Uid, Uid> +{ + fn into_parts(self) -> ComponentParts + { + ComponentParts::builder().name("Pair").build(self.id(), ()) + } +} + +impl<Relation, Target> QueryTermWithField for Pair<Relation, Target> +where + Relation: WithUid, + Target: WithUid, +{ + type Field<'a> = Handle<'a>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Self::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world> + { + let first_matching_comp = entity_handle + .get_matching_components(Self::uid()) + .next() + .expect("Not possible"); + + Handle { + world, + component_storage_lock: world.data.component_storage.read_nonblock().unwrap(), + pair_uid: first_matching_comp.id(), + } + } +} + +impl<Relation, Target> WithUid for Pair<Relation, Target> +where + Relation: WithUid, + Target: WithUid, +{ + fn uid() -> Uid + { + Uid::new_pair(UidPairParams { + relation: Relation::uid(), + target: Target::uid(), + }) + } +} + +impl<Relation> QueryTermWithField for &'static [Pair<Relation, Wildcard>] +where + Relation: WithUid, +{ + type Field<'a> = HandleIter<'a>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Pair::<Relation, Wildcard>::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world> + { + HandleIter { + inner: entity_handle + .get_matching_components(Pair::<Relation, Wildcard>::uid()), + world, + } + } +} + +pub struct Handle<'world> +{ + world: &'world World, + component_storage_lock: ReadGuard<'world, ComponentStorage>, + pair_uid: Uid, +} + +impl Handle<'_> +{ + pub fn get_target_entity(&self) -> Option<EntityHandle<'_>> + { + let archetype = self + .component_storage_lock + .get_entity_archetype(self.pair_uid.target_entity())?; + + let archetype_entity = archetype + .get_entity_by_id(self.pair_uid.target_entity()) + .expect("Not possible"); + + Some(EntityHandle::new(self.world, archetype, archetype_entity)) + } +} + +pub struct HandleIter<'a> +{ + inner: EntityMatchingComponentIter<'a>, + world: &'a World, +} + +impl<'a> Iterator for HandleIter<'a> +{ + type Item = Handle<'a>; + + fn next(&mut self) -> Option<Self::Item> + { + let matching_comp = self.inner.next()?; + + Some(Handle { + world: self.world, + component_storage_lock: self + .world + .data + .component_storage + .read_nonblock() + .unwrap(), + pair_uid: matching_comp.id(), + }) + } +} + +pub trait Element: sealed::Sealed +{ + type Value; +} + +impl Element for Uid +{ + type Value = Uid; +} + +impl sealed::Sealed for Uid {} + +impl<WithUidT: WithUid> Element for WithUidT +{ + type Value = Infallible; +} + +impl<WithUidT: WithUid> sealed::Sealed for WithUidT {} + +/// Relation denoting a dependency to another entity +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct DependsOn; + +/// Relation denoting being the child of another entity. +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct ChildOf; + +mod sealed +{ + pub trait Sealed {} +} diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs index b8660f2..48dd38d 100644 --- a/ecs/src/phase.rs +++ b/ecs/src/phase.rs @@ -1,6 +1,6 @@ use ecs_macros::Component; -use crate::relationship::{ChildOf, Relationship}; +use crate::pair::{ChildOf, Pair}; use crate::static_entity; #[derive(Debug, Default, Clone, Copy, Component)] @@ -10,6 +10,6 @@ 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 UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE))); -static_entity!(pub PRESENT, (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE))); +static_entity!(pub PRESENT, (Phase, Pair::new::<ChildOf>(*UPDATE))); diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 7542f0d..1d27b7a 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -7,7 +7,7 @@ use crate::component::{Component, HandleFromEntityComponentRef, Ref as Component 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::uid::{Kind as UidKind, Uid, With as WithUid}; use crate::util::array_vec::ArrayVec; use crate::util::Array; use crate::World; @@ -111,7 +111,7 @@ where impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator for &'query Query<'world, FieldTerms, FieldlessTerms> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, FieldlessTerms: TermWithoutFieldTuple, { type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; @@ -171,11 +171,13 @@ pub struct TermsBuilder<const MAX_TERM_CNT: usize> pub trait TermsBuilderInterface { - fn with<ComponentT: Component>(self) -> Self; + fn with<WithUidT: WithUid>(self) -> Self; - fn without<ComponentT: Component>(self) -> Self; + fn without<WithUidT: WithUid>(self) -> Self; fn with_required(self, ids: impl Array<Uid>) -> Self; + + fn without_ids(self, ids: impl Array<Uid>) -> Self; } macro_rules! impl_terms_builder { @@ -196,25 +198,25 @@ macro_rules! impl_terms_builder { impl_terms_builder! { #[allow(unused_mut)] - fn with<ComponentT: Component>(mut self) -> Self + fn with<WithUidT: WithUid>(mut self) -> Self { let insert_index = self.required_components - .partition_point(|id| *id <= ComponentT::id()); + .partition_point(|id| *id <= WithUidT::uid()); self.required_components - .insert(insert_index, ComponentT::id()); + .insert(insert_index, WithUidT::uid()); self } #[allow(unused_mut)] - fn without<ComponentT: Component>(mut self) -> Self + fn without<WithUidT: WithUid>(mut self) -> Self { let insert_index = self.excluded_components - .partition_point(|id| *id <= ComponentT::id()); + .partition_point(|id| *id <= WithUidT::uid()); self.excluded_components - .insert(insert_index, ComponentT::id()); + .insert(insert_index, WithUidT::uid()); self } @@ -250,6 +252,38 @@ impl_terms_builder! { self } + + #[allow(unused_mut)] + fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self + { + if !ids.as_ref().is_sorted() { + ids.as_mut().sort(); + } + + if self.excluded_components.len() == 0 { + self.excluded_components.extend(ids); + return self; + } + + let mut id_iter = ids.into_iter(); + + while let Some(id) = id_iter.next() { + let insert_index = self.excluded_components + .partition_point(|other_id| *other_id <= id); + + if insert_index == self.excluded_components.len() { + self.excluded_components.extend([id].into_iter().chain(id_iter)); + + return self; + } + + self.excluded_components + .insert(insert_index, id); + + } + + self + } } impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT> @@ -303,8 +337,12 @@ impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT world: &'world World, ) -> Self::Field<'world> { + assert_eq!(ComponentRefT::Component::id().kind(), UidKind::Component); + Self::Field::from_entity_component_ref( - entity_handle.get_component(ComponentRefT::Component::id()), + entity_handle + .get_matching_components(ComponentRefT::Component::id()) + .next(), world, ) .unwrap_or_else(|err| { @@ -339,7 +377,7 @@ pub trait TermWithFieldTuple pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, @@ -350,7 +388,7 @@ where impl<'query, 'world, FieldTerms, EntityHandleIter> Iter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { @@ -369,7 +407,7 @@ where impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator for Iter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { @@ -385,7 +423,7 @@ where pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, @@ -396,7 +434,7 @@ where impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index 2f0b5e7..3e72b34 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -16,6 +16,7 @@ use crate::World; #[derive(Debug)] pub struct Query<'world, const MAX_TERM_CNT: usize> { + world: &'world World, component_storage: ReadGuard<'world, ComponentStorage>, terms: Terms<MAX_TERM_CNT>, } @@ -27,6 +28,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> pub fn iter(&self) -> Iter<'_> { Iter { + world: self.world, iter: self .component_storage .search_archetypes(ArchetypeSearchTerms { @@ -45,6 +47,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self { Self { + world, component_storage: world .data .component_storage @@ -57,6 +60,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> pub struct Iter<'query> { + world: &'query World, iter: QueryEntityIter<'query>, } @@ -68,7 +72,7 @@ impl<'query> Iterator for Iter<'query> { let (archetype, entity) = self.iter.next()?; - Some(EntityHandle::new(archetype, entity)) + Some(EntityHandle::new(self.world, archetype, entity)) } } diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs index 78668c5..597dd1a 100644 --- a/ecs/src/query/term.rs +++ b/ecs/src/query/term.rs @@ -8,42 +8,43 @@ use crate::query::{ TermsBuilder, TermsBuilderInterface, }; +use crate::uid::With as WithUid; -pub struct With<ComponentT> +pub struct With<WithUidT> where - ComponentT: Component, + WithUidT: WithUid, { - _pd: PhantomData<ComponentT>, + _pd: PhantomData<WithUidT>, } -impl<ComponentT> TermWithoutField for With<ComponentT> +impl<WithUidT> TermWithoutField for With<WithUidT> where - ComponentT: Component, + WithUidT: WithUid, { fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, ) { - terms_builder.with::<ComponentT>(); + terms_builder.with::<WithUidT>(); } } -pub struct Without<ComponentT> +pub struct Without<WithUidT> where - ComponentT: Component, + WithUidT: WithUid, { - _pd: PhantomData<ComponentT>, + _pd: PhantomData<WithUidT>, } -impl<ComponentT> TermWithoutField for Without<ComponentT> +impl<WithUidT> TermWithoutField for Without<WithUidT> where - ComponentT: Component, + WithUidT: WithUid, { fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, ) { - terms_builder.without::<ComponentT>(); + terms_builder.without::<WithUidT>(); } } @@ -66,7 +67,11 @@ where { Some( ComponentRefT::Handle::<'world>::from_entity_component_ref( - Some(entity_handle.get_component(ComponentRefT::Component::id())?), + Some( + entity_handle + .get_matching_components(ComponentRefT::Component::id()) + .next()?, + ), world, ) .unwrap_or_else(|err| { diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs deleted file mode 100644 index e368dd4..0000000 --- a/ecs/src/relationship.rs +++ /dev/null @@ -1,434 +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, - Handle as ComponentHandle, - HandleError as ComponentHandleError, - HandleFromEntityComponentRef, - HandleMut as ComponentHandleMut, -}; -use crate::lock::ReadGuard; -use crate::uid::{Kind as UidKind, Uid}; -use crate::{EntityComponentRef, 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> HandleFromEntityComponentRef<'rel_comp> - for RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - type Error = ComponentHandleError; - - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'rel_comp>>, - world: &'rel_comp World, - ) -> Result<Self, Self::Error> - { - let relationship_comp_handle_from_ent_comp_ref = ComponentHandleMut::< - Relationship<Kind, ComponentT>, - >::from_entity_component_ref; - - let relationship_comp = - relationship_comp_handle_from_ent_comp_ref(entity_component_ref, 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> 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> HandleFromEntityComponentRef<'rel_comp> - for Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - type Error = ComponentHandleError; - - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'rel_comp>>, - world: &'rel_comp World, - ) -> Result<Self, Self::Error> - { - let relationship_comp_handle_from_ent_comp_ref = - ComponentHandle::<Relationship<Kind, ComponentT>>::from_entity_component_ref; - - let relationship_comp = - relationship_comp_handle_from_ent_comp_ref(entity_component_ref, 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> 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/uid.rs b/ecs/src/uid.rs index c3ed85b..6c97649 100644 --- a/ecs/src/uid.rs +++ b/ecs/src/uid.rs @@ -1,23 +1,28 @@ -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::mem::transmute; use std::sync::atomic::{AtomicU32, Ordering}; +use crate::component::Component; use crate::util::{gen_mask_64, BitMask, NumberExt}; -static NEXT: AtomicU32 = AtomicU32::new(1); +static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID); + +static WILDCARD_ID: u32 = 1; const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63)); +const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31)); const KIND_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(0..=1)); #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] pub enum Kind { + Pair = 3, Entity = 2, Component = 1, } -/// Unique entity/component ID. +/// A unique identifier. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Uid { @@ -26,6 +31,10 @@ pub struct Uid impl Uid { + /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are + /// reserved. + pub const FIRST_UNIQUE_ID: u32 = 5; + /// Returns a new unique entity/component ID. pub fn new_unique(kind: Kind) -> Self { @@ -37,6 +46,37 @@ impl Uid } #[must_use] + pub fn wildcard() -> Self + { + Self { + inner: ID_BITS.field_prep(u64::from(WILDCARD_ID)) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + #[must_use] + pub fn new_pair(params: PairParams) -> Self + { + assert_ne!( + params.relation.kind(), + Kind::Pair, + "Pair relation cannot be a pair" + ); + + assert_ne!( + params.target.kind(), + Kind::Pair, + "Pair target cannot be a pair" + ); + + Self { + inner: ID_BITS.field_prep(u64::from(params.target.id())) + | RELATION_BITS.field_prep(u64::from(params.relation.id())) + | KIND_BITS.field_prep(Kind::Pair as u64), + } + } + + #[must_use] pub fn id(&self) -> u32 { let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else { @@ -57,6 +97,76 @@ impl Uid // in the new_unique function unsafe { transmute::<u8, Kind>(kind) } } + + /// If this `Uid` is a pair, returns the relation as a component `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + pub fn relation_component(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.relation())) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + pub fn has_same_relation_as(&self, other: Self) -> bool + { + self.relation() == other.relation() + } + + /// If this `Uid` is a pair, returns the relation as a entity `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + pub fn relation_entity(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.relation())) + | KIND_BITS.field_prep(Kind::Entity as u64), + } + } + + /// If this `Uid` is a pair, returns the target as a component `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + pub fn target_component(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.id())) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + /// If this `Uid` is a pair, returns the target as a entity `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + pub fn target_entity(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.id())) + | KIND_BITS.field_prep(Kind::Entity as u64), + } + } + + fn relation(&self) -> u32 + { + let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else { + unreachable!("Uid relation does not fit in u32"); + }; + + relation + } } impl Debug for Uid @@ -70,3 +180,55 @@ impl Debug for Uid .finish_non_exhaustive() } } + +impl Display for Uid +{ + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result + { + if self.kind() == Kind::Pair { + return write!( + formatter, + "({}, {})", + self.relation(), + self.target_component() + ); + } + + if *self == Uid::wildcard() { + return write!(formatter, "*"); + } + + write!(formatter, "{}", self.id()) + } +} + +#[derive(Debug, Clone)] +pub struct PairParams +{ + pub relation: Uid, + pub target: Uid, +} + +pub trait With: 'static +{ + fn uid() -> Uid; +} + +impl<ComponentT: Component> With for ComponentT +{ + fn uid() -> Uid + { + Self::id() + } +} + +#[derive(Debug)] +pub enum Wildcard {} + +impl With for Wildcard +{ + fn uid() -> Uid + { + Uid::wildcard() + } +} diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs index 6e747e3..062fd5a 100644 --- a/ecs/tests/query.rs +++ b/ecs/tests/query.rs @@ -36,13 +36,13 @@ 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); + assert_eq!(A::id().id(), Uid::FIRST_UNIQUE_ID); + assert_eq!(B::id().id(), Uid::FIRST_UNIQUE_ID + 1); + assert_eq!(C::id().id(), Uid::FIRST_UNIQUE_ID + 2); + assert_eq!(D::id().id(), Uid::FIRST_UNIQUE_ID + 3); + assert_eq!(E::id().id(), Uid::FIRST_UNIQUE_ID + 4); + assert_eq!(F::id().id(), Uid::FIRST_UNIQUE_ID + 5); + assert_eq!(G::id().id(), Uid::FIRST_UNIQUE_ID + 6); }); } |