summaryrefslogtreecommitdiff
path: root/ecs/src
diff options
context:
space:
mode:
Diffstat (limited to 'ecs/src')
-rw-r--r--ecs/src/actions.rs54
-rw-r--r--ecs/src/component.rs496
-rw-r--r--ecs/src/component/storage.rs181
-rw-r--r--ecs/src/component/storage/archetype.rs177
-rw-r--r--ecs/src/component/storage/graph.rs36
-rw-r--r--ecs/src/entity.rs109
-rw-r--r--ecs/src/event/component.rs88
-rw-r--r--ecs/src/lib.rs494
-rw-r--r--ecs/src/lock.rs95
-rw-r--r--ecs/src/pair.rs198
-rw-r--r--ecs/src/phase.rs6
-rw-r--r--ecs/src/query.rs273
-rw-r--r--ecs/src/query/flexible.rs39
-rw-r--r--ecs/src/query/term.rs109
-rw-r--r--ecs/src/relationship.rs466
-rw-r--r--ecs/src/sole.rs11
-rw-r--r--ecs/src/system/stateful.rs9
-rw-r--r--ecs/src/type_name.rs15
-rw-r--r--ecs/src/uid.rs177
-rw-r--r--ecs/src/util.rs2
-rw-r--r--ecs/src/util/array_vec.rs131
21 files changed, 1677 insertions, 1489 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs
index 7dff3a5..3dd8755 100644
--- a/ecs/src/actions.rs
+++ b/ecs/src/actions.rs
@@ -1,11 +1,7 @@
use std::marker::PhantomData;
-use std::sync::{Arc, Weak};
+use std::rc::{Rc, Weak};
-use crate::component::{
- Component,
- Metadata as ComponentMetadata,
- Sequence as ComponentSequence,
-};
+use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence};
use crate::system::{Param as SystemParam, System};
use crate::uid::{Kind as UidKind, Uid};
use crate::{ActionQueue, World};
@@ -23,13 +19,11 @@ 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() },
- ));
+ self.action_queue
+ .push(Action::Spawn(components.into_parts_array().into()));
}
- /// Queues up despawning a entity at the end of the current tick.
+ /// Queues up despawning a entity at the end of the **next** tick.
pub fn despawn(&mut self, entity_uid: Uid)
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
@@ -50,26 +44,28 @@ impl<'world> Actions<'world>
self.action_queue.push(Action::AddComponents(
entity_uid,
- components.into_array().into(),
- EventIds { ids: Comps::added_event_ids() },
+ components.into_parts_array().into(),
));
}
- /// 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,
+ /// Queues up removing component(s) from a entity at the end of the **next** tick.
+ pub fn remove_components(
+ &mut self,
+ entity_uid: Uid,
+ component_ids: impl IntoIterator<Item = Uid>,
+ )
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
- if Comps::COUNT == 0 {
+ let mut component_ids = component_ids.into_iter().peekable();
+
+ if component_ids.peek().is_none() {
return;
}
self.action_queue.push(Action::RemoveComponents(
entity_uid,
- Comps::metadata().into_iter().collect(),
- EventIds { ids: Comps::removed_event_ids() },
+ component_ids.collect(),
));
}
@@ -91,11 +87,11 @@ impl<'world> Actions<'world>
}
}
- fn new(action_queue: &'world Arc<ActionQueue>) -> Self
+ fn new(action_queue: &'world Rc<ActionQueue>) -> Self
{
Self {
action_queue,
- action_queue_weak: Arc::downgrade(action_queue),
+ action_queue_weak: Rc::downgrade(action_queue),
}
}
}
@@ -146,7 +142,7 @@ impl WeakRef
#[derive(Debug, Clone)]
pub struct Ref<'weak_ref>
{
- action_queue: Arc<ActionQueue>,
+ action_queue: Rc<ActionQueue>,
_pd: PhantomData<&'weak_ref ()>,
}
@@ -159,19 +155,13 @@ impl Ref<'_>
}
}
-#[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),
+ Spawn(Vec<ComponentParts>),
Despawn(Uid),
- AddComponents(Uid, Vec<(Uid, Box<dyn Component>)>, EventIds),
- RemoveComponents(Uid, Vec<ComponentMetadata>, EventIds),
+ AddComponents(Uid, Vec<ComponentParts>),
+ RemoveComponents(Uid, Vec<Uid>),
Stop,
}
diff --git a/ecs/src/component.rs b/ecs/src/component.rs
index 525bd98..5a8cd0b 100644
--- a/ecs/src/component.rs
+++ b/ecs/src/component.rs
@@ -2,68 +2,35 @@ use std::any::{type_name, Any};
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
+use ecs_macros::Component;
+use hashbrown::HashSet;
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;
+use crate::EntityComponentRef;
pub mod local;
pub(crate) mod storage;
-pub trait Component: SystemInput + Any + TypeName
+pub trait Component: SystemInput + Any
{
- /// 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
- }
+ /// Returns the name of this component.
+ fn name(&self) -> &'static str;
}
impl dyn Component
@@ -92,199 +59,61 @@ impl Debug for dyn Component
}
}
-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>;
-}
+ type PartsArray: Array<Parts>;
-/// [`Component`] metadata.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub struct Metadata
-{
- pub id: Uid,
- pub is_optional: bool,
+ fn into_parts_array(self) -> Self::PartsArray;
}
-impl Metadata
+#[derive(Debug)]
+pub struct Handle<'a, ComponentData: 'static>
{
- #[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(),
- }
- }
+ inner: MappedReadGuard<'a, ComponentData>,
}
-pub trait FromLockedOptional<'comp>: Sized
+impl<'comp, ComponentData: 'static> Handle<'comp, ComponentData>
{
- /// Converts a reference to a optional locked boxed component to a instance of `Self`.
+ /// Creates a new handle instance from a [`EntityComponentRef`].
///
/// # 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>,
-}
+ /// Will return `Err` if acquiring the component's lock fails.
+ pub fn from_entity_component_ref(
+ entity_component_ref: EntityComponentRef<'comp>,
+ ) -> Result<Self, HandleError>
+ {
+ Ok(Self::new(
+ entity_component_ref
+ .component()
+ .read_nonblock()
+ .map_err(AcquireLockError)?,
+ ))
+ }
-impl<'a, ComponentT: Component> Handle<'a, ComponentT>
-{
- pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Component>>) -> Self
+ pub(crate) fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> 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>()
- );
- })
+ component
+ .downcast_ref::<ComponentData>()
+ .unwrap_or_else(|| {
+ panic!(
+ "Failed to downcast component to type {}",
+ type_name::<ComponentData>()
+ );
+ })
}),
}
}
}
-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>
+impl<ComponentData: 'static> Deref for Handle<'_, ComponentData>
{
- type Target = ComponentT;
+ type Target = ComponentData;
fn deref(&self) -> &Self::Target
{
@@ -293,67 +122,49 @@ impl<ComponentT: Component> Deref for Handle<'_, ComponentT>
}
#[derive(Debug)]
-pub struct HandleMut<'a, ComponentT: Component>
+pub struct HandleMut<'a, ComponentData: 'static>
{
- inner: MappedWriteGuard<'a, ComponentT>,
+ inner: MappedWriteGuard<'a, ComponentData>,
}
-impl<'a, ComponentT: Component> HandleMut<'a, ComponentT>
+impl<'comp, ComponentData: 'static> HandleMut<'comp, ComponentData>
{
- pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Component>>) -> Self
+ /// Creates a new handle instance from a [`EntityComponentRef`].
+ ///
+ /// # Errors
+ /// Will return `Err` if acquiring the component's lock fails.
+ pub fn from_entity_component_ref(
+ entity_component_ref: EntityComponentRef<'comp>,
+ ) -> Result<Self, HandleError>
+ {
+ Ok(Self::new(
+ entity_component_ref
+ .component()
+ .write_nonblock()
+ .map_err(AcquireLockError)?,
+ ))
+ }
+
+ pub(crate) fn new(inner: WriteGuard<'comp, Box<dyn Any>>) -> 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>()
- );
- })
+ component
+ .downcast_mut::<ComponentData>()
+ .unwrap_or_else(|| {
+ panic!(
+ "Failed to downcast component to type {}",
+ type_name::<ComponentData>()
+ );
+ })
}),
}
}
}
-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>
+impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData>
{
- type Target = ComponentT;
+ type Target = ComponentData;
fn deref(&self) -> &Self::Target
{
@@ -361,7 +172,7 @@ impl<ComponentT: Component> Deref for HandleMut<'_, ComponentT>
}
}
-impl<ComponentT: Component> DerefMut for HandleMut<'_, ComponentT>
+impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData>
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -369,53 +180,32 @@ impl<ComponentT: Component> DerefMut for HandleMut<'_, ComponentT>
}
}
+#[derive(Debug, thiserror::Error)]
+pub enum HandleError
+{
+ #[error(transparent)]
+ AcquireLockFailed(#[from] AcquireLockError),
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to acquire component lock")]
+pub struct AcquireLockError(#[source] LockError);
+
macro_rules! inner {
($c: tt) => {
seq!(I in 0..=$c {
- impl<#(IntoComp~I,)*> Sequence for (#(IntoComp~I,)*)
- where
- #(
- for<'comp> IntoComp~I: Into<Component: Component>,
- )*
+ impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*)
{
const COUNT: usize = $c + 1;
- type Array = [(Uid, Box<dyn Component>); $c + 1];
+ type PartsArray = [Parts; $c + 1];
- fn into_array(self) -> Self::Array
+ fn into_parts_array(self) -> Self::PartsArray
{
[#({
- let (id, component) = self.I.into_component();
-
- (id, Box::new(component))
+ self.I.into_parts()
},)*]
}
-
- 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(),)*
- ]
- }
}
});
};
@@ -427,46 +217,134 @@ seq!(C in 0..=16 {
impl Sequence for ()
{
- type Array = [(Uid, Box<dyn Component>); 0];
+ type PartsArray = [Parts; 0];
const COUNT: usize = 0;
- fn into_array(self) -> Self::Array
+ fn into_parts_array(self) -> Self::PartsArray
{
[]
}
+}
+
+pub trait IntoParts
+{
+ fn into_parts(self) -> Parts;
+}
- fn metadata() -> impl Array<Metadata>
+impl<ComponentT> IntoParts for ComponentT
+where
+ ComponentT: Component,
+{
+ fn into_parts(self) -> Parts
{
- []
+ Parts::builder()
+ .name(type_name::<Self>())
+ .build(Self::id(), self)
+ }
+}
+
+/// The parts of a component.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Parts
+{
+ id: Uid,
+ name: &'static str,
+ data: Box<dyn Any>,
+}
+
+impl Parts
+{
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.id
+ }
+
+ #[must_use]
+ pub fn name(&self) -> &'static str
+ {
+ self.name
+ }
+
+ #[must_use]
+ pub fn builder() -> PartsBuilder
+ {
+ PartsBuilder::default()
}
- fn added_event_ids() -> Vec<Uid>
+ pub(crate) fn into_data(self) -> Box<dyn Any>
+ {
+ self.data
+ }
+}
+
+#[derive(Debug)]
+pub struct PartsBuilder
+{
+ name: &'static str,
+}
+
+impl PartsBuilder
+{
+ #[must_use]
+ pub fn name(mut self, name: &'static str) -> Self
{
- Vec::new()
+ self.name = name;
+ self
}
- fn removed_event_ids() -> Vec<Uid>
+ #[must_use]
+ pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts
{
- Vec::new()
+ Parts {
+ id,
+ name: self.name,
+ data: Box::new(data),
+ }
}
}
-pub trait Into
+impl Default for PartsBuilder
{
- type Component;
+ fn default() -> Self
+ {
+ Self { name: "(unspecified)" }
+ }
+}
- fn into_component(self) -> (Uid, Self::Component);
+/// Pending component removals for a entity.
+#[derive(Debug, Clone, Component)]
+pub struct Removals
+{
+ component_ids: HashSet<Uid>,
}
-impl<ComponentT> Into for ComponentT
-where
- ComponentT: Component,
+impl Removals
{
- type Component = Self;
+ pub fn contains<ComponentT: Component>(&self) -> bool
+ {
+ self.contains_id(ComponentT::id())
+ }
+
+ pub fn contains_id(&self, component_id: Uid) -> bool
+ {
+ self.component_ids.contains(&component_id)
+ }
+
+ pub(crate) fn add_ids(&mut self, ids: impl IntoIterator<Item = Uid>)
+ {
+ self.component_ids.extend(ids)
+ }
+}
- fn into_component(self) -> (Uid, Self::Component)
+impl FromIterator<Uid> for Removals
+{
+ fn from_iter<T: IntoIterator<Item = Uid>>(iter: T) -> Self
{
- (Self::id(), self)
+ Self {
+ component_ids: iter.into_iter().collect(),
+ }
}
}
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs
index 40909fb..b27b552 100644
--- a/ecs/src/component/storage.rs
+++ b/ecs/src/component/storage.rs
@@ -1,4 +1,4 @@
-use std::any::type_name;
+use std::any::Any;
use std::array::IntoIter as ArrayIter;
use std::cell::RefCell;
use std::vec::IntoIter as VecIntoIter;
@@ -17,8 +17,6 @@ use crate::component::storage::graph::{
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};
@@ -35,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 contains_conflicting(&self) -> bool
+ {
+ self.excluded_components.iter().any(|excluded_comp_id| {
+ self.required_components
+ .binary_search(excluded_comp_id)
+ .is_ok()
+ })
}
- fn required_contains(&self, uid: Uid) -> bool
+ fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool
{
- self.required_components.binary_search(&uid).is_ok()
+ self.required_components
+ .iter()
+ .all(|comp_id| archetype.contains_matching_component(*comp_id))
}
}
@@ -61,13 +88,9 @@ impl Storage
search_terms: ArchetypeSearchTerms<'search_terms>,
) -> ArchetypeRefIter<'_, 'search_terms>
{
- let archetype_id = ArchetypeId::new(&search_terms.required_components);
+ 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()),
@@ -82,8 +105,21 @@ impl Storage
self.imaginary_archetypes
.borrow_mut()
.push(ImaginaryArchetype {
- id: archetype_id,
- component_ids: search_terms.required_components.to_vec(),
+ id: ArchetypeId::new(search_terms.required_components.iter().filter(
+ |required_comp_id| {
+ required_comp_id.kind() != UidKind::Pair
+ || required_comp_id.target_component() != Uid::wildcard()
+ },
+ )),
+ component_ids: search_terms
+ .required_components
+ .iter()
+ .copied()
+ .filter(|required_comp_id| {
+ required_comp_id.kind() != UidKind::Pair
+ || required_comp_id.target_component() != Uid::wildcard()
+ })
+ .collect(),
});
let found_archetypes = self.find_all_archetype_with_comps(&search_terms);
@@ -117,7 +153,7 @@ impl Storage
return Err(Error::EntityAlreadyExists(uid));
}
- let empty_archetype_id = ArchetypeId::from_components_metadata(&[]);
+ let empty_archetype_id = ArchetypeId::new_empty();
let archetype_node = self.graph.get_or_create_node(empty_archetype_id, &[]);
@@ -161,16 +197,9 @@ impl Storage
pub fn add_entity_component(
&mut self,
entity_uid: Uid,
- component: (Uid, Box<dyn Component>),
+ (component_id, component_name, component): (Uid, &'static str, Box<dyn Any>),
) -> 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));
};
@@ -184,7 +213,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,
@@ -192,56 +221,36 @@ impl Storage
});
}
- let add_edge_archetype_id = match archetype_node
+ let add_edge_archetype_id = if let Some(add_edge_id) = 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
+ if !self.graph.contains_archetype(add_edge_id) {
+ let (_, add_edge_comp_ids) = 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);
+ .get_node_by_id(archetype_id)
+ .expect("Archetype should exist")
+ .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
+ self.graph.create_node(add_edge_id, &add_edge_comp_ids);
}
- };
- {
- let add_edge_archetype_node = self
+ add_edge_id
+ } else {
+ let archetype_node = self
.graph
- .get_node_by_id_mut(add_edge_archetype_id)
- .expect("Add edge archetype should exist");
+ .get_node_by_id(archetype_id)
+ .expect("Archetype should exist");
- let add_edge_archetype_edges = add_edge_archetype_node
- .get_or_insert_edges(component_id, ArchetypeEdges::default);
+ let (add_edge_id, add_edge_comp_ids) =
+ archetype_node.make_add_edge(component_id);
- add_edge_archetype_edges.remove = Some(archetype_id);
- }
+ if !self.graph.contains_archetype(add_edge_id) {
+ self.graph.create_node(add_edge_id, &add_edge_comp_ids);
+ }
+
+ add_edge_id
+ };
let archetype_node = self
.graph
@@ -261,7 +270,7 @@ impl Storage
entity.insert_component(
component_id,
- ArchetypeEntityComponent::new(component),
+ ArchetypeEntityComponent::new(component, component_id, component_name),
add_edge_archetype,
);
@@ -292,7 +301,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,
@@ -312,11 +321,6 @@ impl Storage
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);
@@ -367,9 +371,12 @@ impl Storage
) -> Vec<ArchetypeId>
{
let Some(mut search_iter) =
- self.graph.dfs_archetype_add_edges(ArchetypeId::new(&[]))
+ self.graph.dfs_archetype_add_edges(ArchetypeId::new_empty())
else {
// If the root archetype doesn't exist, no other archetype can exist either
+ //
+ // TODO: The above comment is not true. Cases where imaginary archetypes have
+ // been created should be handled as well
return Vec::new();
};
@@ -398,11 +405,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;
}
@@ -415,14 +418,6 @@ impl Storage
}
}
-impl TypeName for Storage
-{
- fn type_name(&self) -> &'static str
- {
- type_name::<Self>()
- }
-}
-
#[cfg(feature = "vizoxide")]
impl Storage
{
@@ -602,8 +597,7 @@ pub struct ArchetypeRefIter<'storage, 'search_terms>
search_terms: ArchetypeSearchTerms<'search_terms>,
}
-impl<'component_storage, 'search_terms> Iterator
- for ArchetypeRefIter<'component_storage, 'search_terms>
+impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
{
type Item = &'component_storage Archetype;
@@ -656,10 +650,7 @@ impl<'component_storage, 'search_terms> Iterator
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<_>>(),
+ component_ids: add_edge_archetype_comps.clone(),
},
);
@@ -673,8 +664,6 @@ impl<'component_storage, 'search_terms> Iterator
)),
found.into_iter(),
));
-
- continue;
}
_ => {
unreachable!();
@@ -784,7 +773,7 @@ mod tests
let archetype_node = new_storage
.graph
- .get_node_by_id(ArchetypeId::from_components_metadata(&[]))
+ .get_node_by_id(ArchetypeId::new_empty())
.expect("Archetype for entities with no component doesn't exist");
assert_eq!(archetype_node.archetype().component_cnt(), 0);
@@ -792,7 +781,7 @@ mod tests
assert_eq!(
new_storage.entity_archetype_lookup.get(&uid).copied(),
- Some(ArchetypeId::from_components_metadata(&[]))
+ Some(ArchetypeId::new_empty())
);
}
}
diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs
index 5306cf9..bb29701 100644
--- a/ecs/src/component/storage/archetype.rs
+++ b/ecs/src/component/storage/archetype.rs
@@ -1,12 +1,15 @@
+use std::any::Any;
+use std::array::IntoIter as ArrayIntoIter;
use std::hash::{DefaultHasher, Hash, Hasher};
+use std::iter::{Enumerate, Filter, Map, RepeatN, Zip};
+use std::option::IntoIter as OptionIntoIter;
use std::slice::Iter as SliceIter;
use hashbrown::HashMap;
-use crate::component::{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
@@ -116,8 +119,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)),
+ ),
+ }
+ }
+
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()
}
@@ -131,14 +180,70 @@ 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
{
- 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
+ );
+
+ if component_id.kind() == UidKind::Pair
+ && component_id.target_component() == Uid::wildcard()
+ {
+ return self.component_ids.iter().any(|other_comp_id| {
+ other_comp_id.kind() == UidKind::Pair
+ && other_comp_id.has_same_relation_as(component_id)
+ });
+ }
+
+ self.contains_component_with_exact_id(component_id)
+ }
+
+ pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool
+ {
+ let component_id_kind = component_id.kind();
+
+ debug_assert!(
+ component_id_kind == UidKind::Component
+ || (component_id_kind == UidKind::Pair
+ && component_id.target_component() != Uid::wildcard())
+ );
self.component_index_lookup.contains_key(&component_id)
}
}
+type MatchingComponentIterFilterFn = fn(&((usize, &Uid), Uid)) -> bool;
+
+type MatchingComponentIterMapFn = fn(((usize, &Uid), Uid)) -> (Uid, usize);
+
+type InnerMatchingComponentIterA<'archetype> = Map<
+ Filter<
+ Zip<Enumerate<SliceIter<'archetype, Uid>>, RepeatN<Uid>>,
+ MatchingComponentIterFilterFn,
+ >,
+ MatchingComponentIterMapFn,
+>;
+
+type InnerMatchingComponentIterB = Zip<ArrayIntoIter<Uid, 1>, OptionIntoIter<usize>>;
+
+#[derive(Debug)]
+pub struct MatchingComponentIter<'archetype>
+{
+ inner: Either<InnerMatchingComponentIterA<'archetype>, InnerMatchingComponentIterB>,
+}
+
+impl Iterator for MatchingComponentIter<'_>
+{
+ type Item = (Uid, usize);
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ self.inner.next()
+ }
+}
+
#[derive(Debug)]
pub struct EntityIter<'archetype>
{
@@ -209,26 +314,31 @@ impl Entity
#[derive(Debug)]
pub struct EntityComponent
{
- name: &'static str,
- component: Lock<Box<dyn Component>>,
+ id: Uid,
+ component: Lock<Box<dyn Any>>,
}
impl EntityComponent
{
- pub fn new(component: Box<dyn Component>) -> Self
+ pub fn new(
+ component: Box<dyn Any>,
+ component_id: Uid,
+ component_name: &'static str,
+ ) -> Self
{
Self {
- name: component.type_name(),
- component: Lock::new(component),
+ id: component_id,
+ component: Lock::new(component, component_name),
}
}
- pub fn name(&self) -> &str
+ #[allow(dead_code)]
+ pub fn id(&self) -> Uid
{
- self.name
+ self.id
}
- pub fn component(&self) -> &Lock<Box<dyn Component>>
+ pub fn component(&self) -> &Lock<Box<dyn Any>>
{
&self.component
}
@@ -243,56 +353,33 @@ pub struct Id
impl Id
{
- pub fn new(component_ids: &impl AsRef<[Uid]>) -> Self
+ pub fn new_empty() -> 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() }
+ Self { hash: 0 }
}
- pub fn from_components_metadata<'a>(
- components_metadata: impl IntoIterator<Item = &'a ComponentMetadata>,
- ) -> Self
+ pub fn new<'a>(component_ids: impl IntoIterator<Item = &'a Uid>) -> Self
{
let mut hasher = DefaultHasher::new();
let mut prev_component_id: Option<Uid> = None;
- let mut comp_metadata_iter = components_metadata.into_iter().peekable();
+ let mut component_id_iter = component_ids.into_iter().peekable();
- if comp_metadata_iter.peek().is_none() {
- return Self { hash: 0 };
+ if component_id_iter.peek().is_none() {
+ return Self::new_empty();
}
- for comp_metadata in comp_metadata_iter {
- if prev_component_id
- .is_some_and(|prev_comp_id| comp_metadata.id < prev_comp_id)
- {
+ for comp_id in component_id_iter {
+ if prev_component_id.is_some_and(|prev_comp_id| *comp_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;
- }
+ prev_component_id = Some(*comp_id);
- comp_metadata.id.hash(&mut hasher);
+ comp_id.hash(&mut hasher);
}
Self { hash: hasher.finish() }
diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs
index 11160e7..29fa937 100644
--- a/ecs/src/component/storage/graph.rs
+++ b/ecs/src/component/storage/graph.rs
@@ -140,19 +140,31 @@ impl Graph
}
fn create_missing_subset_node_edges(
- target_node: &ArchetypeNode,
+ target_node: &mut 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))
+ .find(|id| {
+ !subset_node
+ .archetype()
+ .contains_component_with_exact_id(*id)
+ })
.unwrap();
subset_node
.get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default)
.add = Some(subset_node.make_add_edge(uniq_comp_id).0);
+
+ if target_node.archetype().component_cnt()
+ == subset_node.archetype().component_cnt() + 1
+ {
+ target_node
+ .get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default)
+ .remove = Some(subset_node.archetype().id());
+ }
}
fn create_missing_superset_node_edges(
@@ -169,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 {
@@ -196,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
@@ -234,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)
}
@@ -245,13 +264,6 @@ impl ArchetypeNode
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
diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs
index a43f9ce..bab3d61 100644
--- a/ecs/src/entity.rs
+++ b/ecs/src/entity.rs
@@ -1,10 +1,22 @@
+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,
+ 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>
{
archetype: &'a Archetype,
@@ -21,15 +33,72 @@ impl<'a> Handle<'a>
self.entity.uid()
}
+ /// Returns a reference to the specified component in this entity. `None` is
+ /// returned if the component isn't found in the entity.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component's ID is not a component ID
+ /// - The component is mutably borrowed elsewhere
+ #[must_use]
+ pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'_, ComponentT>>
+ {
+ assert_eq!(ComponentT::id().kind(), UidKind::Component);
+
+ let component = self.get_matching_components(ComponentT::id()).next()?;
+
+ Some(
+ ComponentHandle::from_entity_component_ref(component).unwrap_or_else(|err| {
+ panic!(
+ "Taking component {} lock failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }),
+ )
+ }
+
+ /// Returns a mutable reference to the specified component in this entity. `None` is
+ /// returned if the component isn't found in the entity.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component's ID is not a component ID
+ /// - The component is borrowed elsewhere
+ #[must_use]
+ pub fn get_mut<ComponentT: Component>(
+ &self,
+ ) -> Option<ComponentHandleMut<'_, ComponentT>>
+ {
+ assert_eq!(ComponentT::id().kind(), UidKind::Component);
+
+ let component = self.get_matching_components(ComponentT::id()).next()?;
+
+ Some(
+ ComponentHandleMut::from_entity_component_ref(component).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 fn component_ids(&self) -> impl Iterator<Item = Uid> + '_
+ {
+ self.archetype.component_ids_sorted()
}
pub(crate) fn new(archetype: &'a Archetype, entity: &'a ArchetypeEntity) -> Self
@@ -38,6 +107,28 @@ impl<'a> Handle<'a>
}
}
+#[derive(Debug)]
+pub struct MatchingComponentIter<'a>
+{
+ inner: ArchetypeMatchingComponentIter<'a>,
+ entity: &'a ArchetypeEntity,
+}
+
+impl<'a> Iterator for MatchingComponentIter<'a>
+{
+ type Item = EntityComponentRef<'a>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let (matching_component_id, index) = self.inner.next()?;
+
+ Some(EntityComponentRef::new(
+ matching_component_id,
+ self.entity.components().get(index).unwrap(),
+ ))
+ }
+}
+
#[allow(clippy::module_name_repetitions)]
#[macro_export]
macro_rules! static_entity {
@@ -55,7 +146,7 @@ macro_rules! static_entity {
$crate::entity::CREATE_STATIC_ENTITIES
)]
#[linkme(crate=$crate::private::linkme)]
- static CREATE_STATIC_ENTITY: fn(&$crate::World) = |world| {
+ static CREATE_STATIC_ENTITY: fn(&mut $crate::World) = |world| {
world.create_entity_with_uid($components, *$ident);
};
}
@@ -65,4 +156,4 @@ macro_rules! static_entity {
#[distributed_slice]
#[doc(hidden)]
-pub static CREATE_STATIC_ENTITIES: [fn(&World)];
+pub static CREATE_STATIC_ENTITIES: [fn(&mut World)];
diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs
index b4edffc..72a78a3 100644
--- a/ecs/src/event/component.rs
+++ b/ecs/src/event/component.rs
@@ -1,84 +1,12 @@
//! Component events.
-use std::fmt::{Debug, Formatter};
-use std::marker::PhantomData;
+use std::convert::Infallible;
+use std::fmt::Debug;
-use ecs_macros::Component;
+use crate::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,
-}
+/// Pair relation for events emitted when:
+/// a) A entity with the target component is spawned.
+/// b) The target component is added to a entity.
+#[derive(Debug, Component)]
+pub struct Added(Infallible);
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index 3caaa6b..07b1cba 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -1,9 +1,10 @@
#![deny(clippy::all, clippy::pedantic)]
-use std::any::{type_name, TypeId};
+use std::any::{type_name, Any, TypeId};
use std::cell::RefCell;
use std::fmt::Debug;
use std::mem::ManuallyDrop;
+use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@@ -12,11 +13,18 @@ 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::component::{
+ Component,
+ IntoParts,
+ Parts as ComponentParts,
+ Removals as ComponentRemovals,
+ Sequence as ComponentSequence,
+};
+use crate::entity::{Handle as EntityHandle, CREATE_STATIC_ENTITIES};
+use crate::event::component::Added as ComponentAddedEvent;
use crate::extension::{Collector as ExtensionCollector, Extension};
-use crate::lock::{Lock, WriteGuard};
+use crate::lock::Lock;
+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;
@@ -27,33 +35,31 @@ 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::type_name::TypeName;
-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;
-pub mod lock;
+pub mod pair;
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;
+mod lock;
+
pub use ecs_macros::{Component, Sole};
pub use crate::query::Query;
@@ -80,16 +86,13 @@ impl World
world.add_sole(Stats::default()).ok();
for create_static_entity in CREATE_STATIC_ENTITIES {
- create_static_entity(&world);
+ create_static_entity(&mut 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,
@@ -103,29 +106,25 @@ impl World
#[tracing::instrument(skip_all)]
#[doc(hidden)]
- pub fn create_entity_with_uid<Comps>(&self, components: Comps, entity_uid: Uid)
+ pub fn create_entity_with_uid<Comps>(&mut 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,
- );
+ if let Err(err) = self.data.component_storage.create_entity(entity_uid) {
+ tracing::warn!("Failed to create entity: {err}");
+ return;
}
- for added_event_id in Comps::added_event_ids() {
- self.emit_event_by_id(added_event_id);
+ let added_component_ids = Self::add_entity_components(
+ entity_uid,
+ components.into_parts_array(),
+ &mut self.data.component_storage,
+ );
+
+ for comp_id in added_component_ids {
+ self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
}
}
@@ -148,27 +147,23 @@ impl World
{
self.create_entity((
SystemComponent { system: system.into_type_erased() },
- Relationship::<DependsOn, Phase>::new(phase_euid),
+ Pair::new::<DependsOn>(phase_euid),
));
}
- pub fn register_observer_system<'this, SystemImpl, Event>(
+ pub fn register_observer_system<'this, SystemImpl>(
&'this mut self,
system: impl System<'this, SystemImpl>,
- event: Event,
- ) where
- Event: Component,
+ event: Pair<Uid, Uid>,
+ )
{
- self.create_entity::<(SystemComponent, Event)>((
+ self.create_entity((
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);
@@ -184,19 +179,18 @@ impl World
Query::new(self)
}
- pub fn flexible_query<'terms>(
+ pub fn flexible_query<const MAX_TERM_CNT: usize>(
&self,
- terms: QueryTerms<'terms>,
- ) -> FlexibleQuery<'_, 'terms>
+ 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
+ /// Will panic if mutable internal lock cannot be acquired.
+ pub fn step(&mut self) -> StepResult
{
if self.stop.load(Ordering::Relaxed) {
return StepResult::Stop;
@@ -212,11 +206,14 @@ impl World
self.perform_phases();
- self.lock_component_storage_rw()
- .create_imaginary_archetypes();
+ self.data.component_storage.create_imaginary_archetypes();
+
+ let prev_pending_removals = std::mem::take(&mut self.data.pending_removals);
self.perform_queued_actions();
+ self.perform_removals(prev_pending_removals);
+
if self.stop.load(Ordering::Relaxed) {
return StepResult::Stop;
}
@@ -239,10 +236,7 @@ impl World
}
/// 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)
+ pub fn start_loop(&mut self)
{
while let StepResult::Continue = self.step() {}
}
@@ -260,13 +254,7 @@ impl World
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(
+ self.data.component_storage.create_vizoxide_archetype_graph(
name,
VizoxideArchetypeGraphParams {
create_node_name: |archetype, _| {
@@ -275,7 +263,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(", ")
))
@@ -302,16 +290,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);
@@ -321,38 +311,37 @@ 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 {
+ 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);
}
}
#[tracing::instrument(skip_all)]
- fn perform_queued_actions(&self)
+ fn perform_queued_actions(&mut self)
{
let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
{
@@ -369,92 +358,64 @@ impl World
let mut has_swapped_active_queue = false;
+ // TODO: Figure out a good way to handle situations where there are multiple
+ // AddComponents/RemoveComponents actions that affect the same entity.
for action in active_action_queue.drain(..) {
match action {
- Action::Spawn(components, component_added_event_ids) => {
+ Action::Spawn(components) => {
+ let new_entity_uid = Uid::new_unique(UidKind::Entity);
+
+ if let Err(err) =
+ self.data.component_storage.create_entity(new_entity_uid)
{
- 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,
- );
+ tracing::warn!("Failed to create entity: {err}");
+ continue;
}
+ let added_component_ids = Self::add_entity_components(
+ new_entity_uid,
+ components,
+ &mut self.data.component_storage,
+ );
+
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);
+ for comp_id in added_component_ids {
+ self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
}
}
Action::Despawn(entity_uid) => {
- self.despawn_entity(entity_uid, &mut has_swapped_active_queue);
+ Self::schedule_removal(
+ &mut self.data.component_storage,
+ &mut self.data.pending_removals,
+ entity_uid,
+ PendingRemoval::Entity,
+ );
}
- 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,
- );
- }
+ Action::AddComponents(entity_uid, components) => {
+ let added_component_ids = Self::add_entity_components(
+ entity_uid,
+ components,
+ &mut self.data.component_storage,
+ );
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);
+ for comp_id in added_component_ids {
+ self.emit_event_by_id::<ComponentAddedEvent>(comp_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::RemoveComponents(entity_uid, component_ids) => {
+ Self::schedule_removal(
+ &mut self.data.component_storage,
+ &mut self.data.pending_removals,
+ entity_uid,
+ PendingRemoval::Components(component_ids),
+ );
}
Action::Stop => {
self.stop.store(true, Ordering::Relaxed);
@@ -463,84 +424,128 @@ impl World
}
}
- #[tracing::instrument(skip_all)]
- fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool)
+ fn perform_removals(&mut self, removals: Vec<(Uid, PendingRemoval)>)
{
- 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;
+ for (entity_id, removal) in removals {
+ match removal {
+ PendingRemoval::Components(component_ids) => {
+ Self::remove_entity_components(
+ entity_id,
+ component_ids.into_iter().chain([ComponentRemovals::id()]),
+ &mut self.data.component_storage,
+ );
+ }
+ PendingRemoval::Entity => {
+ if let Err(err) = self.data.component_storage.remove_entity(entity_id)
+ {
+ tracing::error!("Failed to remove entity {entity_id}: {err}");
+ }
+ }
}
+ }
+ }
+
+ #[tracing::instrument(skip(component_storage, pending_removals))]
+ fn schedule_removal(
+ component_storage: &mut ComponentStorage,
+ pending_removals: &mut Vec<(Uid, PendingRemoval)>,
+ entity_uid: Uid,
+ removal: PendingRemoval,
+ )
+ {
+ let Some(ent_handle) = Self::get_entity(component_storage, entity_uid) else {
+ tracing::warn!("Cannot schedule removal. Entity does not exist");
+ 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);
- }
+ let component_ids = match removal {
+ PendingRemoval::Components(ref component_ids) => component_ids,
+ PendingRemoval::Entity => &ent_handle.component_ids().collect::<Vec<_>>(),
+ };
- for comp_removed_event_id in component_removed_event_uids {
- self.emit_event_by_id(comp_removed_event_id);
- }
+ let Some(mut component_removals) = ent_handle.get_mut::<ComponentRemovals>()
+ else {
+ Self::add_entity_components(
+ entity_uid,
+ [ComponentRemovals::from_iter(component_ids.iter().copied())
+ .into_parts()],
+ component_storage,
+ );
+
+ pending_removals.push((entity_uid, removal));
+
+ return;
+ };
+
+ component_removals.add_ids(component_ids.iter().copied());
+
+ drop(component_removals);
+
+ pending_removals.push((entity_uid, removal));
}
fn add_entity_components(
entity_uid: Uid,
- components: impl IntoIterator<Item = (Uid, Box<dyn Component>)>,
+ components: impl IntoIterator<Item = ComponentParts>,
component_storage: &mut ComponentStorage,
- )
+ ) -> Vec<Uid>
{
- for (component_id, component) in components {
- if let Err(err) = component_storage
- .add_entity_component(entity_uid, (component_id, component))
- {
+ let component_iter = components.into_iter();
+
+ let mut added_component_ids =
+ Vec::<Uid>::with_capacity(component_iter.size_hint().0);
+
+ for component_parts in component_iter {
+ let comp_id = component_parts.id();
+
+ if let Err(err) = component_storage.add_entity_component(
+ entity_uid,
+ (comp_id, component_parts.name(), component_parts.into_data()),
+ ) {
tracing::error!("Failed to add component to entity: {err}");
+ continue;
}
+
+ added_component_ids.push(comp_id);
}
+
+ added_component_ids
}
fn remove_entity_components(
entity_uid: Uid,
component_ids: impl IntoIterator<Item = Uid>,
component_storage: &mut ComponentStorage,
- )
+ ) -> Vec<Uid>
{
- for component_id in component_ids {
+ let component_id_iter = component_ids.into_iter();
+
+ let mut removed_component_ids =
+ Vec::<Uid>::with_capacity(component_id_iter.size_hint().0);
+
+ for component_id in component_id_iter {
if let Err(err) =
component_storage.remove_entity_component(entity_uid, component_id)
{
tracing::error!("Failed to remove component to entity: {err}");
+ continue;
}
+
+ removed_component_ids.push(component_id);
}
+
+ removed_component_ids
}
- fn emit_event_by_id(&self, event_id: Uid)
+ fn emit_event_by_id<Event: Component>(&self, target: Uid)
{
- let mut query_required_ids = [SystemComponent::id(), event_id];
+ if target.kind() == UidKind::Pair {
+ return;
+ }
let query = self.flexible_query(
- QueryTerms::builder()
- .with_required_ids(&mut query_required_ids)
+ QueryTerms::<2>::builder()
+ .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()])
.build(),
);
@@ -563,12 +568,19 @@ impl World
*has_swapped_active_queue = true;
}
- fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage>
+ fn get_entity(
+ component_storage: &mut ComponentStorage,
+ entity_uid: Uid,
+ ) -> Option<EntityHandle<'_>>
{
- self.data
- .component_storage
- .write_nonblock()
- .expect("Failed to acquire read-write component storage lock")
+ let archetype = component_storage.get_entity_archetype(entity_uid)?;
+
+ Some(EntityHandle::new(
+ archetype,
+ archetype
+ .get_entity_by_id(entity_uid)
+ .expect("Not possible"),
+ ))
}
}
@@ -590,30 +602,58 @@ pub enum StepResult
Stop,
}
-#[derive(Debug, Default)]
-pub struct WorldData
+#[derive(Debug)]
+struct WorldData
{
- component_storage: Arc<Lock<ComponentStorage>>,
+ component_storage: ComponentStorage,
sole_storage: SoleStorage,
- action_queue: Arc<ActionQueue>,
+ action_queue: Rc<ActionQueue>,
+ pending_removals: Vec<(Uid, PendingRemoval)>,
+}
+
+impl Default for WorldData
+{
+ fn default() -> Self
+ {
+ Self {
+ component_storage: ComponentStorage::default(),
+ sole_storage: SoleStorage::default(),
+ action_queue: Rc::new(ActionQueue::default()),
+ pending_removals: Vec::new(),
+ }
+ }
+}
+
+#[derive(Debug)]
+enum PendingRemoval
+{
+ Components(Vec<Uid>),
+ Entity,
}
#[derive(Debug)]
pub struct EntityComponentRef<'a>
{
- comp: &'a ArchetypeEntityComponent,
+ component_id: Uid,
+ component: &'a ArchetypeEntityComponent,
}
impl<'a> EntityComponentRef<'a>
{
- pub fn component(&self) -> &'a Lock<Box<dyn Component>>
+ fn component(&self) -> &'a Lock<Box<dyn Any>>
{
- self.comp.component()
+ self.component.component()
}
- fn new(comp: &'a ArchetypeEntityComponent) -> Self
+ #[must_use]
+ pub fn id(&self) -> Uid
{
- Self { comp }
+ self.component_id
+ }
+
+ fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self
+ {
+ Self { component_id, component: comp }
}
}
@@ -625,7 +665,7 @@ enum ActiveActionQueue
B,
}
-#[derive(Debug, Default)]
+#[derive(Debug)]
struct ActionQueue
{
queue_a: Lock<Vec<Action>>,
@@ -652,11 +692,15 @@ impl ActionQueue
}
}
-impl TypeName for ActionQueue
+impl Default for ActionQueue
{
- fn type_name(&self) -> &'static str
+ fn default() -> Self
{
- type_name::<Self>()
+ Self {
+ queue_a: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
+ queue_b: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
+ active_queue: RefCell::new(ActiveActionQueue::default()),
+ }
}
}
@@ -701,7 +745,7 @@ impl SoleStorage
self.storage.insert(
sole_type_id,
ManuallyDrop::new(StoredSole {
- sole: Arc::new(Lock::new(Box::new(sole))),
+ sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())),
drop_last,
}),
);
@@ -718,18 +762,9 @@ impl Drop for SoleStorage
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);
@@ -737,11 +772,6 @@ impl Drop for SoleStorage
}
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
index d6ed40e..0b36922 100644
--- a/ecs/src/lock.rs
+++ b/ecs/src/lock.rs
@@ -9,23 +9,21 @@ use parking_lot::{
RwLockWriteGuard,
};
-use crate::type_name::TypeName;
-
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub struct Lock<Value>
-where
- Value: TypeName,
{
inner: RwLock<Value>,
+ value_type_name: &'static str,
}
impl<Value> Lock<Value>
-where
- Value: TypeName,
{
- pub fn new(value: Value) -> Self
+ pub fn new(value: Value, value_type_name: &'static str) -> Self
{
- Self { inner: RwLock::new(value) }
+ Self {
+ inner: RwLock::new(value),
+ value_type_name,
+ }
}
/// Tries to a acquire a handle to the resource with read access.
@@ -36,9 +34,12 @@ where
{
let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?;
- tracing::trace!("Acquired lock to value of type {}", guard.type_name());
+ tracing::trace!("Acquired lock to value of type {}", self.value_type_name);
- Ok(ReadGuard { inner: guard })
+ Ok(ReadGuard {
+ inner: guard,
+ value_type_name: self.value_type_name,
+ })
}
/// Tries to a acquire a handle to the resource with mutable access.
@@ -51,15 +52,13 @@ where
tracing::trace!(
"Acquired mutable lock to value of type {}",
- guard.type_name()
+ self.value_type_name
);
- Ok(WriteGuard { inner: guard })
- }
-
- pub fn into_inner(self) -> Value
- {
- self.inner.into_inner()
+ Ok(WriteGuard {
+ inner: guard,
+ value_type_name: self.value_type_name,
+ })
}
}
@@ -75,23 +74,20 @@ pub enum Error
#[derive(Debug)]
pub struct ReadGuard<'guard, Value>
-where
- Value: TypeName,
{
inner: RwLockReadGuard<'guard, Value>,
+ value_type_name: &'static str,
}
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,
{
+ let value_type_name = self.value_type_name;
+
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
let inner = unsafe { std::ptr::read(&self.inner) };
@@ -99,13 +95,12 @@ where
MappedReadGuard {
inner: RwLockReadGuard::map(inner, func),
+ value_type_name,
}
}
}
impl<Value> Deref for ReadGuard<'_, Value>
-where
- Value: TypeName,
{
type Target = Value;
@@ -116,26 +111,21 @@ where
}
impl<Value> Drop for ReadGuard<'_, Value>
-where
- Value: TypeName,
{
fn drop(&mut self)
{
- tracing::trace!("Dropped lock to value of type {}", self.type_name());
+ tracing::trace!("Dropped lock to value of type {}", self.value_type_name);
}
}
#[derive(Debug)]
pub struct MappedReadGuard<'guard, Value>
-where
- Value: TypeName,
{
inner: MappedRwLockReadGuard<'guard, Value>,
+ value_type_name: &'static str,
}
impl<Value> Deref for MappedReadGuard<'_, Value>
-where
- Value: TypeName,
{
type Target = Value;
@@ -146,34 +136,32 @@ where
}
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());
+ tracing::trace!(
+ "Dropped mapped lock to value of type {}",
+ self.value_type_name
+ );
}
}
#[derive(Debug)]
pub struct WriteGuard<'guard, Value>
-where
- Value: TypeName,
{
inner: RwLockWriteGuard<'guard, Value>,
+ value_type_name: &'static str,
}
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,
{
+ let value_type_name = self.value_type_name;
+
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
let inner = unsafe { std::ptr::read(&self.inner) };
@@ -181,13 +169,12 @@ where
MappedWriteGuard {
inner: RwLockWriteGuard::map(inner, func),
+ value_type_name,
}
}
}
impl<Value> Deref for WriteGuard<'_, Value>
-where
- Value: TypeName,
{
type Target = Value;
@@ -198,8 +185,6 @@ where
}
impl<Value> DerefMut for WriteGuard<'_, Value>
-where
- Value: TypeName,
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -208,26 +193,24 @@ where
}
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());
+ tracing::trace!(
+ "Dropped mutable lock to value of type {}",
+ self.value_type_name
+ );
}
}
#[derive(Debug)]
pub struct MappedWriteGuard<'guard, Value>
-where
- Value: TypeName,
{
inner: MappedRwLockWriteGuard<'guard, Value>,
+ value_type_name: &'static str,
}
impl<Value> Deref for MappedWriteGuard<'_, Value>
-where
- Value: TypeName,
{
type Target = Value;
@@ -238,8 +221,6 @@ where
}
impl<Value> DerefMut for MappedWriteGuard<'_, Value>
-where
- Value: TypeName,
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -248,14 +229,12 @@ where
}
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()
+ self.value_type_name
);
}
}
diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs
new file mode 100644
index 0000000..4ff4995
--- /dev/null
+++ b/ecs/src/pair.rs
@@ -0,0 +1,198 @@
+use std::convert::Infallible;
+
+use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts};
+use crate::entity::{
+ Handle as EntityHandle,
+ MatchingComponentIter as EntityMatchingComponentIter,
+};
+use crate::query::{
+ TermWithField as QueryTermWithField,
+ TermsBuilder as QueryTermsBuilder,
+ TermsBuilderInterface,
+};
+use crate::uid::{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>
+{
+ #[must_use]
+ pub fn new<Relation: WithUid>(target: Uid) -> Self
+ {
+ Self { relation: Relation::uid(), target }
+ }
+
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ Uid::new_pair(&UidPairParams {
+ relation: self.relation,
+ target: self.target,
+ })
+ }
+}
+
+impl IntoComponentParts for Pair<Uid, Uid>
+{
+ fn into_parts(self) -> ComponentParts
+ {
+ ComponentParts::builder().name("Pair").build(self.id(), ())
+ }
+}
+
+impl<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,
+ 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,
+ pair_uid: Uid,
+}
+
+impl Handle<'_>
+{
+ #[must_use]
+ pub fn get_target_entity(&self) -> Option<EntityHandle<'_>>
+ {
+ let archetype = self
+ .world
+ .data
+ .component_storage
+ .get_entity_archetype(self.pair_uid.target_entity())?;
+
+ let Some(archetype_entity) =
+ archetype.get_entity_by_id(self.pair_uid.target_entity())
+ else {
+ unreachable!();
+ };
+
+ Some(EntityHandle::new(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,
+ 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..9f47fb8 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,4 @@ 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)));
+static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE)));
diff --git a/ecs/src/query.rs b/ecs/src/query.rs
index d7d2d1c..ccb7add 100644
--- a/ecs/src/query.rs
+++ b/ecs/src/query.rs
@@ -1,15 +1,19 @@
use std::any::type_name;
-use std::borrow::Cow;
use std::marker::PhantomData;
use seq_macro::seq;
-use crate::component::{Component, FromLockedOptional, Ref as ComponentRef};
+use crate::component::{
+ Component,
+ Handle as ComponentHandle,
+ HandleMut as ComponentHandleMut,
+};
use crate::entity::Handle as EntityHandle;
use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery};
use crate::system::{Param as SystemParam, System};
-use crate::uid::Uid;
-use crate::util::VecExt;
+use crate::uid::{Kind as UidKind, Uid, With as WithUid};
+use crate::util::array_vec::ArrayVec;
+use crate::util::Array;
use crate::World;
pub mod flexible;
@@ -22,7 +26,8 @@ where
FieldlessTerms: TermWithoutFieldTuple,
{
world: &'world World,
- inner: FlexibleQuery<'world, 'static>,
+ // A term tuple type can have a maximum of 17 elements
+ inner: FlexibleQuery<'world, 17>,
_pd: PhantomData<(FieldTerms, FieldlessTerms)>,
}
@@ -110,7 +115,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>>;
@@ -147,43 +152,51 @@ where
}
#[derive(Debug)]
-pub struct Terms<'a>
+pub struct Terms<const MAX_TERM_CNT: usize>
{
- required_components: Cow<'a, [Uid]>,
- excluded_components: Cow<'a, [Uid]>,
+ required_components: ArrayVec<Uid, MAX_TERM_CNT>,
+ excluded_components: ArrayVec<Uid, MAX_TERM_CNT>,
}
-impl<'a> Terms<'a>
+impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT>
{
- pub fn builder() -> TermsBuilder<'a>
+ pub fn builder() -> TermsBuilder<MAX_TERM_CNT>
{
TermsBuilder::default()
}
}
#[derive(Debug, Default)]
-pub struct TermsBuilder<'a>
+#[must_use]
+pub struct TermsBuilder<const MAX_TERM_CNT: usize>
{
- required_components: Cow<'a, [Uid]>,
- excluded_components: Cow<'a, [Uid]>,
+ required_components: ArrayVec<Uid, MAX_TERM_CNT>,
+ excluded_components: ArrayVec<Uid, MAX_TERM_CNT>,
}
-pub trait TermsBuilderInterface<'a>
+#[allow(clippy::return_self_not_must_use)]
+pub trait TermsBuilderInterface
{
- fn with<ComponentT: Component>(self) -> Self;
+ fn with<WithUidT: WithUid>(self) -> Self;
+
+ fn without<WithUidT: WithUid>(self) -> Self;
- fn without<ComponentT: Component>(self) -> Self;
+ fn with_required(self, ids: impl Array<Uid>) -> Self;
- fn with_required_ids(self, ids: &'a mut [Uid]) -> Self;
+ fn without_ids(self, ids: impl Array<Uid>) -> Self;
}
macro_rules! impl_terms_builder {
($($impl_content: tt)*) => {
- impl<'a> TermsBuilderInterface<'a> for TermsBuilder<'a> {
+ impl<const MAX_TERM_CNT: usize>
+ TermsBuilderInterface for TermsBuilder<MAX_TERM_CNT>
+ {
$($impl_content)*
}
- impl<'a> TermsBuilderInterface<'a> for &mut TermsBuilder<'a> {
+ impl<const MAX_TERM_CNT: usize>
+ TermsBuilderInterface for &mut TermsBuilder<MAX_TERM_CNT>
+ {
$($impl_content)*
}
};
@@ -191,74 +204,101 @@ 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
{
- if ComponentT::is_optional() {
- return self;
- }
+ let insert_index = self.required_components
+ .partition_point(|id| *id <= WithUidT::uid());
self.required_components
- .to_mut()
- .insert_at_partition_point_by_key(ComponentT::id(), |id| *id);
+ .insert(insert_index, WithUidT::uid());
self
}
#[allow(unused_mut)]
- fn without<ComponentT: Component>(mut self) -> Self
+ fn without<WithUidT: WithUid>(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 <= WithUidT::uid());
self.excluded_components
- .to_mut()
- .insert_at_partition_point_by_key(ComponentT::id(), |id| *id);
+ .insert(insert_index, WithUidT::uid());
self
}
#[allow(unused_mut)]
- fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self
+ fn with_required(mut self, mut ids: impl Array<Uid>) -> Self
{
- if ids.is_empty() {
+ if !ids.as_ref().is_sorted() {
+ ids.as_mut().sort();
+ }
+
+ if self.required_components.is_empty() {
+ self.required_components.extend(ids);
return self;
}
- if !ids.is_sorted() {
- ids.sort();
+ 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);
+
}
- if self.required_components.is_empty() {
- self.required_components = Cow::Borrowed(ids);
+ self
+ }
+ #[allow(unused_mut)]
+ fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self
+ {
+ if !ids.as_ref().is_sorted() {
+ ids.as_mut().sort();
+ }
+
+ if self.excluded_components.is_empty() {
+ self.excluded_components.extend(ids);
return self;
}
- let first_id_pp_index = self.required_components.partition_point(|req_comp_id| {
- req_comp_id <= ids.first().expect("Cannot happend since not empty")
- });
+ let mut id_iter = ids.into_iter();
- let removed = self
- .required_components
- .to_mut()
- .splice(first_id_pp_index..first_id_pp_index, ids.iter().copied());
+ while let Some(id) = id_iter.next() {
+ let insert_index = self.excluded_components
+ .partition_point(|other_id| *other_id <= id);
- assert_eq!(removed.count(), 0);
+ 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<'a> TermsBuilder<'a>
+impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT>
{
- pub fn build(self) -> Terms<'a>
+ #[must_use]
+ pub fn build(self) -> Terms<MAX_TERM_CNT>
{
- assert!(self.required_components.is_sorted());
- assert!(self.excluded_components.is_sorted());
+ debug_assert!(self.required_components.is_sorted());
+ debug_assert!(self.excluded_components.is_sorted());
Terms {
required_components: self.required_components,
@@ -269,14 +309,18 @@ impl<'a> TermsBuilder<'a>
pub trait TermWithoutField
{
- fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>);
+ 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(terms_builder: &mut TermsBuilder<'_>);
+ 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>,
@@ -284,30 +328,83 @@ pub trait TermWithField
) -> Self::Field<'world>;
}
-impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT
+impl<ComponentT: Component> TermWithField for &ComponentT
{
- type Field<'a> = ComponentRefT::Handle<'a>;
+ type Field<'a> = ComponentHandle<'a, ComponentT>;
- fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>)
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ )
{
- terms_builder.with::<ComponentRefT::Component>();
+ terms_builder.with::<ComponentT>();
}
fn get_field<'world>(
entity_handle: &EntityHandle<'world>,
- world: &'world 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| {
+ assert_eq!(ComponentT::id().kind(), UidKind::Component);
+
+ let Some(component) = entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()
+ else {
panic!(
- "Taking component {} lock failed: {err}",
- type_name::<ComponentRefT::Component>()
+ concat!(
+ "Component {} was not found in entity {}. There ",
+ "is most likely a bug in the entity querying"
+ ),
+ type_name::<ComponentT>(),
+ entity_handle.uid()
+ );
+ };
+
+ Self::Field::from_entity_component_ref(component).unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ })
+ }
+}
+
+impl<ComponentT: Component> TermWithField for &mut ComponentT
+{
+ type Field<'a> = ComponentHandleMut<'a, ComponentT>;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ terms_builder.with::<ComponentT>();
+ }
+
+ fn get_field<'world>(
+ entity_handle: &EntityHandle<'world>,
+ _world: &'world World,
+ ) -> Self::Field<'world>
+ {
+ assert_eq!(ComponentT::id().kind(), UidKind::Component);
+
+ let Some(component) = entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()
+ else {
+ panic!(
+ concat!(
+ "Component {} was not found in entity {}. There ",
+ "is most likely a bug in the entity querying"
+ ),
+ type_name::<ComponentT>(),
+ entity_handle.uid()
+ );
+ };
+
+ Self::Field::from_entity_component_ref(component).unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
);
})
}
@@ -315,14 +412,18 @@ impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT
pub trait TermWithoutFieldTuple
{
- fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>);
+ 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(terms_builder: &mut TermsBuilder<'_>);
+ 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>,
@@ -332,7 +433,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,
@@ -343,7 +444,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,
{
@@ -362,7 +463,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,
{
@@ -378,7 +479,7 @@ where
pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
where
- FieldTerms: TermWithFieldTuple + 'world,
+ FieldTerms: TermWithFieldTuple,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
{
world: &'world World,
@@ -389,7 +490,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,
{
@@ -411,7 +512,9 @@ macro_rules! impl_term_sequence {
seq!(I in 0..=$c {
impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*)
{
- fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>)
+ 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);
@@ -423,7 +526,9 @@ macro_rules! impl_term_sequence {
{
type Fields<'component> = (#(Term~I::Field<'component>,)*);
- fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>)
+ 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);
@@ -448,14 +553,22 @@ seq!(C in 0..=16 {
impl TermWithoutFieldTuple for ()
{
- fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {}
+ 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(_terms_builder: &mut TermsBuilder<'_>) {}
+ fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ }
fn get_fields<'component>(
_entity_handle: &EntityHandle<'_>,
diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs
index 652b96f..add30b0 100644
--- a/ecs/src/query/flexible.rs
+++ b/ecs/src/query/flexible.rs
@@ -2,25 +2,20 @@
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::component::storage::{ArchetypeRefIter, ArchetypeSearchTerms};
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, 'terms>
+pub struct Query<'world, const MAX_TERM_CNT: usize>
{
- component_storage: ReadGuard<'world, ComponentStorage>,
- terms: Terms<'terms>,
+ world: &'world World,
+ terms: Terms<MAX_TERM_CNT>,
}
-impl<'world, 'terms> Query<'world, 'terms>
+impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
{
/// Iterates over the entities matching this query.
#[must_use]
@@ -28,6 +23,8 @@ impl<'world, 'terms> Query<'world, 'terms>
{
Iter {
iter: self
+ .world
+ .data
.component_storage
.search_archetypes(ArchetypeSearchTerms {
required_components: &self.terms.required_components,
@@ -42,16 +39,20 @@ impl<'world, 'terms> Query<'world, 'terms>
}
}
- pub(crate) fn new(world: &'world World, terms: Terms<'terms>) -> Self
+ 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,
- }
+ Self { world, terms }
+ }
+}
+
+impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT>
+{
+ type IntoIter = Iter<'query>;
+ type Item = EntityHandle<'query>;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ self.iter()
}
}
diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs
index 7f24147..9c772da 100644
--- a/ecs/src/query/term.rs
+++ b/ecs/src/query/term.rs
@@ -1,38 +1,115 @@
+use std::any::type_name;
use std::marker::PhantomData;
-use crate::component::Component;
-use crate::query::{TermWithoutField, TermsBuilder, TermsBuilderInterface};
+use crate::component::{
+ Component,
+ Handle as ComponentHandle,
+ HandleMut as ComponentHandleMut,
+};
+use crate::query::{
+ TermWithField,
+ TermWithoutField,
+ TermsBuilder,
+ TermsBuilderInterface,
+};
+use crate::uid::With as WithUid;
-pub struct With<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(terms_builder: &mut TermsBuilder<'_>)
+ 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(terms_builder: &mut TermsBuilder<'_>)
+ 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>();
+ }
+}
+
+impl<ComponentT: Component> TermWithField for Option<&ComponentT>
+{
+ type Field<'a> = Option<ComponentHandle<'a, ComponentT>>;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ }
+
+ fn get_field<'world>(
+ entity_handle: &crate::entity::Handle<'world>,
+ _world: &'world crate::World,
+ ) -> Self::Field<'world>
+ {
+ Some(
+ ComponentHandle::<'world, ComponentT>::from_entity_component_ref(
+ entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?,
+ )
+ .unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }),
+ )
+ }
+}
+
+impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>
+{
+ type Field<'a> = Option<ComponentHandleMut<'a, ComponentT>>;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ }
+
+ fn get_field<'world>(
+ entity_handle: &crate::entity::Handle<'world>,
+ _world: &'world crate::World,
+ ) -> Self::Field<'world>
+ {
+ Some(
+ ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref(
+ entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?,
+ )
+ .unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<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
index 5af5ce3..1cce419 100644
--- a/ecs/src/sole.rs
+++ b/ecs/src/sole.rs
@@ -6,11 +6,10 @@ 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
+pub trait Sole: Any
{
fn drop_last(&self) -> bool;
@@ -40,14 +39,6 @@ impl Debug for dyn Sole
}
}
-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>
diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs
index 9d911ee..54f6979 100644
--- a/ecs/src/system/stateful.rs
+++ b/ecs/src/system/stateful.rs
@@ -1,4 +1,4 @@
-use std::any::{Any, TypeId};
+use std::any::{type_name, Any, TypeId};
use std::panic::{RefUnwindSafe, UnwindSafe};
use hashbrown::HashMap;
@@ -25,7 +25,7 @@ use crate::World;
pub struct Stateful<Func>
{
func: Func,
- local_components: HashMap<Uid, Lock<Box<dyn Component>>>,
+ local_components: HashMap<Uid, Lock<Box<dyn Any>>>,
}
macro_rules! impl_system {
@@ -127,7 +127,10 @@ macro_rules! impl_system {
self.local_components
.insert(
LocalComponent::id(),
- Lock::new(Box::new(local_component))
+ Lock::new(
+ Box::new(local_component),
+ type_name::<LocalComponent>()
+ )
);
}
}
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
index c3ed85b..feed62c 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,41 @@ 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),
+ }
+ }
+
+ /// Returns a new pair UID.
+ ///
+ /// # Panics
+ /// Will panic if either the given relation or target is a pair UID.
+ #[must_use]
+ pub fn new_pair(params: &PairParams) -> Self
+ {
+ assert_ne!(
+ params.relation.kind(),
+ Kind::Pair,
+ "Pair relation cannot be a pair"
+ );
+
+ assert_ne!(
+ params.target.kind(),
+ Kind::Pair,
+ "Pair target cannot be a pair"
+ );
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(params.target.id()))
+ | RELATION_BITS.field_prep(u64::from(params.relation.id()))
+ | KIND_BITS.field_prep(Kind::Pair as u64),
+ }
+ }
+
+ #[must_use]
pub fn id(&self) -> u32
{
let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else {
@@ -57,6 +101,81 @@ 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.
+ #[must_use]
+ pub fn relation_component(&self) -> Self
+ {
+ assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(self.relation()))
+ | KIND_BITS.field_prep(Kind::Component as u64),
+ }
+ }
+
+ #[must_use]
+ pub fn has_same_relation_as(&self, other: Self) -> bool
+ {
+ self.relation() == other.relation()
+ }
+
+ /// If this `Uid` is a pair, returns the relation as a entity `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if this `Uid` is not a pair.
+ #[must_use]
+ pub fn relation_entity(&self) -> Self
+ {
+ assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(self.relation()))
+ | KIND_BITS.field_prep(Kind::Entity as u64),
+ }
+ }
+
+ /// If this `Uid` is a pair, returns the target as a component `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if this `Uid` is not a pair.
+ #[must_use]
+ pub fn target_component(&self) -> Self
+ {
+ assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(self.id()))
+ | KIND_BITS.field_prep(Kind::Component as u64),
+ }
+ }
+
+ /// If this `Uid` is a pair, returns the target as a entity `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if this `Uid` is not a pair.
+ #[must_use]
+ pub fn target_entity(&self) -> Self
+ {
+ assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(self.id()))
+ | KIND_BITS.field_prep(Kind::Entity as u64),
+ }
+ }
+
+ fn relation(self) -> u32
+ {
+ let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else {
+ unreachable!("Uid relation does not fit in u32");
+ };
+
+ relation
+ }
}
impl Debug for Uid
@@ -70,3 +189,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/src/util.rs b/ecs/src/util.rs
index 6f7aed7..9ab4dc6 100644
--- a/ecs/src/util.rs
+++ b/ecs/src/util.rs
@@ -4,6 +4,8 @@ use std::ops::{BitAnd, Deref};
use hashbrown::HashMap;
+pub(crate) mod array_vec;
+
pub trait VecExt<Item>
{
fn insert_at_partition_point_by_key<Key>(
diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs
new file mode 100644
index 0000000..a37b1f9
--- /dev/null
+++ b/ecs/src/util/array_vec.rs
@@ -0,0 +1,131 @@
+use std::mem::MaybeUninit;
+use std::ops::{Deref, DerefMut};
+
+#[derive(Debug)]
+pub struct ArrayVec<Item, const CAPACITY: usize>
+{
+ items: [MaybeUninit<Item>; CAPACITY],
+ len: usize,
+}
+
+impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY>
+{
+ #[inline]
+ #[must_use]
+ pub fn len(&self) -> usize
+ {
+ self.len
+ }
+
+ #[inline]
+ #[must_use]
+ pub fn is_empty(&self) -> bool
+ {
+ self.len == 0
+ }
+
+ pub fn push(&mut self, item: Item)
+ {
+ assert!(self.len < CAPACITY);
+
+ self.items[self.len].write(item);
+
+ self.len += 1;
+ }
+
+ pub fn insert(&mut self, index: usize, item: Item)
+ {
+ assert!(index <= self.len);
+ assert!(self.len < CAPACITY);
+
+ if index == self.len {
+ self.push(item);
+ return;
+ }
+
+ unsafe {
+ std::ptr::copy(
+ &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]
+ {
+ let ptr = &raw const self.items[..self.len];
+
+ unsafe { &*(ptr as *const [Item]) }
+ }
+}
+
+impl<Item, const CAPACITY: usize> AsMut<[Item]> for ArrayVec<Item, CAPACITY>
+{
+ fn as_mut(&mut self) -> &mut [Item]
+ {
+ let ptr = &raw mut self.items[..self.len];
+
+ unsafe { &mut *(ptr as *mut [Item]) }
+ }
+}
+
+impl<Item, const CAPACITY: usize> Deref for ArrayVec<Item, CAPACITY>
+{
+ type Target = [Item];
+
+ fn deref(&self) -> &Self::Target
+ {
+ self.as_ref()
+ }
+}
+
+impl<Item, const CAPACITY: usize> DerefMut for ArrayVec<Item, CAPACITY>
+{
+ fn deref_mut(&mut self) -> &mut Self::Target
+ {
+ self.as_mut()
+ }
+}
+
+impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY>
+{
+ fn default() -> Self
+ {
+ Self {
+ items: [const { MaybeUninit::uninit() }; CAPACITY],
+ len: 0,
+ }
+ }
+}
+
+impl<Item, const CAPACITY: usize> Drop for ArrayVec<Item, CAPACITY>
+{
+ fn drop(&mut self)
+ {
+ for item in &mut self.items[..self.len] {
+ // SAFETY: The items from index 0 to the length index will always be
+ // initialized and satisfy all the invariants of the Item type.
+ unsafe {
+ item.assume_init_drop();
+ }
+ }
+ }
+}