summaryrefslogtreecommitdiff
path: root/ecs/src
diff options
context:
space:
mode:
Diffstat (limited to 'ecs/src')
-rw-r--r--ecs/src/actions.rs145
-rw-r--r--ecs/src/component.rs192
-rw-r--r--ecs/src/component/local.rs75
-rw-r--r--ecs/src/component/storage.rs13
-rw-r--r--ecs/src/component/storage/archetype.rs24
-rw-r--r--ecs/src/component/storage/graph.rs2
-rw-r--r--ecs/src/entity.rs209
-rw-r--r--ecs/src/entity/obtainer.rs29
-rw-r--r--ecs/src/event.rs104
-rw-r--r--ecs/src/event/component.rs57
-rw-r--r--ecs/src/extension.rs17
-rw-r--r--ecs/src/lib.rs486
-rw-r--r--ecs/src/lock.rs67
-rw-r--r--ecs/src/pair.rs595
-rw-r--r--ecs/src/phase.rs18
-rw-r--r--ecs/src/private.rs2
-rw-r--r--ecs/src/query.rs121
-rw-r--r--ecs/src/query/flexible.rs12
-rw-r--r--ecs/src/query/term.rs20
-rw-r--r--ecs/src/sole.rs16
-rw-r--r--ecs/src/system.rs126
-rw-r--r--ecs/src/system/initializable.rs131
-rw-r--r--ecs/src/system/observer.rs310
-rw-r--r--ecs/src/system/stateful.rs295
-rw-r--r--ecs/src/uid.rs38
-rw-r--r--ecs/src/util.rs90
-rw-r--r--ecs/src/util/array_vec.rs18
27 files changed, 2361 insertions, 851 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs
index e0efeda..549e341 100644
--- a/ecs/src/actions.rs
+++ b/ecs/src/actions.rs
@@ -1,9 +1,12 @@
+use std::any::type_name;
use std::marker::PhantomData;
use std::rc::{Rc, Weak};
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::event::component::Removed;
+use crate::pair::Pair;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::uid::{Kind as UidKind, Uid, WithUidTuple};
use crate::{ActionQueue, World};
/// Used to to queue up actions for a [`World`] to perform.
@@ -11,16 +14,23 @@ use crate::{ActionQueue, World};
pub struct Actions<'world>
{
action_queue: &'world ActionQueue,
- action_queue_weak: Weak<ActionQueue>,
+ world: Option<&'world World>,
}
-impl<'world> Actions<'world>
+impl Actions<'_>
{
- /// Queues up a entity to spawn at the end of the current tick.
- pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps)
+ /// Queues up a entity to spawn at the end of the current tick, returning the [`Uid`]
+ /// that the entity will have.
+ pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps) -> Uid
{
- self.action_queue
- .push(Action::Spawn(components.into_parts_array().into()));
+ let new_entity_uid = Uid::new_unique(UidKind::Entity);
+
+ self.action_queue.push(Action::Spawn(
+ new_entity_uid,
+ components.into_parts_array().into(),
+ ));
+
+ new_entity_uid
}
/// Queues up despawning a entity at the end of the current tick.
@@ -28,6 +38,31 @@ impl<'world> Actions<'world>
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
+ let Some(world) = self.world else {
+ self.action_queue.push(Action::Despawn(entity_uid));
+ return;
+ };
+
+ let Some(ent) = world.get_entity(entity_uid) else {
+ tracing::warn!("Cannot entity that doesn't exist");
+ return;
+ };
+
+ // TODO: Submit all events with a single function call to reduce overhead
+ for comp_id in ent.component_ids() {
+ if comp_id.kind() == UidKind::Pair {
+ continue;
+ }
+
+ world.event_submitter().submit_event(
+ &Pair::builder()
+ .relation::<Removed>()
+ .target_id(comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+
self.action_queue.push(Action::Despawn(entity_uid));
}
@@ -49,6 +84,7 @@ impl<'world> Actions<'world>
}
/// Queues up removing component(s) from a entity at the end of the current tick.
+ #[tracing::instrument(skip(self, component_ids))]
pub fn remove_components(
&mut self,
entity_uid: Uid,
@@ -63,10 +99,50 @@ impl<'world> Actions<'world>
return;
}
- self.action_queue.push(Action::RemoveComponents(
- entity_uid,
- component_ids.collect(),
- ));
+ let Some(world) = self.world else {
+ self.action_queue.push(Action::RemoveComponents(
+ entity_uid,
+ component_ids.collect(),
+ ));
+ return;
+ };
+
+ let Some(ent) = world.get_entity(entity_uid) else {
+ tracing::warn!("Cannot remove components from entity that doesn't exist");
+ return;
+ };
+
+ let component_ids = component_ids
+ .filter(|comp_id| ent.has_component(*comp_id))
+ .collect::<Vec<_>>();
+
+ if component_ids.is_empty() {
+ return;
+ }
+
+ // TODO: Submit all events with a single function call to reduce overhead
+ for comp_id in &component_ids {
+ if comp_id.kind() == UidKind::Pair {
+ continue;
+ }
+
+ world.event_submitter().submit_event(
+ &Pair::builder()
+ .relation::<Removed>()
+ .target_id(*comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+
+ self.action_queue
+ .push(Action::RemoveComponents(entity_uid, component_ids));
+ }
+
+ /// Queues up removing component(s) from a entity at the end of the current tick.
+ pub fn remove_comps<Ids: WithUidTuple>(&mut self, entity_uid: Uid)
+ {
+ self.remove_components(entity_uid, Ids::uids());
}
/// Stops the [`World`]. The world will finish the current tick and that tick will be
@@ -79,19 +155,22 @@ impl<'world> Actions<'world>
/// Returns a struct which holds a weak reference to the [`World`] that `Actions`
/// references and that can be used to aquire a new `Actions` instance if the
/// referenced [`World`] is still alive.
+ ///
+ /// # Panics
+ /// This function will panic if `self` was retrieved from a [`WeakRef`].
#[must_use]
pub fn to_weak_ref(&self) -> WeakRef
{
- WeakRef {
- action_queue: self.action_queue_weak.clone(),
- }
- }
+ let world = self.world.unwrap_or_else(|| {
+ panic!(
+ "This function cannot be called if the {} was retrieved from a {}",
+ type_name::<Self>(),
+ type_name::<WeakRef>()
+ )
+ });
- fn new(action_queue: &'world Rc<ActionQueue>) -> Self
- {
- Self {
- action_queue,
- action_queue_weak: Rc::downgrade(action_queue),
+ WeakRef {
+ action_queue: Rc::downgrade(&world.data.action_queue),
}
}
}
@@ -100,19 +179,12 @@ impl<'world> SystemParam<'world> for Actions<'world>
{
type Input = ();
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
{
- Self::new(&world.data.action_queue)
+ Self {
+ action_queue: &world.data.action_queue,
+ world: Some(world),
+ }
}
}
@@ -151,7 +223,10 @@ impl Ref<'_>
#[must_use]
pub fn to_actions(&self) -> Actions<'_>
{
- Actions::new(&self.action_queue)
+ Actions {
+ action_queue: &self.action_queue,
+ world: None,
+ }
}
}
@@ -159,7 +234,7 @@ impl Ref<'_>
#[derive(Debug)]
pub(crate) enum Action
{
- Spawn(Vec<ComponentParts>),
+ Spawn(Uid, Vec<ComponentParts>),
Despawn(Uid),
AddComponents(Uid, Vec<ComponentParts>),
RemoveComponents(Uid, Vec<Uid>),
diff --git a/ecs/src/component.rs b/ecs/src/component.rs
index a0ed752..17b279b 100644
--- a/ecs/src/component.rs
+++ b/ecs/src/component.rs
@@ -1,10 +1,11 @@
use std::any::{type_name, Any};
-use std::error::Error;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use seq_macro::seq;
+use crate::event::component::Changed;
+use crate::event::Submitter as EventSubmitter;
use crate::lock::{
Error as LockError,
MappedReadGuard,
@@ -12,6 +13,7 @@ use crate::lock::{
ReadGuard,
WriteGuard,
};
+use crate::pair::Pair;
use crate::system::Input as SystemInput;
use crate::uid::Uid;
use crate::util::Array;
@@ -23,14 +25,6 @@ pub(crate) mod storage;
pub trait Component: SystemInput + Any
{
- type HandleMut<'component>: HandleFromEntityComponentRef<'component>
- where
- Self: Sized;
-
- type Handle<'component>: HandleFromEntityComponentRef<'component>
- where
- Self: Sized;
-
/// Returns the ID of this component.
fn id() -> Uid
where
@@ -77,87 +71,44 @@ pub trait Sequence
fn into_parts_array(self) -> Self::PartsArray;
}
-/// [`Component`] metadata.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub struct Metadata
+#[derive(Debug)]
+pub struct Handle<'a, DataT: 'static>
{
- pub id: Uid,
+ inner: MappedReadGuard<'a, DataT>,
}
-impl Metadata
+impl<'comp, DataT: 'static> Handle<'comp, DataT>
{
- #[must_use]
- pub fn of<ComponentT: Component>() -> Self
- {
- Self { id: ComponentT::id() }
- }
-}
-
-pub trait HandleFromEntityComponentRef<'comp>: Sized
-{
- type Error: Error;
-
/// Creates a new handle instance from a [`EntityComponentRef`].
///
/// # Errors
- /// See the implementation's [`Self::Error`] type.
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- world: &'comp World,
- ) -> Result<Self, Self::Error>;
-}
-
-#[derive(Debug)]
-pub struct Handle<'a, ComponentData: 'static>
-{
- inner: MappedReadGuard<'a, ComponentData>,
-}
-
-impl<'a, ComponentData: 'static> Handle<'a, ComponentData>
-{
- pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Any>>) -> Self
+ /// Will return `Err` if acquiring the component's lock fails.
+ pub fn from_entity_component_ref(
+ entity_component_ref: &EntityComponentRef<'comp>,
+ ) -> Result<Self, HandleError>
{
- Self {
- inner: inner.map(|component| {
- component
- .downcast_ref::<ComponentData>()
- .unwrap_or_else(|| {
- panic!(
- "Failed to downcast component to type {}",
- type_name::<ComponentData>()
- );
- })
- }),
- }
+ Self::new(
+ entity_component_ref
+ .component()
+ .read_nonblock()
+ .map_err(AcquireLockError)?,
+ )
}
-}
-
-impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp>
- for Handle<'comp, ComponentData>
-{
- type Error = HandleError;
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- _world: &'comp World,
- ) -> Result<Self, Self::Error>
+ fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Result<Self, HandleError>
{
- let entity_comp =
- entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?;
-
- Ok(Self::new(
- entity_comp
- .component()
- .read_nonblock()
- .map_err(AcquireComponentLockFailed)?,
- ))
+ Ok(Self {
+ inner: ReadGuard::try_map(inner, |component| {
+ component.downcast_ref::<DataT>()
+ })
+ .map_err(|_| HandleError::IncorrectType)?,
+ })
}
}
-impl<ComponentData: 'static> Deref for Handle<'_, ComponentData>
+impl<DataT: 'static> Deref for Handle<'_, DataT>
{
- type Target = ComponentData;
+ type Target = DataT;
fn deref(&self) -> &Self::Target
{
@@ -166,55 +117,54 @@ impl<ComponentData: 'static> Deref for Handle<'_, ComponentData>
}
#[derive(Debug)]
-pub struct HandleMut<'a, ComponentData: 'static>
+pub struct HandleMut<'a, DataT: 'static>
{
- inner: MappedWriteGuard<'a, ComponentData>,
+ entity_component_ref: EntityComponentRef<'a>,
+ inner: MappedWriteGuard<'a, DataT>,
+ event_submitter: EventSubmitter<'a>,
}
-impl<'a, ComponentData: 'static> HandleMut<'a, ComponentData>
+impl<'comp, DataT: 'static> HandleMut<'comp, DataT>
{
- pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Any>>) -> 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>,
+ world: &'comp World,
+ ) -> Result<Self, HandleError>
{
- Self {
- inner: inner.map(|component| {
- component
- .downcast_mut::<ComponentData>()
- .unwrap_or_else(|| {
- panic!(
- "Failed to downcast component to type {}",
- type_name::<ComponentData>()
- );
- })
- }),
- }
+ let inner = entity_component_ref
+ .component()
+ .write_nonblock()
+ .map_err(AcquireLockError)?;
+
+ Ok(Self {
+ entity_component_ref: entity_component_ref.clone(),
+ inner: WriteGuard::try_map(inner, |component| {
+ component.downcast_mut::<DataT>()
+ })
+ .map_err(|_| HandleError::IncorrectType)?,
+ event_submitter: world.event_submitter(),
+ })
}
-}
-impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp>
- for HandleMut<'comp, ComponentData>
-{
- type Error = HandleError;
-
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- _world: &'comp World,
- ) -> Result<Self, Self::Error>
+ pub fn set_changed(&self)
{
- let entity_comp =
- entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?;
-
- Ok(Self::new(
- entity_comp
- .component()
- .write_nonblock()
- .map_err(AcquireComponentLockFailed)?,
- ))
+ self.event_submitter.submit_event(
+ &Pair::builder()
+ .relation::<Changed>()
+ .target_id(self.entity_component_ref.id())
+ .build(),
+ self.entity_component_ref.entity_id(),
+ );
}
}
-impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData>
+impl<DataT: 'static> Deref for HandleMut<'_, DataT>
{
- type Target = ComponentData;
+ type Target = DataT;
fn deref(&self) -> &Self::Target
{
@@ -222,7 +172,7 @@ impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData>
}
}
-impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData>
+impl<DataT: 'static> DerefMut for HandleMut<'_, DataT>
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -234,15 +184,15 @@ impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData>
pub enum HandleError
{
#[error(transparent)]
- AcquireComponentLockFailed(#[from] AcquireComponentLockFailed),
+ AcquireLockFailed(#[from] AcquireLockError),
- #[error("Component does not exist")]
- ComponentDoesNotExist,
+ #[error("Incorrect component type")]
+ IncorrectType,
}
#[derive(Debug, thiserror::Error)]
-#[error(transparent)]
-pub struct AcquireComponentLockFailed(LockError);
+#[error("Failed to acquire component lock")]
+pub struct AcquireLockError(#[source] LockError);
macro_rules! inner {
($c: tt) => {
@@ -357,6 +307,12 @@ impl PartsBuilder
data: Box::new(data),
}
}
+
+ #[must_use]
+ pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts
+ {
+ Parts { id, name: self.name, data }
+ }
}
impl Default for PartsBuilder
diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs
index 0f6f641..b19a30b 100644
--- a/ecs/src/component/local.rs
+++ b/ecs/src/component/local.rs
@@ -1,7 +1,17 @@
+use std::any::type_name;
use std::ops::{Deref, DerefMut};
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::system::{Param as SystemParam, System};
+use ecs_macros::Component;
+
+use crate::component::{
+ Component,
+ HandleMut as ComponentHandleMut,
+ IntoParts as _,
+ Parts as ComponentParts,
+};
+use crate::pair::Pair;
+use crate::system::initializable::Param as InitializableParam;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
use crate::World;
/// Holds a component which is local to a single system.
@@ -17,27 +27,52 @@ where
{
type Input = LocalComponent;
- fn initialize<SystemImpl>(
- system: &mut impl System<'world, SystemImpl>,
- input: Self::Input,
- )
+ fn new(world: &'world World, system_metadata: &SystemMetadata) -> Self
{
- system.set_local_component(input);
- }
+ let Some(system_ent) = world.get_entity(system_metadata.ent_id) else {
+ panic!(
+ "System entity with ID {} does not exist",
+ system_metadata.ent_id
+ );
+ };
- fn new<SystemImpl>(
- system: &'world impl System<'world, SystemImpl>,
- _world: &'world World,
- ) -> Self
- {
- let local_component = system
- .get_local_component_mut::<LocalComponent>()
- .expect("Local component is uninitialized");
+ let Some(local_component) = system_ent.get_with_id_mut::<LocalComponent>(
+ Pair::builder()
+ .relation::<IsLocalComponent>()
+ .target::<LocalComponent>()
+ .build()
+ .id(),
+ ) else {
+ panic!(
+ "Local component {} of system with ID {} is uninitialized",
+ type_name::<LocalComponent>(),
+ system_metadata.ent_id
+ );
+ };
Self { local_component }
}
}
+impl<'world, LocalComponent, SystemT> InitializableParam<'world, SystemT>
+ for Local<'world, LocalComponent>
+where
+ LocalComponent: Component,
+ SystemT: SystemWithLocalComponents,
+ Self: SystemParam<'world, Input = LocalComponent>,
+{
+ fn initialize(system: &mut SystemT, input: Self::Input)
+ {
+ system.add_local_component(
+ Pair::builder()
+ .relation::<IsLocalComponent>()
+ .target_as_data(input)
+ .build()
+ .into_parts(),
+ );
+ }
+}
+
impl<LocalComponent> Deref for Local<'_, LocalComponent>
where
LocalComponent: Component,
@@ -59,3 +94,11 @@ where
&mut self.local_component
}
}
+
+pub trait SystemWithLocalComponents
+{
+ fn add_local_component(&mut self, component_parts: ComponentParts);
+}
+
+#[derive(Component)]
+struct IsLocalComponent;
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs
index b27b552..a8711c5 100644
--- a/ecs/src/component/storage.rs
+++ b/ecs/src/component/storage.rs
@@ -270,7 +270,7 @@ impl Storage
entity.insert_component(
component_id,
- ArchetypeEntityComponent::new(component, component_id, component_name),
+ ArchetypeEntityComponent::new(component, component_name),
add_edge_archetype,
);
@@ -642,10 +642,10 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
let mut add_edge_archetype_comps =
archetype.component_ids_sorted().collect::<Vec<_>>();
- add_edge_archetype_comps.insert_at_partition_point_by_key(
- add_edge_component_id,
- |comp_id| *comp_id,
- );
+ add_edge_archetype_comps
+ .insert_at_part_pt_by_key(add_edge_component_id, |comp_id| {
+ comp_id
+ });
self.storage.imaginary_archetypes.borrow_mut().push(
ImaginaryArchetype {
@@ -712,8 +712,7 @@ impl ArchetypeRefIter<'_, '_>
let mut add_edge_comp_ids = imaginary_archetype_comps.to_vec();
- add_edge_comp_ids
- .insert_at_partition_point_by_key(unique_comp_id, |id| *id);
+ add_edge_comp_ids.insert_at_part_pt_by_key(unique_comp_id, |id| id);
let add_edge = ArchetypeId::new(&add_edge_comp_ids);
diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs
index a88e0e8..d96632e 100644
--- a/ecs/src/component/storage/archetype.rs
+++ b/ecs/src/component/storage/archetype.rs
@@ -122,7 +122,7 @@ impl Archetype
pub fn get_matching_component_indices(
&self,
component_id: Uid,
- ) -> MatchingComponentIter
+ ) -> MatchingComponentIter<'_>
{
assert!(
component_id.kind() == UidKind::Component
@@ -314,29 +314,18 @@ impl Entity
#[derive(Debug)]
pub struct EntityComponent
{
- id: Uid,
component: Lock<Box<dyn Any>>,
}
impl EntityComponent
{
- pub fn new(
- component: Box<dyn Any>,
- component_id: Uid,
- component_name: &'static str,
- ) -> Self
+ pub fn new(component: Box<dyn Any>, component_name: &'static str) -> Self
{
Self {
- id: component_id,
component: Lock::new(component, component_name),
}
}
- pub fn id(&self) -> Uid
- {
- self.id
- }
-
pub fn component(&self) -> &Lock<Box<dyn Any>>
{
&self.component
@@ -370,11 +359,10 @@ impl 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"
- );
- }
+ assert!(
+ prev_component_id.is_none_or(|prev_comp_id| *comp_id >= prev_comp_id),
+ "Cannot create archetype ID from a unsorted component metadata list"
+ );
prev_component_id = Some(*comp_id);
diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs
index 29fa937..76200f9 100644
--- a/ecs/src/component/storage/graph.rs
+++ b/ecs/src/component/storage/graph.rs
@@ -80,7 +80,7 @@ impl Graph
pub fn dfs_archetype_add_edges(
&self,
archetype_id: ArchetypeId,
- ) -> Option<ArchetypeAddEdgeDfsIter>
+ ) -> Option<ArchetypeAddEdgeDfsIter<'_>>
{
let node = self.get_node_by_id(archetype_id)?;
diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs
index 562f7ea..ad9f179 100644
--- a/ecs/src/entity.rs
+++ b/ecs/src/entity.rs
@@ -1,6 +1,6 @@
use std::any::type_name;
-
-use linkme::distributed_slice;
+use std::ops::Deref;
+use std::sync::LazyLock;
use crate::component::storage::archetype::{
Archetype,
@@ -10,19 +10,26 @@ use crate::component::storage::archetype::{
use crate::component::{
Component,
Handle as ComponentHandle,
- HandleFromEntityComponentRef,
HandleMut as ComponentHandleMut,
};
+use crate::pair::{
+ ComponentOrWildcard,
+ MultipleWithWildcard as PairMultipleWithWildcard,
+ Pair,
+ WithWildcard as PairWithWildcard,
+};
use crate::uid::{Kind as UidKind, Uid};
use crate::{EntityComponentRef, World};
+pub mod obtainer;
+
/// A handle to a entity.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Handle<'a>
{
- world: &'a World,
archetype: &'a Archetype,
entity: &'a ArchetypeEntity,
+ world: &'a World,
}
impl<'a> Handle<'a>
@@ -43,20 +50,21 @@ impl<'a> Handle<'a>
/// - 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>>
+ pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'a, ComponentT>>
{
assert_eq!(ComponentT::id().kind(), UidKind::Component);
let component = self.get_matching_components(ComponentT::id()).next()?;
Some(
- ComponentHandle::from_entity_component_ref(Some(component), self.world)
- .unwrap_or_else(|err| {
+ ComponentHandle::from_entity_component_ref(&component).unwrap_or_else(
+ |err| {
panic!(
- "Taking component {} lock failed: {err}",
+ "Creating handle to component {} failed: {err}",
type_name::<ComponentT>()
);
- }),
+ },
+ ),
)
}
@@ -70,23 +78,118 @@ impl<'a> Handle<'a>
#[must_use]
pub fn get_mut<ComponentT: Component>(
&self,
- ) -> Option<ComponentHandleMut<'_, ComponentT>>
+ ) -> Option<ComponentHandleMut<'a, ComponentT>>
{
assert_eq!(ComponentT::id().kind(), UidKind::Component);
let component = self.get_matching_components(ComponentT::id()).next()?;
Some(
- ComponentHandleMut::from_entity_component_ref(Some(component), self.world)
+ ComponentHandleMut::from_entity_component_ref(&component, self.world)
.unwrap_or_else(|err| {
panic!(
- "Taking component {} lock failed: {err}",
+ "Creating handle to component {} failed: {err}",
type_name::<ComponentT>()
);
}),
)
}
+ /// Returns a reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The ID is not a component/pair ID
+ /// - The component is borrowed mutably elsewhere
+ /// - The component type is incorrect
+ #[must_use]
+ pub fn get_with_id<ComponentDataT: 'static>(
+ &self,
+ id: Uid,
+ ) -> Option<ComponentHandle<'a, ComponentDataT>>
+ {
+ assert!(
+ matches!(id.kind(), UidKind::Component | UidKind::Pair),
+ "ID {id:?} is not a component/pair ID"
+ );
+
+ let component = self.get_matching_components(id).next()?;
+
+ Some(
+ ComponentHandle::from_entity_component_ref(&component).unwrap_or_else(
+ |err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentDataT>()
+ );
+ },
+ ),
+ )
+ }
+
+ /// Returns a mutable reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The ID is not a component/pair ID
+ /// - The component is borrowed elsewhere
+ /// - The component type is incorrect
+ #[must_use]
+ pub fn get_with_id_mut<ComponentDataT: 'static>(
+ &self,
+ id: Uid,
+ ) -> Option<ComponentHandleMut<'a, ComponentDataT>>
+ {
+ assert!(
+ matches!(id.kind(), UidKind::Component | UidKind::Pair),
+ "ID {id:?} is not a component/pair ID"
+ );
+
+ let component = self.get_matching_components(id).next()?;
+
+ Some(
+ ComponentHandleMut::from_entity_component_ref(&component, self.world)
+ .unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentDataT>()
+ );
+ }),
+ )
+ }
+
+ #[must_use]
+ pub fn get_first_wildcard_pair_match<Relation, Target>(
+ &self,
+ ) -> Option<PairWithWildcard<'a, Relation, Target>>
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ let mut matching_comps = self.get_matching_components(
+ Pair::builder()
+ .relation_id(Relation::uid())
+ .target_id(Target::uid())
+ .build()
+ .id(),
+ );
+
+ Some(PairWithWildcard::new(self.world, matching_comps.next()?))
+ }
+
+ #[must_use]
+ pub fn get_wildcard_pair_matches<Relation, Target>(
+ &self,
+ ) -> PairMultipleWithWildcard<'a, Relation, Target>
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ PairMultipleWithWildcard::new(self.world, self.clone())
+ }
+
#[inline]
#[must_use]
pub fn get_matching_components(&self, component_uid: Uid)
@@ -98,13 +201,27 @@ impl<'a> Handle<'a>
}
}
+ /// Returns whether or not this entity contains a component with the specified `Uid`.
+ #[must_use]
+ pub fn has_component(&self, component_uid: Uid) -> bool
+ {
+ self.archetype
+ .contains_component_with_exact_id(component_uid)
+ }
+
+ /// Returns the `Uids`s of the components this entity has.
+ pub fn component_ids(&self) -> impl Iterator<Item = Uid> + '_
+ {
+ self.archetype.component_ids_sorted()
+ }
+
pub(crate) fn new(
- world: &'a World,
archetype: &'a Archetype,
entity: &'a ArchetypeEntity,
+ world: &'a World,
) -> Self
{
- Self { world, archetype, entity }
+ Self { archetype, entity, world }
}
}
@@ -126,35 +243,53 @@ impl<'a> Iterator for MatchingComponentIter<'a>
Some(EntityComponentRef::new(
matching_component_id,
self.entity.components().get(index).unwrap(),
+ self.entity.uid(),
))
}
}
+/// The data type of a declaration of a entity.
+#[derive(Debug)]
+pub struct Declaration
+{
+ uid: LazyLock<Uid>,
+ create_func: fn(&mut World),
+}
+
+impl Declaration
+{
+ pub(crate) fn create(&self, world: &mut World)
+ {
+ (self.create_func)(world);
+ }
+
+ #[doc(hidden)]
+ pub const fn new(create_func: fn(&mut World)) -> Self
+ {
+ Self {
+ uid: LazyLock::new(|| Uid::new_unique(UidKind::Entity)),
+ create_func,
+ }
+ }
+}
+
+impl Deref for Declaration
+{
+ type Target = Uid;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.uid
+ }
+}
+
#[allow(clippy::module_name_repetitions)]
#[macro_export]
-macro_rules! static_entity {
+macro_rules! declare_entity {
($visibility: vis $ident: ident, $components: expr) => {
- $visibility static $ident: ::std::sync::LazyLock<$crate::uid::Uid> =
- ::std::sync::LazyLock::new(|| {
- $crate::uid::Uid::new_unique($crate::uid::Kind::Entity)
+ $visibility static $ident: $crate::entity::Declaration =
+ $crate::entity::Declaration::new(|world| {
+ world.create_entity_with_uid(*$ident, $components);
});
-
- $crate::private::paste::paste! {
- mod [<__ecs_ $ident:lower _static_entity_priv>] {
- use super::*;
-
- #[$crate::private::linkme::distributed_slice(
- $crate::entity::CREATE_STATIC_ENTITIES
- )]
- #[linkme(crate=$crate::private::linkme)]
- static CREATE_STATIC_ENTITY: fn(&mut $crate::World) = |world| {
- world.create_entity_with_uid($components, *$ident);
- };
- }
- }
}
}
-
-#[distributed_slice]
-#[doc(hidden)]
-pub static CREATE_STATIC_ENTITIES: [fn(&mut World)];
diff --git a/ecs/src/entity/obtainer.rs b/ecs/src/entity/obtainer.rs
new file mode 100644
index 0000000..6c2ea96
--- /dev/null
+++ b/ecs/src/entity/obtainer.rs
@@ -0,0 +1,29 @@
+use crate::entity::Handle as EntityHandle;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::uid::Uid;
+use crate::World;
+
+#[derive(Debug)]
+pub struct Obtainer<'world>
+{
+ world: &'world World,
+}
+
+impl<'world> SystemParam<'world> for Obtainer<'world>
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ Self { world }
+ }
+}
+
+impl Obtainer<'_>
+{
+ #[must_use]
+ pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>>
+ {
+ self.world.get_entity(entity_id)
+ }
+}
diff --git a/ecs/src/event.rs b/ecs/src/event.rs
index 9cea807..15455b6 100644
--- a/ecs/src/event.rs
+++ b/ecs/src/event.rs
@@ -1 +1,105 @@
+use crate::lock::Lock;
+use crate::pair::Pair;
+use crate::uid::{Kind as UidKind, Uid};
+use crate::util::VecExt;
+
pub mod component;
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Emitted<'a>
+{
+ pub event: Uid,
+ pub match_ids: &'a [Uid],
+}
+
+#[derive(Debug)]
+pub struct Submitter<'world>
+{
+ new_events: &'world Lock<NewEvents>,
+}
+
+impl<'world> Submitter<'world>
+{
+ /// Submits a event to be handled later.
+ ///
+ /// # Panics
+ /// Will panic if unable to acquire a read-write lock to the event store.
+ pub fn submit_event(&self, event: &Pair<Uid, Uid>, match_id: Uid)
+ {
+ let mut new_events_lock = self
+ .new_events
+ .write_nonblock()
+ .expect("Failed to acquire read-write lock to new events");
+
+ new_events_lock.push_event_match(event, match_id);
+ }
+
+ pub(crate) fn new(new_events: &'world Lock<NewEvents>) -> Self
+ {
+ Self { new_events }
+ }
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct NewEvents
+{
+ events: Vec<(Uid, Matches)>,
+}
+
+impl NewEvents
+{
+ pub fn push_event_match(&mut self, event: &Pair<Uid, Uid>, match_id: Uid)
+ {
+ let event_id = event.id();
+
+ assert_eq!(event_id.kind(), UidKind::Pair);
+
+ if let Ok(event_index) = self
+ .events
+ .binary_search_by_key(&event_id, |(other_event_id, _)| *other_event_id)
+ {
+ let Some((_, matches)) = self.events.get_mut(event_index) else {
+ unreachable!();
+ };
+
+ matches.sorted_push(match_id);
+
+ return;
+ }
+
+ self.events.insert_at_part_pt_by_key(
+ (event_id, Matches { match_ids: Vec::from([match_id]) }),
+ |(other_event_id, _)| other_event_id,
+ );
+ }
+
+ pub fn take(&mut self) -> Vec<(Uid, Matches)>
+ {
+ std::mem::take(&mut self.events)
+ }
+
+ pub fn is_empty(&self) -> bool
+ {
+ self.events.is_empty()
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct Matches
+{
+ pub match_ids: Vec<Uid>,
+}
+
+impl Matches
+{
+ fn sorted_push(&mut self, match_id: Uid)
+ {
+ if self.match_ids.binary_search(&match_id).is_ok() {
+ return;
+ }
+
+ self.match_ids
+ .insert_at_part_pt_by_key(match_id, |other_match_id| other_match_id);
+ }
+}
diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs
index ef09480..ed6b7cf 100644
--- a/ecs/src/event/component.rs
+++ b/ecs/src/event/component.rs
@@ -1,8 +1,12 @@
//! Component events.
use std::convert::Infallible;
-use std::fmt::Debug;
+use crate::component::{Handle as ComponentHandle, HandleMut as ComponentHandleMut};
+use crate::entity::Handle as EntityHandle;
+use crate::pair::Pair;
+use crate::system::observer::EventMatch;
+use crate::util::impl_multiple;
use crate::Component;
/// Pair relation for events emitted when:
@@ -11,8 +15,57 @@ use crate::Component;
#[derive(Debug, Component)]
pub struct Added(Infallible);
-/// Pair relation for events emitted when:
+/// Pair relation for events emitted **before**:
/// a) The target component is removed from a entity.
/// b) A entity with the target component is despawned.
#[derive(Debug, Component)]
pub struct Removed(Infallible);
+
+#[derive(Debug, Component)]
+pub struct Changed(Infallible);
+
+impl_multiple!(
+ EventMatch,
+ (
+ impl<Target: Component> _<'_><Pair<Removed, Target>> (removed),
+ impl<Target: Component> _<'_><Pair<Added, Target>> (added),
+ impl<Target: Component> _<'_><Pair<Changed, Target>> (changed)
+ )
+ cb=(type_params=(observable_type), event_name) => {
+ paste::paste! {
+ #[must_use]
+ pub fn [<get_ $event_name _comp>](&self) -> ComponentHandle<'_, Target>
+ {
+ let ent = self.get_ent_infallible();
+
+ let Some(comp) = ent.get::<Target>() else {
+ unreachable!();
+ };
+
+ comp
+ }
+
+ #[must_use]
+ pub fn [<get_ $event_name _comp_mut>](&self) -> ComponentHandleMut<'_, Target>
+ {
+ let ent = self.get_ent_infallible();
+
+ let Some(comp) = ent.get_mut::<Target>() else {
+ unreachable!();
+ };
+
+ comp
+ }
+ }
+
+ #[must_use]
+ pub fn get_ent_infallible(&self) -> EntityHandle<'_>
+ {
+ let Some(ent) = self.get_entity() else {
+ unreachable!();
+ };
+
+ ent
+ }
+ }
+);
diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs
index 42ebef9..9c6614b 100644
--- a/ecs/src/extension.rs
+++ b/ecs/src/extension.rs
@@ -1,5 +1,7 @@
use crate::component::Sequence as ComponentSequence;
+use crate::entity::Declaration as EntityDeclaration;
use crate::sole::Sole;
+use crate::system::observer::Observer;
use crate::system::System;
use crate::uid::Uid;
use crate::{SoleAlreadyExistsError, World};
@@ -34,6 +36,15 @@ impl<'world> Collector<'world>
self.world.register_system(phase_euid, system);
}
+ /// Adds a observer system to the [`World`].
+ pub fn add_observer<'this, SystemImpl>(
+ &'this mut self,
+ observer: impl Observer<'this, SystemImpl>,
+ )
+ {
+ self.world.register_observer(observer);
+ }
+
/// Adds a entity to the [`World`].
pub fn add_entity<Comps>(&mut self, components: Comps)
where
@@ -42,6 +53,12 @@ impl<'world> Collector<'world>
self.world.create_entity(components);
}
+ /// Adds a declared entity to the [`World`].
+ pub fn add_declared_entity(&mut self, entity_decl: &EntityDeclaration)
+ {
+ self.world.create_declared_entity(entity_decl);
+ }
+
/// Adds a globally shared singleton value to the [`World`].
///
/// # Errors
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index 53abc6b..f6fba64 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -1,7 +1,6 @@
#![deny(clippy::all, clippy::pedantic)]
use std::any::{type_name, Any, TypeId};
-use std::cell::RefCell;
use std::fmt::Debug;
use std::mem::ManuallyDrop;
use std::rc::Rc;
@@ -15,31 +14,36 @@ use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComp
use crate::component::storage::Storage as ComponentStorage;
use crate::component::{
Component,
+ IntoParts as IntoComponentParts,
Parts as ComponentParts,
Sequence as ComponentSequence,
};
-use crate::entity::CREATE_STATIC_ENTITIES;
-use crate::event::component::{
- Added as ComponentAddedEvent,
- Removed as ComponentRemovedEvent,
-};
+use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle};
+use crate::event::component::Added;
+use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter};
use crate::extension::{Collector as ExtensionCollector, Extension};
use crate::lock::Lock;
use crate::pair::{ChildOf, DependsOn, Pair};
-use crate::phase::{Phase, START as START_PHASE};
+use crate::phase::{
+ Phase,
+ POST_UPDATE as POST_UPDATE_PHASE,
+ PRE_UPDATE as PRE_UPDATE_PHASE,
+ START as START_PHASE,
+ UPDATE as UPDATE_PHASE,
+};
use crate::query::flexible::Query as FlexibleQuery;
-use crate::query::term::Without;
use crate::query::{
- Iter as QueryIter,
TermWithFieldTuple as QueryTermWithFieldTuple,
TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
Terms as QueryTerms,
TermsBuilderInterface,
+ MAX_TERM_CNT as QUERY_MAX_TERM_CNT,
};
-use crate::sole::Sole;
+use crate::sole::{Single, Sole};
use crate::stats::Stats;
-use crate::system::{System, SystemComponent};
-use crate::uid::{Kind as UidKind, Uid, Wildcard};
+use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent};
+use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent};
+use crate::uid::{Kind as UidKind, Uid};
pub mod actions;
pub mod component;
@@ -56,9 +60,6 @@ pub mod tuple;
pub mod uid;
pub mod util;
-#[doc(hidden)]
-pub mod private;
-
mod lock;
pub use ecs_macros::{Component, Sole};
@@ -84,49 +85,49 @@ impl World
is_first_tick: AtomicBool::new(false),
};
- world.add_sole(Stats::default()).ok();
+ crate::phase::spawn_entities(&mut world);
- for create_static_entity in CREATE_STATIC_ENTITIES {
- create_static_entity(&mut world);
- }
+ world.add_sole(Stats::default()).ok();
world
}
- /// Creates a new entity with the given components.
+ /// Creates a entity with the given components. A new unique [`Uid`] will be generated
+ /// for this entity.
pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid
where
Comps: ComponentSequence,
{
let entity_uid = Uid::new_unique(UidKind::Entity);
- self.create_entity_with_uid(components, entity_uid);
+ self.create_entity_with_uid(entity_uid, components);
entity_uid
}
+ /// Creates a entity with the given components. The entity will have the specified
+ /// [`Uid`].
#[tracing::instrument(skip_all)]
- #[doc(hidden)]
- pub fn create_entity_with_uid<Comps>(&mut self, components: Comps, entity_uid: Uid)
+ pub fn create_entity_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps)
where
Comps: ComponentSequence,
{
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- if let Err(err) = self.data.component_storage.create_entity(entity_uid) {
- tracing::warn!("Failed to create entity: {err}");
- return;
- }
+ self.create_ent(entity_uid, components.into_parts_array());
+ }
- let added_component_ids = Self::add_entity_components(
- entity_uid,
- components.into_parts_array(),
+ pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts)
+ {
+ Self::add_entity_components(
+ entity_id,
+ [component_parts],
&mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
);
+ }
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
+ pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration)
+ {
+ entity_decl.create(self);
}
/// Adds a globally shared singleton value.
@@ -140,28 +141,45 @@ impl World
self.data.sole_storage.insert(sole)
}
- pub fn register_system<'this, SystemImpl>(
+ pub fn register_observer<'this, SystemImpl, ObserverT>(
&'this mut self,
- phase_euid: Uid,
- system: impl System<'this, SystemImpl>,
- )
+ observer: ObserverT,
+ ) where
+ ObserverT: Observer<'this, SystemImpl>,
{
- self.create_entity((
- SystemComponent { system: system.into_type_erased() },
- Pair::new::<DependsOn>(phase_euid),
- ));
+ let (wrapper_comp, mut system_callbacks) = observer.finish_observer();
+
+ let ent_id = Uid::new_unique(UidKind::Entity);
+
+ self.create_ent(
+ ent_id,
+ [wrapper_comp.into_parts()].into_iter().chain(
+ ObserverT::observed_events()
+ .into_iter()
+ .map(IntoComponentParts::into_parts),
+ ),
+ );
+
+ system_callbacks.on_created(self, SystemMetadata { ent_id });
}
- pub fn register_observer_system<'this, SystemImpl>(
+ pub fn register_system<'this, SystemImpl>(
&'this mut self,
+ phase_euid: Uid,
system: impl System<'this, SystemImpl>,
- event: Pair<Uid, Uid>,
)
{
- self.create_entity((
- SystemComponent { system: system.into_type_erased() },
- event,
+ let (type_erased_system, mut system_callbacks) = system.finish();
+
+ let system_ent_id = self.create_entity((
+ SystemComponent { system: type_erased_system },
+ Pair::builder()
+ .relation::<DependsOn>()
+ .target_id(phase_euid)
+ .build(),
));
+
+ system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });
}
/// Adds a extensions.
@@ -172,7 +190,9 @@ impl World
extension.collect(extension_collector);
}
- pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms>
+ pub fn query<FieldTerms, FieldlessTerms>(
+ &self,
+ ) -> Query<'_, FieldTerms, FieldlessTerms>
where
FieldTerms: QueryTermWithFieldTuple,
FieldlessTerms: QueryTermWithoutFieldTuple,
@@ -188,6 +208,30 @@ impl World
FlexibleQuery::new(self, terms)
}
+ pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>>
+ {
+ let archetype = self
+ .data
+ .component_storage
+ .get_entity_archetype(entity_id)?;
+
+ let Some(entity) = archetype.get_entity_by_id(entity_id) else {
+ unreachable!("Should exist since archetype was found by entity id");
+ };
+
+ Some(EntityHandle::new(archetype, entity, self))
+ }
+
+ pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>>
+ {
+ Some(Single::new(self.data.sole_storage.get::<SoleT>()?))
+ }
+
+ pub fn event_submitter(&self) -> EventSubmitter<'_>
+ {
+ EventSubmitter::new(&self.data.new_events)
+ }
+
/// Performs a single tick.
/// # Panics
/// Will panic if mutable internal lock cannot be acquired.
@@ -207,6 +251,8 @@ impl World
self.perform_phases();
+ self.emit_new_events();
+
self.data.component_storage.create_imaginary_archetypes();
self.perform_queued_actions();
@@ -215,17 +261,9 @@ impl World
return StepResult::Stop;
}
- let mut stats_lock = self
- .data
- .sole_storage
- .get::<Stats>()
- .expect("No stats sole found")
- .write_nonblock()
- .expect("Failed to aquire read-write stats sole lock");
-
- let stats = stats_lock
- .downcast_mut::<Stats>()
- .expect("Casting stats sole to Stats type failed");
+ let Some(mut stats) = self.get_sole::<Stats>() else {
+ unreachable!(); // Reason: is added in World::new
+ };
stats.current_tick += 1;
@@ -285,23 +323,51 @@ impl World
)
}
+ #[tracing::instrument(skip_all)]
+ fn create_ent(
+ &mut self,
+ entity_uid: Uid,
+ components: impl IntoIterator<Item = ComponentParts>,
+ )
+ {
+ debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
+
+ if let Err(err) = self.data.component_storage.create_entity(entity_uid) {
+ tracing::warn!("Failed to create entity: {err}");
+ return;
+ }
+
+ Self::add_entity_components(
+ entity_uid,
+ components,
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+
fn query_and_run_systems(&self, phase_euid: Uid)
{
- let system_query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([
- SystemComponent::id(),
- Pair::new::<DependsOn>(phase_euid).id(),
- ])
- .build(),
+ let system_query = Query::<(&SystemComponent,)>::from_flexible_query(
+ self.flexible_query(
+ QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
+ .with_required([
+ SystemComponent::id(),
+ Pair::builder()
+ .relation::<DependsOn>()
+ .target_id(phase_euid)
+ .build()
+ .id(),
+ ])
+ .build(),
+ ),
);
- for (system_component,) in
- QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter())
- {
+ for (system_ent_id, (system_component,)) in system_query.iter_with_euids() {
// SAFETY: The world lives long enough
unsafe {
- system_component.system.run(self);
+ system_component
+ .system
+ .run(self, SystemMetadata { ent_id: system_ent_id });
}
}
}
@@ -312,7 +378,11 @@ impl World
QueryTerms::<2>::builder()
.with_required([
Phase::id(),
- Pair::new::<ChildOf>(parent_phase_euid).id(),
+ Pair::builder()
+ .relation::<ChildOf>()
+ .target_id(parent_phase_euid)
+ .build()
+ .id(),
])
.build(),
);
@@ -323,43 +393,63 @@ impl World
}
}
+ fn perform_single_phase(&self, phase_entity_id: Uid)
+ {
+ self.query_and_run_systems(phase_entity_id);
+ self.perform_child_phases(phase_entity_id);
+ }
+
fn perform_phases(&self)
{
- let phase_query = self.query::<(&Phase,), (Without<Pair<ChildOf, Wildcard>>,)>();
+ self.perform_single_phase(*PRE_UPDATE_PHASE);
+ self.perform_single_phase(*UPDATE_PHASE);
+ self.perform_single_phase(*POST_UPDATE_PHASE);
+ }
- for (phase_entity_id, _) in phase_query.iter_with_euids() {
- if phase_entity_id == *START_PHASE {
- continue;
- }
+ fn emit_new_events(&self)
+ {
+ loop {
+ let new_events = {
+ let mut new_events_lock = self
+ .data
+ .new_events
+ .write_nonblock()
+ .expect("Failed to acquire read-write lock to new events");
+
+ if new_events_lock.is_empty() {
+ break;
+ }
- self.query_and_run_systems(phase_entity_id);
- self.perform_child_phases(phase_entity_id);
+ new_events_lock.take()
+ };
+
+ for (event_id, event_matches) in new_events {
+ self.emit_event_observers(
+ event_id,
+ &EmittedEvent {
+ event: event_id,
+ match_ids: &event_matches.match_ids,
+ },
+ );
+ }
}
}
#[tracing::instrument(skip_all)]
fn perform_queued_actions(&mut self)
{
- let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
- {
- ActiveActionQueue::A => &self.data.action_queue.queue_a,
- ActiveActionQueue::B => &self.data.action_queue.queue_b,
- }
- .write_nonblock()
- .unwrap_or_else(|err| {
- panic!(
- "Failed to take read-write action queue lock {:?}: {err}",
- self.data.action_queue.active_queue
- );
- });
-
- let mut has_swapped_active_queue = false;
+ let mut action_queue_lock = self
+ .data
+ .action_queue
+ .queue
+ .write_nonblock()
+ .unwrap_or_else(|err| {
+ panic!("Failed to take read-write action queue lock: {err}",);
+ });
- for action in active_action_queue.drain(..) {
+ for action in action_queue_lock.drain(..) {
match action {
- Action::Spawn(components) => {
- let new_entity_uid = Uid::new_unique(UidKind::Entity);
-
+ Action::Spawn(new_entity_uid, components) => {
if let Err(err) =
self.data.component_storage.create_entity(new_entity_uid)
{
@@ -367,69 +457,34 @@ impl World
continue;
}
- let added_component_ids = Self::add_entity_components(
+ Self::add_entity_components(
new_entity_uid,
components,
&mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
);
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
}
Action::Despawn(entity_uid) => {
- let removed_entity =
- match self.data.component_storage.remove_entity(entity_uid) {
- Ok(components) => components,
- Err(err) => {
- tracing::error!("Failed to despawn entity: {err}");
- return;
- }
- };
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for removed_ent_comp in removed_entity.components() {
- self.emit_event_by_id::<ComponentRemovedEvent>(
- removed_ent_comp.id(),
- );
+ if let Err(err) =
+ self.data.component_storage.remove_entity(entity_uid)
+ {
+ tracing::error!("Failed to despawn entity: {err}");
}
}
Action::AddComponents(entity_uid, components) => {
- let added_component_ids = Self::add_entity_components(
+ Self::add_entity_components(
entity_uid,
components,
&mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
);
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
}
Action::RemoveComponents(entity_uid, component_ids) => {
- let removed_component_ids = Self::remove_entity_components(
+ Self::remove_entity_components(
entity_uid,
component_ids,
&mut self.data.component_storage,
);
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in removed_component_ids {
- self.emit_event_by_id::<ComponentRemovedEvent>(comp_id);
- }
}
Action::Stop => {
self.stop.store(true, Ordering::Relaxed);
@@ -442,85 +497,77 @@ impl World
entity_uid: Uid,
components: impl IntoIterator<Item = ComponentParts>,
component_storage: &mut ComponentStorage,
- ) -> Vec<Uid>
+ event_submitter: &EventSubmitter<'_>,
+ )
{
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();
+ let comp_name = component_parts.name();
+
if let Err(err) = component_storage.add_entity_component(
entity_uid,
- (comp_id, component_parts.name(), component_parts.into_data()),
+ (comp_id, comp_name, component_parts.into_data()),
) {
- tracing::error!("Failed to add component to entity: {err}");
+ tracing::error!("Failed to add component {comp_name} to entity: {err}");
continue;
}
- added_component_ids.push(comp_id);
- }
+ if comp_id.kind() == UidKind::Pair {
+ continue;
+ }
- added_component_ids
+ event_submitter.submit_event(
+ &Pair::builder()
+ .relation::<Added>()
+ .target_id(comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
}
fn remove_entity_components(
entity_uid: Uid,
component_ids: impl IntoIterator<Item = Uid>,
component_storage: &mut ComponentStorage,
- ) -> Vec<Uid>
+ )
{
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<Event: Component>(&self, target: Uid)
+ fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>)
{
- if target.kind() == UidKind::Pair {
- return;
- }
-
- let query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()])
- .build(),
+ assert_eq!(event_id.kind(), UidKind::Pair);
+
+ let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query(
+ self.flexible_query(
+ QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
+ .with_required([ObserverWrapperComponent::id(), event_id])
+ .build(),
+ ),
);
- for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) {
+ for (observer_ent_id, (observer,)) in query.iter_with_euids() {
unsafe {
- system.system.run(self);
+ observer.run(
+ self,
+ SystemMetadata { ent_id: observer_ent_id },
+ emitted_event.clone(),
+ );
}
}
}
-
- fn swap_event_queue(&self, has_swapped_active_queue: &mut bool)
- {
- let mut active_queue = self.data.action_queue.active_queue.borrow_mut();
-
- *active_queue = match *active_queue {
- ActiveActionQueue::A => ActiveActionQueue::B,
- ActiveActionQueue::B => ActiveActionQueue::A,
- };
-
- *has_swapped_active_queue = true;
- }
}
impl Default for World
@@ -541,31 +588,21 @@ pub enum StepResult
Stop,
}
-#[derive(Debug)]
-pub struct WorldData
+#[derive(Debug, Default)]
+struct WorldData
{
component_storage: ComponentStorage,
sole_storage: SoleStorage,
action_queue: Rc<ActionQueue>,
+ new_events: Lock<NewEvents>,
}
-impl Default for WorldData
-{
- fn default() -> Self
- {
- Self {
- component_storage: ComponentStorage::default(),
- sole_storage: SoleStorage::default(),
- action_queue: Rc::new(ActionQueue::default()),
- }
- }
-}
-
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct EntityComponentRef<'a>
{
component_id: Uid,
component: &'a ArchetypeEntityComponent,
+ entity_id: Uid,
}
impl<'a> EntityComponentRef<'a>
@@ -581,56 +618,37 @@ impl<'a> EntityComponentRef<'a>
self.component_id
}
- fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self
+ #[must_use]
+ pub fn entity_id(&self) -> Uid
{
- Self { component_id, component: comp }
+ self.entity_id
}
-}
-#[derive(Debug, Default, Clone, Copy)]
-enum ActiveActionQueue
-{
- #[default]
- A,
- B,
+ fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid)
+ -> Self
+ {
+ Self {
+ component_id,
+ component: comp,
+ entity_id,
+ }
+ }
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
struct ActionQueue
{
- queue_a: Lock<Vec<Action>>,
- queue_b: Lock<Vec<Action>>,
- active_queue: RefCell<ActiveActionQueue>,
+ queue: Lock<Vec<Action>>,
}
impl ActionQueue
{
fn push(&self, action: Action)
{
- match *self.active_queue.borrow() {
- ActiveActionQueue::A => self
- .queue_a
- .write_nonblock()
- .expect("Failed to aquire read-write action queue A lock")
- .push(action),
- ActiveActionQueue::B => self
- .queue_b
- .write_nonblock()
- .expect("Failed to aquire read-write action queue A lock")
- .push(action),
- }
- }
-}
-
-impl Default for ActionQueue
-{
- fn default() -> 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()),
- }
+ self.queue
+ .write_nonblock()
+ .expect("Failed to aquire read-write lock to action queue")
+ .push(action);
}
}
diff --git a/ecs/src/lock.rs b/ecs/src/lock.rs
index 0b36922..fe4e08b 100644
--- a/ecs/src/lock.rs
+++ b/ecs/src/lock.rs
@@ -1,3 +1,4 @@
+use std::any::type_name;
use std::mem::forget;
use std::ops::{Deref, DerefMut};
@@ -30,7 +31,7 @@ impl<Value> Lock<Value>
///
/// # Errors
/// Returns `Err` if unavailable (A mutable handle is hold).
- pub fn read_nonblock(&self) -> Result<ReadGuard<Value>, Error>
+ pub fn read_nonblock(&self) -> Result<ReadGuard<'_, Value>, Error>
{
let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?;
@@ -46,7 +47,7 @@ impl<Value> Lock<Value>
///
/// # Errors
/// Returns `Err` if unavailable (A mutable or immutable handle is hold).
- pub fn write_nonblock(&self) -> Result<WriteGuard<Value>, Error>
+ pub fn write_nonblock(&self) -> Result<WriteGuard<'_, Value>, Error>
{
let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?;
@@ -62,6 +63,14 @@ impl<Value> Lock<Value>
}
}
+impl<Value: Default + 'static> Default for Lock<Value>
+{
+ fn default() -> Self
+ {
+ Self::new(Value::default(), type_name::<Value>())
+ }
+}
+
#[derive(Debug, thiserror::Error)]
pub enum Error
{
@@ -81,21 +90,26 @@ pub struct ReadGuard<'guard, Value>
impl<'guard, Value> ReadGuard<'guard, Value>
{
- pub fn map<NewValue>(
- self,
- func: impl FnOnce(&Value) -> &NewValue,
- ) -> MappedReadGuard<'guard, NewValue>
+ pub fn try_map<NewValue>(
+ this: Self,
+ func: impl FnOnce(&Value) -> Option<&NewValue>,
+ ) -> Result<MappedReadGuard<'guard, NewValue>, Self>
{
- let value_type_name = self.value_type_name;
+ let value_type_name = this.value_type_name;
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
- let inner = unsafe { std::ptr::read(&self.inner) };
- forget(self);
-
- MappedReadGuard {
- inner: RwLockReadGuard::map(inner, func),
- value_type_name,
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+ forget(this);
+
+ match RwLockReadGuard::try_map(inner, func) {
+ Ok(mapped_guard) => {
+ Ok(MappedReadGuard { inner: mapped_guard, value_type_name })
+ }
+ Err(unmapped_guard) => Err(Self {
+ inner: unmapped_guard,
+ value_type_name,
+ }),
}
}
}
@@ -155,21 +169,26 @@ pub struct WriteGuard<'guard, Value>
impl<'guard, Value> WriteGuard<'guard, Value>
{
- pub fn map<NewValue>(
- self,
- func: impl FnOnce(&mut Value) -> &mut NewValue,
- ) -> MappedWriteGuard<'guard, NewValue>
+ pub fn try_map<NewValue>(
+ this: Self,
+ func: impl FnOnce(&mut Value) -> Option<&mut NewValue>,
+ ) -> Result<MappedWriteGuard<'guard, NewValue>, Self>
{
- let value_type_name = self.value_type_name;
+ let value_type_name = this.value_type_name;
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
- let inner = unsafe { std::ptr::read(&self.inner) };
- forget(self);
-
- MappedWriteGuard {
- inner: RwLockWriteGuard::map(inner, func),
- value_type_name,
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+ forget(this);
+
+ match RwLockWriteGuard::try_map(inner, func) {
+ Ok(mapped_guard) => {
+ Ok(MappedWriteGuard { inner: mapped_guard, value_type_name })
+ }
+ Err(unmapped_guard) => Err(Self {
+ inner: unmapped_guard,
+ value_type_name,
+ }),
}
}
}
diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs
index 2055d5e..b4bfa57 100644
--- a/ecs/src/pair.rs
+++ b/ecs/src/pair.rs
@@ -1,6 +1,14 @@
+use std::any::type_name;
use std::convert::Infallible;
+use std::marker::PhantomData;
-use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts};
+use crate::component::{
+ Handle as ComponentHandle,
+ HandleError as ComponentHandleError,
+ HandleMut as ComponentHandleMut,
+ IntoParts as IntoComponentParts,
+ Parts as ComponentParts,
+};
use crate::entity::{
Handle as EntityHandle,
MatchingComponentIter as EntityMatchingComponentIter,
@@ -10,24 +18,127 @@ use crate::query::{
TermsBuilder as QueryTermsBuilder,
TermsBuilderInterface,
};
-use crate::uid::{PairParams as UidPairParams, Uid, Wildcard, With as WithUid};
-use crate::{Component, World};
+use crate::uid::{Kind as UidKind, PairParams as UidPairParams, Uid, With as WithUid};
+use crate::util::impl_multiple;
+use crate::{Component, EntityComponentRef, World};
+/// Pair builder.
#[derive(Debug)]
-pub struct Pair<RelationElem: Element, TargetElem: Element>
+pub struct Builder<Relation, Target>
{
- relation: RelationElem,
- target: TargetElem,
+ relation: Relation,
+ target: Target,
}
-impl Pair<Uid, Uid>
+impl<Relation, Target> Builder<Relation, Target>
+{
+ pub fn relation<NewRelation: Component>(self) -> Builder<Uid, Target>
+ {
+ Builder {
+ relation: NewRelation::id(),
+ target: self.target,
+ }
+ }
+
+ pub fn relation_id(self, id: Uid) -> Builder<Uid, Target>
+ {
+ Builder { relation: id, target: self.target }
+ }
+
+ pub fn target<NewTarget: Component>(self) -> Builder<Relation, Uid>
+ {
+ Builder {
+ relation: self.relation,
+ target: NewTarget::id(),
+ }
+ }
+
+ pub fn target_id(self, id: Uid) -> Builder<Relation, Uid>
+ {
+ Builder { relation: self.relation, target: id }
+ }
+}
+
+impl_multiple!(
+ Builder,
+ (impl<Target> _<><Uid, Target>, impl<Target> _<><(), Target>)
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ pub fn target_as_data<NewTarget: Component>(
+ self,
+ data: NewTarget,
+ ) -> Builder<$ty_param_1, NewTarget>
+ {
+ Builder {
+ relation: self.relation,
+ target: data,
+ }
+ }
+ }
+);
+
+impl_multiple!(
+ Builder,
+ (impl<Relation> _<><Relation, Uid>, impl<Relation> _<><Relation, ()>)
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ pub fn relation_as_data<NewRelation: Component>(
+ self,
+ data: NewRelation,
+ ) -> Builder<NewRelation, $ty_param_2>
+ {
+ Builder {
+ relation: data,
+ target: self.target,
+ }
+ }
+ }
+);
+
+impl_multiple!(
+ Builder,
+ (
+ impl _<><Uid, Uid>,
+ impl<Relation: Component> _<><Relation, Uid>,
+ impl<Target: Component> _<><Uid, Target>,
+ impl<Relation: Component, Target: Component> _<><Relation, Target>
+ )
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ #[must_use]
+ pub fn build(self) -> Pair<$ty_param_1, $ty_param_2>
+ {
+ Pair {
+ relation: self.relation,
+ target: self.target
+ }
+ }
+ }
+);
+
+impl Default for Builder<(), ()>
+{
+ fn default() -> Self
+ {
+ Self { relation: (), target: () }
+ }
+}
+
+#[derive(Debug)]
+pub struct Pair<Relation, Target>
+{
+ relation: Relation,
+ target: Target,
+}
+
+impl Pair<(), ()>
{
#[must_use]
- pub fn new<Relation: WithUid>(target: Uid) -> Self
+ pub fn builder() -> Builder<(), ()>
{
- Self { relation: Relation::uid(), target }
+ Builder { relation: (), target: () }
}
+}
+impl Pair<Uid, Uid>
+{
#[must_use]
pub fn id(&self) -> Uid
{
@@ -46,12 +157,116 @@ impl IntoComponentParts for Pair<Uid, Uid>
}
}
-impl<Relation, Target> QueryTermWithField for Pair<Relation, Target>
+impl<Target> IntoComponentParts for Pair<Uid, Target>
where
- Relation: WithUid,
- Target: WithUid,
+ Target: Component,
{
- type Field<'a> = Handle<'a>;
+ fn into_parts(self) -> ComponentParts
+ {
+ let id = Uid::new_pair(&UidPairParams {
+ relation: self.relation,
+ target: Target::id(),
+ });
+
+ ComponentParts::builder()
+ .name("Pair")
+ .build(id, self.target)
+ }
+}
+
+impl<Relation> IntoComponentParts for Pair<Relation, Uid>
+where
+ Relation: Component,
+{
+ fn into_parts(self) -> ComponentParts
+ {
+ let id = Uid::new_pair(&UidPairParams {
+ relation: Relation::id(),
+ target: self.target,
+ });
+
+ ComponentParts::builder()
+ .name("Pair")
+ .build(id, self.relation)
+ }
+}
+
+impl<Relation, Target> QueryTermWithField for Pair<Relation, &Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Field<'a> = ComponentHandle<'a, Target>;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ terms_builder.with_required([Pair::<Relation, Target>::uid()]);
+ }
+
+ fn get_field<'world>(
+ entity_handle: &EntityHandle<'world>,
+ _world: &'world World,
+ ) -> Self::Field<'world>
+ {
+ let target_component = entity_handle
+ .get_matching_components(Pair::<Relation, Target>::uid())
+ .next()
+ .expect("Not possible");
+
+ Self::Field::from_entity_component_ref(&target_component).unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to target component {} failed: {err}",
+ type_name::<Target>()
+ );
+ })
+ }
+}
+
+impl<Relation, Target> QueryTermWithField for Pair<Relation, &mut Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Field<'a> = ComponentHandleMut<'a, Target>;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ )
+ {
+ terms_builder.with_required([Pair::<Relation, Target>::uid()]);
+ }
+
+ fn get_field<'world>(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ ) -> Self::Field<'world>
+ {
+ let target_component = entity_handle
+ .get_matching_components(Pair::<Relation, Target>::uid())
+ .next()
+ .expect("Not possible");
+
+ Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else(
+ |err| {
+ panic!(
+ "Creating handle to target component {} failed: {err}",
+ type_name::<Target>()
+ );
+ },
+ )
+ }
+}
+
+// TODO: implement QueryTermWithField for Pair<&Relation, Target> (or equivalent)
+// TODO: implement QueryTermWithField for Pair<&mut Relation, Target> (or equivalent)
+
+impl<Relation> QueryTermWithField for Pair<Relation, Wildcard>
+where
+ Relation: Component,
+{
+ type Field<'a> = WithWildcard<'a, Relation, Wildcard>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
@@ -70,38 +285,51 @@ where
.next()
.expect("Not possible");
- Handle {
+ WithWildcard {
world,
- pair_uid: first_matching_comp.id(),
+ component_ref: first_matching_comp,
+ _pd: PhantomData,
}
}
}
impl<Relation, Target> WithUid for Pair<Relation, Target>
where
- Relation: WithUid,
- Target: WithUid,
+ Relation: Component,
+ Target: Component,
{
fn uid() -> Uid
{
Uid::new_pair(&UidPairParams {
- relation: Relation::uid(),
- target: Target::uid(),
+ relation: Relation::id(),
+ target: Target::id(),
})
}
}
-impl<Relation> QueryTermWithField for &'static [Pair<Relation, Wildcard>]
+impl<Relation> WithUid for Pair<Relation, Wildcard>
where
- Relation: WithUid,
+ Relation: Component,
{
- type Field<'a> = HandleIter<'a>;
+ fn uid() -> Uid
+ {
+ Uid::new_pair(&UidPairParams {
+ relation: Relation::id(),
+ target: Wildcard::uid(),
+ })
+ }
+}
+
+impl<Relation> QueryTermWithField for &'_ [Pair<Relation, Wildcard>]
+where
+ Relation: Component,
+{
+ type Field<'a> = MultipleWithWildcard<'a, Relation, Wildcard>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ _terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
)
{
- terms_builder.with_required([Pair::<Relation, Wildcard>::uid()]);
}
fn get_field<'world>(
@@ -109,80 +337,304 @@ where
world: &'world World,
) -> Self::Field<'world>
{
- HandleIter {
- inner: entity_handle
- .get_matching_components(Pair::<Relation, Wildcard>::uid()),
+ MultipleWithWildcard {
+ entity_handle: entity_handle.clone(),
world,
+ _pd: PhantomData,
}
}
}
-pub struct Handle<'world>
+/// Reference to a pair with a wildcard relation/target.
+#[derive(Debug)]
+pub struct WithWildcard<'world, Relation, Target>
{
world: &'world World,
- pair_uid: Uid,
+ component_ref: EntityComponentRef<'world>,
+ _pd: PhantomData<(Relation, Target)>,
}
-impl Handle<'_>
+impl<'world, Relation, Target> WithWildcard<'world, Relation, Target>
{
+ /// Returns a new `WithWildcard`.
+ ///
+ /// # Panics
+ /// This function will panic if:
+ /// - The given component's ID is not a pair ID.
+ /// - `Relation::uid()` is not wildcard and does not equal to the relation of the
+ /// given component's ID
+ /// - `Target::uid()` is not wildcard and does not equal to the target of the given
+ /// component's ID
+ /// - Both `Relation::uid()` and `Target::uid()` are wildcards
+ /// - Neither `Relation::uid()` or `Target::uid()` are wildcards
+ pub fn new(world: &'world World, component_ref: EntityComponentRef<'world>) -> Self
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ let component_id = component_ref.id();
+
+ assert!(component_id.kind() == UidKind::Pair);
+
+ assert!(
+ Relation::uid() == Wildcard::uid()
+ || component_id.relation_component() == Relation::uid()
+ );
+
+ assert!(
+ Target::uid() == Wildcard::uid()
+ || component_id.target_component() == Target::uid()
+ );
+
+ assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+
+ assert!(
+ !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
+ );
+
+ WithWildcard {
+ world,
+ component_ref,
+ _pd: PhantomData,
+ }
+ }
+
+ /// Returns the [`Uid`] of the pair.
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.component_ref.id()
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the `Data`
+ /// type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is mutably borrowed elsewhere.
#[must_use]
- pub fn get_target_entity(&self) -> Option<EntityHandle<'_>>
+ pub fn get_data<Data>(&self) -> Option<ComponentHandle<'_, Data>>
+ where
+ Data: 'static,
+ {
+ ComponentHandle::<Data>::from_entity_component_ref(&self.component_ref)
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Data>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the `Data`
+ /// type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_mut<Data>(&self) -> Option<ComponentHandleMut<'_, Data>>
+ where
+ Data: 'static,
+ {
+ ComponentHandleMut::<Data>::from_entity_component_ref(
+ &self.component_ref,
+ self.world,
+ )
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Data>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+}
+
+impl<Relation> WithWildcard<'_, Relation, Wildcard>
+where
+ Relation: Component,
+{
+ /// Attempts to retrieve the target as a entity, returning `None` if not found.
+ #[must_use]
+ pub fn get_target_ent(&self) -> Option<EntityHandle<'_>>
{
let archetype = self
.world
.data
.component_storage
- .get_entity_archetype(self.pair_uid.target_entity())?;
+ .get_entity_archetype(self.component_ref.id().target_entity())?;
let Some(archetype_entity) =
- archetype.get_entity_by_id(self.pair_uid.target_entity())
+ archetype.get_entity_by_id(self.component_ref.id().target_entity())
else {
unreachable!();
};
- Some(EntityHandle::new(self.world, archetype, archetype_entity))
+ Some(EntityHandle::new(archetype, archetype_entity, self.world))
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the
+ /// `Relation` type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is mutably borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_as_relation(&self) -> Option<ComponentHandle<'_, Relation>>
+ {
+ ComponentHandle::<Relation>::from_entity_component_ref(&self.component_ref)
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Relation>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the
+ /// `Relation` type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_as_relation_mut(&self) -> Option<ComponentHandleMut<'_, Relation>>
+ {
+ ComponentHandleMut::<Relation>::from_entity_component_ref(
+ &self.component_ref,
+ self.world,
+ )
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Relation>()
+ );
+ }
+ },
+ Some,
+ )
}
}
-pub struct HandleIter<'a>
+/// Used to access matching pairs in a entity containing zero or more matching pairs.
+#[derive(Debug)]
+pub struct MultipleWithWildcard<'a, Relation, Target>
{
- inner: EntityMatchingComponentIter<'a>,
+ entity_handle: EntityHandle<'a>,
world: &'a World,
+ _pd: PhantomData<(Relation, Target)>,
}
-impl<'a> Iterator for HandleIter<'a>
+impl<'world, Relation, Target> MultipleWithWildcard<'world, Relation, Target>
{
- type Item = Handle<'a>;
-
- fn next(&mut self) -> Option<Self::Item>
+ /// Returns a new `MultipleWithWildcard`.
+ ///
+ /// # Panics
+ /// This function will panic if:
+ /// - Both `Relation::uid()` and `Target::uid()` are wildcards
+ /// - Neither `Relation::uid()` or `Target::uid()` are wildcards
+ pub fn new(world: &'world World, entity_handle: EntityHandle<'world>) -> Self
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
{
- let matching_comp = self.inner.next()?;
+ assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+
+ assert!(
+ !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
+ );
- Some(Handle {
+ MultipleWithWildcard {
+ entity_handle,
+ world,
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<'a, Relation: Component> MultipleWithWildcard<'a, Relation, Wildcard>
+{
+ #[must_use]
+ pub fn get_with_target_id(
+ &self,
+ target_id: Uid,
+ ) -> Option<WithWildcard<'a, Relation, Wildcard>>
+ {
+ Some(WithWildcard {
world: self.world,
- pair_uid: matching_comp.id(),
+ component_ref: self
+ .entity_handle
+ .get_matching_components(
+ Pair::builder()
+ .relation::<Relation>()
+ .target_id(target_id)
+ .build()
+ .id(),
+ )
+ .next()?,
+ _pd: PhantomData,
})
}
}
-pub trait Element: sealed::Sealed
+impl<'a, Relation: Component> IntoIterator
+ for MultipleWithWildcard<'a, Relation, Wildcard>
{
- type Value;
+ type IntoIter = WithWildcardIter<'a, Relation, Wildcard>;
+ type Item = <Self::IntoIter as Iterator>::Item;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ WithWildcardIter {
+ inner: self
+ .entity_handle
+ .get_matching_components(Pair::<Relation, Wildcard>::uid()),
+ world: self.world,
+ _pd: PhantomData,
+ }
+ }
}
-impl Element for Uid
+/// Iterator of matching pairs in a entity.
+pub struct WithWildcardIter<'a, Relation, Target>
{
- type Value = Uid;
+ inner: EntityMatchingComponentIter<'a>,
+ world: &'a World,
+ _pd: PhantomData<(Relation, Target)>,
}
-impl sealed::Sealed for Uid {}
-
-impl<WithUidT: WithUid> Element for WithUidT
+impl<'a, Relation, Target> Iterator for WithWildcardIter<'a, Relation, Target>
{
- type Value = Infallible;
-}
+ type Item = WithWildcard<'a, Relation, Target>;
-impl<WithUidT: WithUid> sealed::Sealed for WithUidT {}
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let matching_comp = self.inner.next()?;
+
+ Some(WithWildcard {
+ world: self.world,
+ component_ref: matching_comp,
+ _pd: PhantomData,
+ })
+ }
+}
/// Relation denoting a dependency to another entity
#[derive(Debug, Default, Clone, Copy, Component)]
@@ -192,6 +644,43 @@ pub struct DependsOn;
#[derive(Debug, Default, Clone, Copy, Component)]
pub struct ChildOf;
+#[derive(Debug)]
+pub struct Wildcard(Infallible);
+
+impl Wildcard
+{
+ #[must_use]
+ pub fn uid() -> Uid
+ {
+ Uid::wildcard()
+ }
+}
+
+pub trait ComponentOrWildcard: sealed::Sealed
+{
+ fn uid() -> Uid;
+}
+
+impl<ComponentT: Component> ComponentOrWildcard for ComponentT
+{
+ fn uid() -> Uid
+ {
+ ComponentT::id()
+ }
+}
+
+impl<ComponentT: Component> sealed::Sealed for ComponentT {}
+
+impl ComponentOrWildcard for Wildcard
+{
+ fn uid() -> Uid
+ {
+ Wildcard::uid()
+ }
+}
+
+impl sealed::Sealed for Wildcard {}
+
mod sealed
{
pub trait Sealed {}
diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs
index 9f47fb8..39f2a08 100644
--- a/ecs/src/phase.rs
+++ b/ecs/src/phase.rs
@@ -1,13 +1,19 @@
use ecs_macros::Component;
-use crate::pair::{ChildOf, Pair};
-use crate::static_entity;
+use crate::{declare_entity, World};
#[derive(Debug, Default, Clone, Copy, Component)]
pub struct Phase;
-static_entity!(pub START, (Phase,));
+declare_entity!(pub START, (Phase,));
+declare_entity!(pub PRE_UPDATE, (Phase,));
+declare_entity!(pub UPDATE, (Phase,));
+declare_entity!(pub POST_UPDATE, (Phase,));
-static_entity!(pub PRE_UPDATE, (Phase,));
-
-static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE)));
+pub(crate) fn spawn_entities(world: &mut World)
+{
+ world.create_declared_entity(&START);
+ world.create_declared_entity(&PRE_UPDATE);
+ world.create_declared_entity(&UPDATE);
+ world.create_declared_entity(&POST_UPDATE);
+}
diff --git a/ecs/src/private.rs b/ecs/src/private.rs
deleted file mode 100644
index 56a6552..0000000
--- a/ecs/src/private.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[doc(hidden)]
-pub use {linkme, paste};
diff --git a/ecs/src/query.rs b/ecs/src/query.rs
index 7e10c5b..5f13579 100644
--- a/ecs/src/query.rs
+++ b/ecs/src/query.rs
@@ -6,12 +6,11 @@ use seq_macro::seq;
use crate::component::{
Component,
Handle as ComponentHandle,
- HandleFromEntityComponentRef,
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::system::{Metadata as SystemMetadata, Param as SystemParam};
use crate::uid::{Kind as UidKind, Uid, With as WithUid};
use crate::util::array_vec::ArrayVec;
use crate::util::Array;
@@ -20,15 +19,16 @@ use crate::World;
pub mod flexible;
pub mod term;
+// A term tuple type can have a maximum of 17 elements
+pub const MAX_TERM_CNT: usize = 17;
+
#[derive(Debug)]
pub struct Query<'world, FieldTerms, FieldlessTerms = ()>
where
FieldTerms: TermWithFieldTuple,
FieldlessTerms: TermWithoutFieldTuple,
{
- world: &'world World,
- // A term tuple type can have a maximum of 17 elements
- inner: FlexibleQuery<'world, 17>,
+ inner: FlexibleQuery<'world, MAX_TERM_CNT>,
_pd: PhantomData<(FieldTerms, FieldlessTerms)>,
}
@@ -47,8 +47,8 @@ where
tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
Iter {
- world: self.world,
- iter: self.inner.iter(),
+ world: self.inner.world(),
+ inner: self.inner.iter(),
comps_pd: PhantomData,
}
}
@@ -63,7 +63,7 @@ where
tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
ComponentAndEuidIter {
- world: self.world,
+ world: self.inner.world(),
iter: self.inner.iter(),
comps_pd: PhantomData,
}
@@ -85,8 +85,8 @@ where
tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
Iter {
- world: self.world,
- iter: func(self.inner.iter()),
+ world: self.inner.world(),
+ inner: func(self.inner.iter()),
comps_pd: PhantomData,
}
}
@@ -98,6 +98,25 @@ where
Some(self.inner.iter().nth(entity_index)?.uid())
}
+ /// Returns a new `Query` created from a [`FlexibleQuery`].
+ ///
+ /// # Important notes
+ /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms
+ /// in the given [`FlexibleQuery`], otherwise any method call or iterating might
+ /// panic.
+ #[must_use]
+ pub fn from_flexible_query(
+ flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>,
+ ) -> Self
+ {
+ // TODO: Check compatability of terms
+
+ Self {
+ inner: flexible_query,
+ _pd: PhantomData,
+ }
+ }
+
pub(crate) fn new(world: &'world World) -> Self
{
let mut terms_builder = Terms::builder();
@@ -106,7 +125,6 @@ where
FieldlessTerms::apply_terms_to_builder(&mut terms_builder);
Self {
- world,
inner: world.flexible_query(terms_builder.build()),
_pd: PhantomData,
}
@@ -136,17 +154,7 @@ where
{
type Input = ();
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
{
Self::new(world)
}
@@ -342,18 +350,26 @@ impl<ComponentT: Component> TermWithField for &ComponentT
fn get_field<'world>(
entity_handle: &EntityHandle<'world>,
- world: &'world World,
+ _world: &'world World,
) -> Self::Field<'world>
{
assert_eq!(ComponentT::id().kind(), UidKind::Component);
- Self::Field::from_entity_component_ref(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next(),
- world,
- )
- .unwrap_or_else(|err| {
+ 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>()
@@ -380,13 +396,21 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT
{
assert_eq!(ComponentT::id().kind(), UidKind::Component);
- Self::Field::from_entity_component_ref(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next(),
- world,
- )
- .unwrap_or_else(|err| {
+ let Some(component) = entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()
+ else {
+ panic!(
+ concat!(
+ "Component {} was not found in entity {}. There ",
+ "is most likely a bug in the entity querying"
+ ),
+ type_name::<ComponentT>(),
+ entity_handle.uid()
+ );
+ };
+
+ Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| {
panic!(
"Creating handle to component {} failed: {err}",
type_name::<ComponentT>()
@@ -422,29 +446,10 @@ where
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
{
world: &'world World,
- iter: EntityHandleIter,
+ inner: EntityHandleIter,
comps_pd: PhantomData<FieldTerms>,
}
-impl<'query, 'world, FieldTerms, EntityHandleIter>
- Iter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
-{
- /// Creates a new iterator from the given entity handle iterator.
- ///
- /// # Important
- /// All of the yielded entities of the entity handle iterator should match the
- /// terms `Terms`. The [`Self::next`] function will panic if it encounters a
- /// entity that does not match the terms `Terms`.
- pub fn new(world: &'world World, iter: EntityHandleIter) -> Self
- {
- Self { world, iter, comps_pd: PhantomData }
- }
-}
-
impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
for Iter<'query, 'world, FieldTerms, EntityHandleIter>
where
@@ -456,7 +461,7 @@ where
fn next(&mut self) -> Option<Self::Item>
{
- let entity_handle = self.iter.next()?;
+ let entity_handle = self.inner.next()?;
Some(FieldTerms::get_fields(&entity_handle, self.world))
}
diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs
index 6d65ee0..936ab82 100644
--- a/ecs/src/query/flexible.rs
+++ b/ecs/src/query/flexible.rs
@@ -22,7 +22,6 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
pub fn iter(&self) -> Iter<'_>
{
Iter {
- world: self.world,
iter: self
.world
.data
@@ -37,9 +36,16 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
.zip(archetype.entities())
}) as ComponentIterMapFn,
),
+ world: self.world,
}
}
+ #[must_use]
+ pub fn world(&self) -> &'world World
+ {
+ self.world
+ }
+
pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self
{
Self { world, terms }
@@ -59,8 +65,8 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T
pub struct Iter<'query>
{
- world: &'query World,
iter: QueryEntityIter<'query>,
+ world: &'query World,
}
impl<'query> Iterator for Iter<'query>
@@ -71,7 +77,7 @@ impl<'query> Iterator for Iter<'query>
{
let (archetype, entity) = self.iter.next()?;
- Some(EntityHandle::new(self.world, archetype, entity))
+ Some(EntityHandle::new(archetype, entity, self.world))
}
}
diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs
index 2e1ecca..0683918 100644
--- a/ecs/src/query/term.rs
+++ b/ecs/src/query/term.rs
@@ -4,7 +4,6 @@ use std::marker::PhantomData;
use crate::component::{
Component,
Handle as ComponentHandle,
- HandleFromEntityComponentRef,
HandleMut as ComponentHandleMut,
};
use crate::query::{
@@ -65,17 +64,14 @@ impl<ComponentT: Component> TermWithField for Option<&ComponentT>
fn get_field<'world>(
entity_handle: &crate::entity::Handle<'world>,
- world: &'world crate::World,
+ _world: &'world crate::World,
) -> Self::Field<'world>
{
Some(
ComponentHandle::<'world, ComponentT>::from_entity_component_ref(
- Some(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- ),
- world,
+ &entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?,
)
.unwrap_or_else(|err| {
panic!(
@@ -104,11 +100,9 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>
{
Some(
ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref(
- Some(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- ),
+ &entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?,
world,
)
.unwrap_or_else(|err| {
diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs
index 1cce419..7cfcc24 100644
--- a/ecs/src/sole.rs
+++ b/ecs/src/sole.rs
@@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Weak};
use crate::lock::{Lock, WriteGuard};
-use crate::system::{Param as SystemParam, System};
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
use crate::World;
/// A type which has a single instance and is shared globally.
@@ -64,7 +64,7 @@ where
}
}
- fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self
+ pub(crate) fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self
{
Self {
sole: sole.write_nonblock().unwrap_or_else(|_| {
@@ -85,17 +85,7 @@ where
{
type Input = ();
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
{
let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| {
panic!("Sole {} was not found in world", type_name::<SoleT>())
diff --git a/ecs/src/system.rs b/ecs/src/system.rs
index 603c015..95ab7a8 100644
--- a/ecs/src/system.rs
+++ b/ecs/src/system.rs
@@ -1,37 +1,28 @@
-use std::any::Any;
-use std::convert::Infallible;
use std::fmt::Debug;
use ecs_macros::Component;
use seq_macro::seq;
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::tuple::{ReduceElement as TupleReduceElement, Tuple};
+use crate::uid::Uid;
use crate::World;
+pub mod initializable;
+pub mod observer;
pub mod stateful;
-pub trait System<'world, Impl>: 'static
+/// Metadata of a system.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Metadata
{
- type Input;
-
- #[must_use]
- fn initialize(self, input: Self::Input) -> Self;
-
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world;
-
- fn into_type_erased(self) -> TypeErased;
+ pub ent_id: Uid,
+}
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>;
+pub trait System<'world, Impl>: 'static
+{
+ type Callbacks: Callbacks;
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- local_component: LocalComponent,
- );
+ fn finish(self) -> (TypeErased, Self::Callbacks);
}
macro_rules! impl_system {
@@ -43,58 +34,23 @@ macro_rules! impl_system {
Func: Fn(#(TParam~I,)*) + Copy + 'static,
#(TParam~I: Param<'world, Input = ()>,)*
{
- type Input = Infallible;
-
- fn initialize(self, _input: Self::Input) -> Self
- {
- self
- }
+ type Callbacks = NoCallbacks;
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world
+ fn finish(self) -> (TypeErased, Self::Callbacks)
{
- let func = *self;
-
- func(#({
- TParam~I::new(self, world)
- },)*);
- }
-
- fn into_type_erased(self) -> TypeErased
- {
- TypeErased {
- data: Box::new(self),
- run: Box::new(|data, world| {
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let data = unsafe { &*std::ptr::from_ref(data) };
-
- let me = data
- .downcast_ref::<Func>()
- .expect("Function downcast failed");
-
+ let type_erased = TypeErased {
+ run: Box::new(move |world, metadata| {
// SAFETY: The caller of TypeErased::run ensures the lifetime
// is correct
let world = unsafe { &*std::ptr::from_ref(world) };
- me.run(world);
+ self(#({
+ TParam~I::new(world, &metadata)
+ },)*);
}),
- }
- }
+ };
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>
- {
- panic!("System does not have any local components");
- }
-
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- _local_component: LocalComponent,
- ) {
- panic!("System does not have any local components");
+ (type_erased, NoCallbacks)
}
}
});
@@ -105,7 +61,7 @@ seq!(C in 1..16 {
impl_system!(C);
});
-pub trait Into<Impl>
+pub trait Into<'world, Impl>
{
type System;
@@ -114,7 +70,6 @@ pub trait Into<Impl>
pub struct TypeErased
{
- data: Box<dyn Any>,
run: Box<TypeErasedRunFn>,
}
@@ -124,12 +79,9 @@ impl TypeErased
///
/// # Safety
/// `world_data` must live at least as long as the [`World`] the system belongs to.
- pub unsafe fn run(&self, world: &World)
+ pub unsafe fn run(&self, world: &World, metadata: Metadata)
{
- // You have to dereference for downcasting to work for some reason
- let data = &*self.data;
-
- (self.run)(data, world);
+ (self.run)(world, metadata);
}
}
@@ -142,41 +94,29 @@ impl Debug for TypeErased
}
/// Function in [`TypeErased`] used to run the system.
-type TypeErasedRunFn = dyn Fn(&dyn Any, &World);
+type TypeErasedRunFn = dyn Fn(&World, Metadata);
/// A parameter to a [`System`].
pub trait Param<'world>
{
type Input;
- fn initialize<SystemImpl>(
- system: &mut impl System<'world, SystemImpl>,
- input: Self::Input,
- );
-
- fn new<SystemImpl>(
- system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self;
+ fn new(world: &'world World, system_metadata: &Metadata) -> Self;
}
/// A type which can be used as input to a [`System`].
pub trait Input: 'static {}
-/// Component tuple reducing operation to get the parameters that takes input.
-pub struct ParamWithInputFilter;
-
-impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter>
- for InputT
-where
- Accumulator: Tuple,
+pub trait Callbacks
{
- type Return = Accumulator::WithElementAtEnd<Self>;
+ fn on_created(&mut self, world: &mut World, metadata: Metadata);
}
-impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for ()
+pub struct NoCallbacks;
+
+impl Callbacks for NoCallbacks
{
- type Return = Accumulator;
+ fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {}
}
#[derive(Debug, Component)]
diff --git a/ecs/src/system/initializable.rs b/ecs/src/system/initializable.rs
new file mode 100644
index 0000000..b6ec8e8
--- /dev/null
+++ b/ecs/src/system/initializable.rs
@@ -0,0 +1,131 @@
+use std::marker::PhantomData;
+
+use seq_macro::seq;
+
+use crate::system::{Input, Param as SystemParam, System};
+use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple};
+
+/// A initializable system.
+pub trait Initializable<'world, Impl>: System<'world, Impl>
+{
+ type Inputs;
+
+ #[must_use]
+ fn initialize(self, inputs: Self::Inputs) -> Self;
+}
+
+pub trait Param<'world, SystemT>: SystemParam<'world>
+{
+ fn initialize(system: &mut SystemT, input: Self::Input);
+}
+
+pub struct ParamTupleFilter<'world, SystemT>
+{
+ _pd: PhantomData<(&'world (), SystemT)>,
+}
+
+impl<'world, SystemT, ParamT, Accumulator>
+ TupleReduceElement<Accumulator, ParamTupleFilter<'world, SystemT>> for ParamT
+where
+ ParamT: SystemParam<
+ 'world,
+ Input: AppendInitializableParam<'world, Accumulator, ParamT, SystemT>,
+ >,
+ Accumulator: Tuple,
+{
+ type Return = <ParamT::Input as AppendInitializableParam<
+ 'world,
+ Accumulator,
+ ParamT,
+ SystemT,
+ >>::Return;
+}
+
+pub trait AppendInitializableParam<'world, Accumulator, ParamT, SystemT>
+{
+ type Return;
+}
+
+impl<'world, InputT, ParamT, Accumulator, SystemT>
+ AppendInitializableParam<'world, Accumulator, ParamT, SystemT> for InputT
+where
+ InputT: Input,
+ Accumulator: Tuple,
+ ParamT: Param<'world, SystemT>,
+{
+ type Return = Accumulator::WithElementAtEnd<ParamT>;
+}
+
+impl<ParamT, Accumulator, SystemT>
+ AppendInitializableParam<'_, Accumulator, ParamT, SystemT> for ()
+where
+ Accumulator: Tuple,
+{
+ type Return = Accumulator;
+}
+
+pub trait ParamTuple<'world, SystemT>
+{
+ type Inputs;
+
+ fn initialize_all(system: &mut SystemT, inputs: Self::Inputs);
+}
+
+macro_rules! impl_initializable_param_tuple {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, SystemT, #(Param~I,)*> ParamTuple<'world, SystemT>
+ for (#(Param~I,)*)
+ where
+ #(Param~I: Param<'world, SystemT>,)*
+ {
+ type Inputs = (#(Param~I::Input,)*);
+
+ fn initialize_all(
+ system: &mut SystemT,
+ inputs: Self::Inputs,
+ ) {
+ #(
+ <Param~I as Param<'world, SystemT>>::initialize(
+ system,
+ inputs.I
+ );
+ )*
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 1..16 {
+ impl_initializable_param_tuple!(C);
+});
+
+impl<SystemT> ParamTuple<'_, SystemT> for ()
+{
+ type Inputs = ();
+
+ fn initialize_all(_system: &mut SystemT, _inputs: Self::Inputs) {}
+}
+
+/// A tuple of system parameters that may or may not be initializable.
+pub trait MaybeInitializableParamTuple<'world, SystemT>
+{
+ /// A tuple of the inputs of the initializable system parameters in this tuple.
+ type Inputs;
+
+ fn init_initializable(system: &mut SystemT, inputs: Self::Inputs);
+}
+
+impl<'world, SystemT, Params> MaybeInitializableParamTuple<'world, SystemT> for Params
+where
+ Params:
+ TupleReduce<ParamTupleFilter<'world, SystemT>, Out: ParamTuple<'world, SystemT>>,
+{
+ type Inputs = <Params::Out as ParamTuple<'world, SystemT>>::Inputs;
+
+ fn init_initializable(system: &mut SystemT, inputs: Self::Inputs)
+ {
+ Params::Out::initialize_all(system, inputs);
+ }
+}
diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs
new file mode 100644
index 0000000..236420c
--- /dev/null
+++ b/ecs/src/system/observer.rs
@@ -0,0 +1,310 @@
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::mem::transmute;
+use std::slice::Iter as SliceIter;
+
+use ecs_macros::Component;
+use seq_macro::seq;
+
+use crate::component::Component;
+use crate::entity::Handle as EntityHandle;
+use crate::event::Emitted as EmittedEvent;
+use crate::pair::Pair;
+use crate::system::{
+ Metadata,
+ NoCallbacks,
+ Param,
+ System,
+ TypeErased as TypeErasedSystem,
+};
+use crate::uid::Uid;
+use crate::util::Array;
+use crate::World;
+
+pub trait Observed
+{
+ type Events: Array<Pair<Uid, Uid>>;
+
+ fn events() -> Self::Events;
+}
+
+impl<Relation, Target> Observed for Pair<Relation, Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Events = [Pair<Uid, Uid>; 1];
+
+ fn events() -> Self::Events
+ {
+ [Pair::builder()
+ .relation::<Relation>()
+ .target::<Target>()
+ .build()]
+ }
+}
+
+/// Observer system.
+pub trait Observer<'world, Impl>: System<'world, Impl>
+{
+ type ObservedEvents: Array<Pair<Uid, Uid>>;
+
+ fn observed_events() -> Self::ObservedEvents;
+
+ fn finish_observer(self) -> (WrapperComponent, Self::Callbacks);
+}
+
+pub struct Observe<'world, ObservedT: Observed>
+{
+ _pd: PhantomData<ObservedT>,
+ world: &'world World,
+ emitted_event: EmittedEvent<'world>,
+}
+
+impl<'world, ObservedT: Observed> Observe<'world, ObservedT>
+{
+ pub fn new(world: &'world World, emitted_event: EmittedEvent<'world>) -> Self
+ {
+ Self {
+ _pd: PhantomData,
+ world,
+ emitted_event,
+ }
+ }
+
+ #[must_use]
+ pub fn event(&self) -> Uid
+ {
+ self.emitted_event.event
+ }
+}
+
+impl<ObservedT: Observed> Observe<'_, ObservedT>
+{
+ #[must_use]
+ pub fn iter(&self) -> ObserveIter<'_, ObservedT>
+ {
+ ObserveIter {
+ world: self.world,
+ inner: self.emitted_event.match_ids.iter(),
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT>
+{
+ type IntoIter = ObserveIter<'a, ObservedT>;
+ type Item = <Self::IntoIter as Iterator>::Item;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ self.iter()
+ }
+}
+
+pub struct ObserveIter<'observe, ObservedT: Observed>
+{
+ world: &'observe World,
+ inner: SliceIter<'observe, Uid>,
+ _pd: PhantomData<ObservedT>,
+}
+
+impl<'observe, ObservedT: Observed> Iterator for ObserveIter<'observe, ObservedT>
+{
+ type Item = EventMatch<'observe, ObservedT>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let match_id = *self.inner.next()?;
+
+ Some(EventMatch {
+ world: self.world,
+ id: match_id,
+ _pd: PhantomData,
+ })
+ }
+}
+
+/// A event match.
+#[derive(Debug)]
+pub struct EventMatch<'world, ObservedT: Observed>
+{
+ world: &'world World,
+ id: Uid,
+ _pd: PhantomData<ObservedT>,
+}
+
+impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT>
+{
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.id
+ }
+
+ /// Attempts to get the entity with the id of this match.
+ #[must_use]
+ pub fn get_entity(&self) -> Option<EntityHandle<'world>>
+ {
+ self.world.get_entity(self.id)
+ }
+}
+
+macro_rules! impl_observer {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, ObservedT, Func, #(TParam~I,)*> System<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*)
+ > for Func
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world, Input = ()>,)*
+ {
+ type Callbacks = NoCallbacks;
+
+ fn finish(self) -> (TypeErasedSystem, NoCallbacks)
+ {
+ unimplemented!();
+ }
+ }
+
+ impl<'world, ObservedT, Func, #(TParam~I,)*> Observer<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*)
+ > for Func
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world, Input = ()>,)*
+ {
+ type ObservedEvents = ObservedT::Events;
+
+ fn observed_events() -> Self::ObservedEvents
+ {
+ ObservedT::events()
+ }
+
+ fn finish_observer(self) -> (WrapperComponent, NoCallbacks)
+ {
+ let wrapper_comp = WrapperComponent {
+ run: Box::new(move |world, metadata, emitted_event| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let emitted_event = unsafe {
+ transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(
+ emitted_event
+ )
+ };
+
+ self(Observe::new(world, emitted_event), #({
+ TParam~I::new(world, &metadata)
+ },)*);
+ }),
+ };
+
+ (wrapper_comp, NoCallbacks)
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 1..16 {
+ impl_observer!(C);
+});
+
+impl<'world, ObservedT, Func> System<'world, fn(Observe<'world, ObservedT>)> for Func
+where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>) + Copy + 'static,
+{
+ type Callbacks = NoCallbacks;
+
+ fn finish(self) -> (TypeErasedSystem, NoCallbacks)
+ {
+ const {
+ panic!("Observers cannot be used as regular systems");
+ }
+ }
+}
+
+impl<'world, ObservedT, Func> Observer<'world, fn(Observe<'world, ObservedT>)> for Func
+where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>) + Copy + 'static,
+{
+ type ObservedEvents = ObservedT::Events;
+
+ fn observed_events() -> Self::ObservedEvents
+ {
+ ObservedT::events()
+ }
+
+ fn finish_observer(self) -> (WrapperComponent, NoCallbacks)
+ {
+ let wrapper_comp = WrapperComponent {
+ run: Box::new(move |world, _metadata, emitted_event| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let emitted_event = unsafe {
+ transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(emitted_event)
+ };
+
+ self(Observe::new(world, emitted_event));
+ }),
+ };
+
+ (wrapper_comp, NoCallbacks)
+ }
+}
+
+#[derive(Component)]
+pub struct WrapperComponent
+{
+ run: Box<RunFn>,
+}
+
+impl WrapperComponent
+{
+ pub fn new(run: impl Fn(&World, Metadata, EmittedEvent<'_>) + 'static) -> Self
+ {
+ Self { run: Box::new(run) }
+ }
+
+ /// Runs the observer system.
+ ///
+ /// # Safety
+ /// `world` must live at least as long as the [`World`] the system belongs to.
+ pub unsafe fn run(
+ &self,
+ world: &World,
+ metadata: Metadata,
+ emitted_event: EmittedEvent<'_>,
+ )
+ {
+ (self.run)(world, metadata, emitted_event);
+ }
+}
+
+impl Debug for WrapperComponent
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_struct("WrapperComponent")
+ .finish_non_exhaustive()
+ }
+}
+
+type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>);
diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs
index 54f6979..e74ef31 100644
--- a/ecs/src/system/stateful.rs
+++ b/ecs/src/system/stateful.rs
@@ -1,152 +1,243 @@
-use std::any::{type_name, Any, TypeId};
+use std::mem::transmute;
use std::panic::{RefUnwindSafe, UnwindSafe};
-use hashbrown::HashMap;
use seq_macro::seq;
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::lock::Lock;
-use crate::system::{
- Into as IntoSystem,
- Param,
- ParamWithInputFilter,
- System,
- TypeErased,
+use crate::component::local::SystemWithLocalComponents;
+use crate::component::Parts as ComponentParts;
+use crate::event::Emitted as EmittedEvent;
+use crate::system::initializable::{Initializable, MaybeInitializableParamTuple};
+use crate::system::observer::{
+ Observe,
+ Observed,
+ Observer,
+ WrapperComponent as ObserverWrapperComponent,
};
-use crate::tuple::{
- Reduce as TupleReduce,
- Tuple,
- WithAllElemLtStatic as TupleWithAllElemLtStatic,
-};
-use crate::uid::Uid;
+use crate::system::{Into as IntoSystem, Metadata, Param, System, TypeErased};
use crate::World;
/// A stateful system.
pub struct Stateful<Func>
{
func: Func,
- local_components: HashMap<Uid, Lock<Box<dyn Any>>>,
+ local_components: Vec<ComponentParts>,
}
macro_rules! impl_system {
($c: tt) => {
seq!(I in 0..$c {
- impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)>
- for Stateful<Func>
+ impl<'world, Func, #(TParam~I,)*>
+ System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func>
where
Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static,
- #(TParam~I: Param<'world>,)*
- #(TParam~I::Input: 'static,)*
- (#(TParam~I::Input,)*): TupleReduce<
- ParamWithInputFilter,
- Out: Tuple<InOptions: TupleWithAllElemLtStatic>
- >,
+ #(TParam~I: Param<'world, Input: 'static>,)*
{
- type Input =
- <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out;
+ type Callbacks = Callbacks;
- fn initialize(mut self, input: Self::Input) -> Self
+ fn finish(self) -> (TypeErased, Self::Callbacks)
{
- let mut option_input = input.into_in_options();
-
- let mut index = 0;
-
- #(
- if TypeId::of::<TParam~I::Input>() !=
- TypeId::of::<()>()
- {
- let input = option_input
- .get_mut::<Option<TParam~I::Input>>(index)
- .expect("Input element index out of range")
- .take()
- .expect("Input element is already taken");
-
- TParam~I::initialize(
- &mut self,
- input
- );
-
- #[allow(unused_assignments)]
- {
- index += 1;
- }
- }
- )*
+ let Self { func, local_components } = self;
- self
+ let callbacks = Callbacks { local_components };
+
+ let type_erased = TypeErased {
+ run: Box::new(move |world, metadata| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ func(#({
+ TParam~I::new(&world, &metadata)
+ },)*);
+ }),
+ };
+
+
+ (type_erased, callbacks)
}
+ }
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world
+ impl<'world, Func, #(TParam~I,)*>
+ Initializable<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func>
+ where
+ Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static,
+ #(TParam~I: Param<'world, Input: 'static>,)*
+ (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self>
+ {
+ type Inputs = <
+ (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self>
+ >::Inputs;
+
+ fn initialize(mut self, inputs: Self::Inputs) -> Self
{
- let func = self.func;
+ init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs);
- func(#({
- TParam~I::new(self, &world)
- },)*);
+ self
}
+ }
- fn into_type_erased(self) -> TypeErased
+ impl<'world, Func, #(TParam~I,)*> IntoSystem<'world, fn(#(TParam~I,)*)>
+ for Func
+ where
+ #(TParam~I: Param<'world>,)*
+ Func: Fn(#(TParam~I,)*) + Copy + 'static,
+ {
+ type System = Stateful<Func>;
+
+ fn into_system(self) -> Self::System
{
- TypeErased {
- data: Box::new(self),
- run: Box::new(|data, world| {
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let data = unsafe { &*std::ptr::from_ref::<dyn Any>(data) };
+ Self::System {
+ func: self,
+ local_components: Vec::new(), // TODO: Use Vec::with_capacity
+ }
+ }
+ }
+ });
+ };
+}
- let me = data.downcast_ref::<Self>().unwrap();
+seq!(C in 1..16 {
+ impl_system!(C);
+});
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let world = unsafe { &*std::ptr::from_ref(world) };
+impl<Func> SystemWithLocalComponents for Stateful<Func>
+{
+ fn add_local_component(&mut self, component_parts: ComponentParts)
+ {
+ self.local_components.push(component_parts);
+ }
+}
- me.run(world);
- }),
- }
+#[derive(Debug)]
+pub struct Callbacks
+{
+ local_components: Vec<ComponentParts>,
+}
+
+impl crate::system::Callbacks for Callbacks
+{
+ fn on_created(&mut self, world: &mut World, metadata: Metadata)
+ {
+ for local_comp_parts in self.local_components.drain(..) {
+ world.add_component(metadata.ent_id, local_comp_parts);
+ }
+ }
+}
+
+fn init_initializable_params<'world, SystemT, Params>(
+ system: &mut SystemT,
+ inputs: Params::Inputs,
+) where
+ Params: MaybeInitializableParamTuple<'world, SystemT>,
+{
+ Params::init_initializable(system, inputs);
+}
+
+macro_rules! impl_observer {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, ObservedT, Func, #(TParam~I,)*> System<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*)
+ > for Stateful<Func>
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world>,)*
+ {
+ type Callbacks = Callbacks;
+
+ fn finish(self) -> (TypeErased, Callbacks)
+ {
+ unimplemented!();
}
+ }
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>
+ impl<'world, ObservedT, Func, #(TParam~I,)*> Initializable<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*)
+ > for Stateful<Func>
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world>,)*
+ (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self>
+ {
+ type Inputs = <
+ (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self>
+ >::Inputs;
+
+ fn initialize(mut self, inputs: Self::Inputs) -> Self
{
- let local_component = self.local_components
- .get(&LocalComponent::id())?
- .write_nonblock()
- .expect("Failed to aquire read-write local component lock");
+ init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs);
- Some(ComponentHandleMut::new(local_component))
+ self
}
+ }
+
+ impl<'world, ObservedT, Func, #(TParam~I,)*> Observer<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*)
+ > for Stateful<Func>
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world>,)*
+ {
+ type ObservedEvents = ObservedT::Events;
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- local_component: LocalComponent,
- )
+ fn observed_events() -> Self::ObservedEvents
{
- self.local_components
- .insert(
- LocalComponent::id(),
- Lock::new(
- Box::new(local_component),
- type_name::<LocalComponent>()
- )
- );
+ ObservedT::events()
+ }
+
+ fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks)
+ {
+ let Self { func, local_components } = self;
+
+ let callbacks = Callbacks { local_components };
+
+ let wrapper_comp = ObserverWrapperComponent::new(
+ move |world, metadata, emitted_event| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let emitted_event = unsafe {
+ transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(
+ emitted_event
+ )
+ };
+
+ func(Observe::new(world, emitted_event), #({
+ TParam~I::new(world, &metadata)
+ },)*);
+ },
+ );
+
+ (wrapper_comp, callbacks)
}
}
- impl<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)>
- for Func
+ impl<'world, Func, ObservedT, #(TParam~I,)*> IntoSystem<
+ 'world,
+ fn(Observe<'world, ObservedT>,
+ #(TParam~I,)*)
+ > for Func
where
- Func: Fn(#(TParam~I,)*) + Copy + 'static,
+ ObservedT: Observed,
+ #(TParam~I: Param<'world>,)*
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,
{
type System = Stateful<Func>;
- fn into_system(self) -> Self::System
+ fn into_system(self) -> Stateful<Func>
{
- Self::System {
+ Stateful {
func: self,
- local_components: HashMap::new(),
+ local_components: Vec::new(), // TODO: Use Vec::with_capacity
}
}
}
@@ -155,5 +246,5 @@ macro_rules! impl_system {
}
seq!(C in 1..16 {
- impl_system!(C);
+ impl_observer!(C);
});
diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs
index feed62c..bb393a1 100644
--- a/ecs/src/uid.rs
+++ b/ecs/src/uid.rs
@@ -2,8 +2,10 @@ use std::fmt::{Debug, Display, Formatter};
use std::mem::transmute;
use std::sync::atomic::{AtomicU32, Ordering};
+use seq_macro::seq;
+
use crate::component::Component;
-use crate::util::{gen_mask_64, BitMask, NumberExt};
+use crate::util::{gen_mask_64, Array, BitMask, NumberExt};
static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID);
@@ -218,7 +220,7 @@ pub struct PairParams
pub target: Uid,
}
-pub trait With: 'static
+pub trait With
{
fn uid() -> Uid;
}
@@ -231,13 +233,29 @@ impl<ComponentT: Component> With for ComponentT
}
}
-#[derive(Debug)]
-pub enum Wildcard {}
-
-impl With for Wildcard
+pub trait WithUidTuple
{
- fn uid() -> Uid
- {
- Uid::wildcard()
- }
+ type UidsArray: Array<Uid>;
+
+ fn uids() -> Self::UidsArray;
+}
+
+macro_rules! impl_with_uid_tuple {
+ ($c: tt) => {
+ seq!(I in 0..=$c {
+ impl<#(WithUid~I: With,)*> WithUidTuple for (#(WithUid~I,)*)
+ {
+ type UidsArray = [Uid; $c + 1];
+
+ fn uids() -> Self::UidsArray
+ {
+ [#(WithUid~I::uid(),)*]
+ }
+ }
+ });
+ };
}
+
+seq!(C in 0..=16 {
+ impl_with_uid_tuple!(C);
+});
diff --git a/ecs/src/util.rs b/ecs/src/util.rs
index 9ab4dc6..27e9748 100644
--- a/ecs/src/util.rs
+++ b/ecs/src/util.rs
@@ -8,20 +8,20 @@ pub(crate) mod array_vec;
pub trait VecExt<Item>
{
- fn insert_at_partition_point_by_key<Key>(
+ fn insert_at_part_pt_by_key<Key>(
&mut self,
item: Item,
- func: impl FnMut(&Item) -> Key,
+ func: impl FnMut(&Item) -> &Key,
) where
Key: Ord;
}
impl<Item> VecExt<Item> for Vec<Item>
{
- fn insert_at_partition_point_by_key<Key>(
+ fn insert_at_part_pt_by_key<Key>(
&mut self,
item: Item,
- mut func: impl FnMut(&Item) -> Key,
+ mut func: impl FnMut(&Item) -> &Key,
) where
Key: Ord,
{
@@ -300,6 +300,88 @@ macro_rules! gen_mask_64 {
pub(crate) use gen_mask_64;
+macro_rules! impl_multiple {
+ (
+ $type: ident,
+ ($(
+ impl$(<$($generic: tt$(: $bound: ident)?),*>)?
+ _<$($lt_param: lifetime),*><$($type_param: ty),*>
+ $(($($extra_cb_arg: expr),*))?
+ ),*)
+ cb=(
+ type_params=($($ty_param_matcher: ident),*)
+ $(, $($extra_matcher: ident),+)?
+ ) => {
+ $($item_tt: tt)*
+ }
+ ) => {
+ const _: () = {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ _gen_multiple_impl_item,
+ ($($ty_param_matcher),*),
+ ($($($extra_matcher),+)?),
+ ($($item_tt)*)
+ );
+
+ $(
+ impl $(<$($generic$(: $bound)?,)*>)? $type<$($lt_param,)* $($type_param),*>
+ {
+ _gen_multiple_impl_item!(
+ type_params=($($type_param),*),
+ $($($extra_cb_arg),*)?
+ );
+ }
+ )*
+ };
+ };
+
+ (
+ @(make_gen_item_macro)
+ $name: ident,
+ ($($ty_param_matcher: ident),*),
+ ($($extra_matcher: ident),*),
+ ($($transcriber: tt)*)
+ ) => {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ ($),
+ $name,
+ ($($ty_param_matcher),*),
+ ($($extra_matcher),*),
+ ($($transcriber)*)
+ );
+ };
+
+ (
+ @(make_gen_item_macro)
+ ($dollar: tt),
+ $name: ident,
+ ($($ty_param_matcher: ident),*),
+ ($($extra_matcher: ident),*),
+ ($($transcriber: tt)*)
+ ) => {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ $name,
+ (
+ type_params=($($dollar$ty_param_matcher: ty),*),
+ $($dollar$extra_matcher: expr),*
+ ) => {
+ $($transcriber)*
+ }
+ );
+ };
+
+ (@(make_gen_item_macro) $name: ident, $($rule: tt)*) => {
+ macro_rules! $name {
+ $($rule)*
+ }
+ };
+}
+
+pub(crate) use impl_multiple;
+
mod sealed
{
pub trait Sealed {}
diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs
index 13a0349..5d0aac9 100644
--- a/ecs/src/util/array_vec.rs
+++ b/ecs/src/util/array_vec.rs
@@ -45,8 +45,8 @@ impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY>
unsafe {
std::ptr::copy(
- &self.items[index],
- &mut self.items[index + 1],
+ &raw const self.items[index],
+ &raw mut self.items[index + 1],
self.len - index,
);
}
@@ -115,3 +115,17 @@ impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY>
}
}
}
+
+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();
+ }
+ }
+ }
+}