summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-08-14 20:05:30 +0200
committerHampusM <hampus@hampusmat.com>2024-08-14 20:05:30 +0200
commit07aa59a122cc5e14d2fb2e2c6e3d8f82e4397bde (patch)
tree0ac63f5262d97d3d7f50ab1c72d1ace61935608c
parente9074af15cae7b3c354e524e9fa78cbddb20ff84 (diff)
feat(ecs): add component added event
-rw-r--r--ecs/src/actions.rs15
-rw-r--r--ecs/src/event.rs35
-rw-r--r--ecs/src/event/component.rs75
-rw-r--r--ecs/src/extension.rs7
-rw-r--r--ecs/src/lib.rs124
-rw-r--r--ecs/src/type_name.rs10
6 files changed, 229 insertions, 37 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs
index 7698b45..32fcfb9 100644
--- a/ecs/src/actions.rs
+++ b/ecs/src/actions.rs
@@ -8,7 +8,6 @@ use crate::component::{
Sequence as ComponentSequence,
};
use crate::entity::Uid as EntityUid;
-use crate::lock::{Lock, WriteGuard};
use crate::system::{NoInitParamFlag, Param as SystemParam, System};
use crate::{ActionQueue, World};
@@ -16,8 +15,8 @@ use crate::{ActionQueue, World};
#[derive(Debug)]
pub struct Actions<'world>
{
- action_queue: WriteGuard<'world, ActionQueue>,
- action_queue_weak: Weak<Lock<ActionQueue>>,
+ action_queue: &'world ActionQueue,
+ action_queue_weak: Weak<ActionQueue>,
}
impl<'world> Actions<'world>
@@ -64,12 +63,10 @@ impl<'world> Actions<'world>
}
}
- fn new(action_queue: &'world Arc<Lock<ActionQueue>>) -> Self
+ fn new(action_queue: &'world Arc<ActionQueue>) -> Self
{
Self {
- action_queue: action_queue
- .write_nonblock()
- .expect("Failed to aquire read-write action queue lock"),
+ action_queue: &*action_queue,
action_queue_weak: Arc::downgrade(action_queue),
}
}
@@ -111,7 +108,7 @@ unsafe impl<'world> SystemParam<'world> for Actions<'world>
#[derive(Debug, Clone)]
pub struct WeakRef
{
- action_queue: Weak<Lock<ActionQueue>>,
+ action_queue: Weak<ActionQueue>,
}
impl WeakRef
@@ -134,7 +131,7 @@ impl WeakRef
#[derive(Debug, Clone)]
pub struct Ref<'weak_ref>
{
- action_queue: Arc<Lock<ActionQueue>>,
+ action_queue: Arc<ActionQueue>,
_pd: PhantomData<&'weak_ref ()>,
}
diff --git a/ecs/src/event.rs b/ecs/src/event.rs
index 93d4545..0caa56f 100644
--- a/ecs/src/event.rs
+++ b/ecs/src/event.rs
@@ -1,10 +1,21 @@
use std::any::TypeId;
use std::fmt::Debug;
-use std::hash::Hash;
+use std::hash::{DefaultHasher, Hash, Hasher};
use seq_macro::seq;
-pub trait Event: Debug + 'static {}
+pub mod component;
+
+pub trait Event: Debug + 'static
+{
+ /// Returns the ID of this event.
+ fn id() -> Id
+ where
+ Self: Sized,
+ {
+ Id::new::<Self, ()>(None)
+ }
+}
pub mod start;
@@ -13,15 +24,27 @@ pub mod start;
pub struct Id
{
inner: TypeId,
+ extra: Option<u64>,
}
impl Id
{
- /// Returns the id of a [`Event`];
#[must_use]
- pub fn of<EventT: Event>() -> Self
+ pub fn new<EventT, Extra>(extra: Option<Extra>) -> Self
+ where
+ EventT: Event,
+ Extra: Hash,
{
- Self { inner: TypeId::of::<EventT>() }
+ Self {
+ inner: TypeId::of::<EventT>(),
+ extra: extra.map(|extra| {
+ let mut hasher = DefaultHasher::new();
+
+ extra.hash(&mut hasher);
+
+ hasher.finish()
+ }),
+ }
}
}
@@ -59,7 +82,7 @@ macro_rules! impl_sequence {
fn ids() -> Self::Ids {
[#(
- Id::of::<Event~I>(),
+ Event~I::id(),
)*]
}
}
diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs
new file mode 100644
index 0000000..306d43f
--- /dev/null
+++ b/ecs/src/event/component.rs
@@ -0,0 +1,75 @@
+//! Component events.
+
+use std::fmt::{Debug, Formatter};
+use std::marker::PhantomData;
+
+use ecs_macros::Component;
+
+use crate::component::{Component, Id as ComponentId};
+use crate::event::{Event, Id};
+use crate::tuple::{ReduceElement as TupleReduceElement, With as TupleWith};
+
+/// Event emitted when:
+/// a) A entity with component `ComponentT` is spawned.
+/// b) A component `ComponentT` is added to a entity.
+pub struct Added<ComponentT>
+where
+ ComponentT: Component,
+{
+ _pd: PhantomData<ComponentT>,
+}
+
+impl<ComponentT> Debug for Added<ComponentT>
+where
+ ComponentT: Component,
+{
+ fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_struct("Added")
+ .field("_pd", &self._pd)
+ .finish()
+ }
+}
+
+impl<ComponentT> Default for Added<ComponentT>
+where
+ ComponentT: Component,
+{
+ fn default() -> Self
+ {
+ Self { _pd: PhantomData }
+ }
+}
+
+impl<ComponentT> Event for Added<ComponentT>
+where
+ ComponentT: Component,
+{
+ fn id() -> Id
+ where
+ Self: Sized,
+ {
+ Id::new::<Added<ComponentForId>, _>(Some(ComponentId::of::<ComponentT>()))
+ }
+}
+
+pub fn create_added_id(component_id: ComponentId) -> Id
+{
+ Id::new::<Added<ComponentForId>, _>(Some(component_id))
+}
+
+pub struct ComponentToAddedEvent;
+
+impl<ComponentT: Component, Accumulator>
+ TupleReduceElement<Accumulator, ComponentToAddedEvent> for ComponentT
+where
+ Accumulator: TupleWith<Added<Self>>,
+{
+ type Return = Accumulator::With;
+}
+
+use crate as ecs;
+
+#[derive(Debug, Component)]
+struct ComponentForId;
diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs
index fc5a345..99320cb 100644
--- a/ecs/src/extension.rs
+++ b/ecs/src/extension.rs
@@ -1,7 +1,9 @@
use crate::component::Sequence as ComponentSequence;
-use crate::event::Event;
+use crate::event::component::ComponentToAddedEvent;
+use crate::event::{Event, Sequence as EventSequence};
use crate::sole::Sole;
use crate::system::System;
+use crate::tuple::Reduce as TupleReduce;
use crate::{SoleAlreadyExistsError, World};
/// A collection of systems, entities & soles that can be added to a [`World`].
@@ -38,7 +40,8 @@ impl<'world> Collector<'world>
/// Adds a entity to the [`World`].
pub fn add_entity<Comps>(&mut self, components: Comps)
where
- Comps: ComponentSequence,
+ Comps: ComponentSequence + TupleReduce<ComponentToAddedEvent>,
+ Comps::Out: EventSequence,
{
self.world.create_entity(components);
}
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index 04c9b9f..ed2ccef 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -1,18 +1,21 @@
#![deny(clippy::all, clippy::pedantic)]
use std::any::{type_name, TypeId};
+use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::mem::ManuallyDrop;
-use std::ops::RangeBounds;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
-use std::vec::Drain;
use crate::actions::Action;
use crate::component::storage::Storage as ComponentStorage;
use crate::component::{Component, Id as ComponentId, Sequence as ComponentSequence};
use crate::entity::Uid as EntityUid;
+use crate::event::component::{
+ create_added_id as create_component_added_event_id,
+ ComponentToAddedEvent,
+};
use crate::event::start::Start as StartEvent;
use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence};
use crate::extension::{Collector as ExtensionCollector, Extension};
@@ -20,6 +23,7 @@ use crate::lock::Lock;
use crate::query::options::Options as QueryOptions;
use crate::sole::Sole;
use crate::system::{System, TypeErased as TypeErasedSystem};
+use crate::tuple::Reduce as TupleReduce;
use crate::type_name::TypeName;
pub mod actions;
@@ -64,7 +68,8 @@ impl World
/// Will panic if mutable internal lock cannot be acquired.
pub fn create_entity<Comps>(&mut self, components: Comps) -> EntityUid
where
- Comps: ComponentSequence,
+ Comps: ComponentSequence + TupleReduce<ComponentToAddedEvent>,
+ Comps::Out: EventSequence,
{
let (_, entity_uid) = self
.data
@@ -73,6 +78,10 @@ impl World
.expect("Failed to acquire read-write component storage lock")
.push_entity(EntityUid::new_unique(), components.into_vec());
+ for component_added_event_id in <Comps::Out as EventSequence>::ids().iter() {
+ self.emit_event_by_id(*component_added_event_id);
+ }
+
entity_uid
}
@@ -98,7 +107,7 @@ impl World
self.data
.events
- .entry(EventId::of::<EventT>())
+ .entry(EventT::id())
.or_default()
.push(self.systems.len() - 1);
@@ -125,7 +134,7 @@ impl World
where
EventT: Event,
{
- self.emit_event_by_id(EventId::of::<EventT>());
+ self.emit_event_by_id(EventT::id());
drop(event);
}
@@ -144,13 +153,22 @@ impl World
/// Will panic if a mutable internal lock cannot be acquired.
pub fn perform_queued_actions(&self)
{
- for action in self
- .data
- .action_queue
- .write_nonblock()
- .expect("Failed to aquire read-write action queue lock")
- .drain(..)
+ let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
{
+ ActiveActionQueue::A => &self.data.action_queue.queue_a,
+ ActiveActionQueue::B => &self.data.action_queue.queue_b,
+ }
+ .write_nonblock()
+ .unwrap_or_else(|err| {
+ panic!(
+ "Failed to take read-write action queue lock {:?}: {err}",
+ self.data.action_queue.active_queue
+ );
+ });
+
+ let mut has_swapped_active_queue = false;
+
+ for action in active_action_queue.drain(..) {
match action {
Action::Spawn(components) => {
let mut component_storage_lock =
@@ -158,8 +176,33 @@ impl World
"Failed to acquire read-write component storage lock",
);
+ let component_ids = components
+ .iter()
+ .map(|component| component.id())
+ .collect::<Vec<_>>();
+
component_storage_lock
.push_entity(EntityUid::new_unique(), components);
+
+ drop(component_storage_lock);
+
+ if !has_swapped_active_queue {
+ 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;
+ }
+
+ for component_id in component_ids {
+ self.emit_event_by_id(create_component_added_event_id(
+ component_id,
+ ));
+ }
}
Action::AddComponents(entity_uid, components) => {
let mut component_storage_lock =
@@ -167,8 +210,33 @@ impl World
"Failed to acquire read-write component storage lock",
);
+ let component_ids = components
+ .iter()
+ .map(|component| component.id())
+ .collect::<Vec<_>>();
+
component_storage_lock
.add_components_to_entity(entity_uid, components);
+
+ drop(component_storage_lock);
+
+ if !has_swapped_active_queue {
+ 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;
+ }
+
+ for component_id in component_ids {
+ self.emit_event_by_id(create_component_added_event_id(
+ component_id,
+ ));
+ }
}
Action::RemoveComponents(entity_uid, component_ids) => {
let mut component_storage_lock =
@@ -237,7 +305,7 @@ pub struct WorldData
events: HashMap<EventId, Vec<usize>>,
component_storage: Arc<Lock<ComponentStorage>>,
sole_storage: SoleStorage,
- action_queue: Arc<Lock<ActionQueue>>,
+ action_queue: Arc<ActionQueue>,
}
#[derive(Debug)]
@@ -261,22 +329,38 @@ impl From<Box<dyn Component>> for EntityComponent
}
}
+#[derive(Debug, Default, Clone, Copy)]
+enum ActiveActionQueue
+{
+ #[default]
+ A,
+ B,
+}
+
#[derive(Debug, Default)]
struct ActionQueue
{
- queue: Vec<Action>,
+ queue_a: Lock<Vec<Action>>,
+ queue_b: Lock<Vec<Action>>,
+ active_queue: RefCell<ActiveActionQueue>,
}
impl ActionQueue
{
- fn push(&mut self, action: Action)
+ fn push(&self, action: Action)
{
- self.queue.push(action);
- }
-
- fn drain(&mut self, range: impl RangeBounds<usize>) -> Drain<Action>
- {
- self.queue.drain(range)
+ 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),
+ }
}
}
diff --git a/ecs/src/type_name.rs b/ecs/src/type_name.rs
index 5892c6f..54179be 100644
--- a/ecs/src/type_name.rs
+++ b/ecs/src/type_name.rs
@@ -1,5 +1,15 @@
+use std::any::type_name;
+
pub trait TypeName
{
/// Returns the name of this type.
fn type_name(&self) -> &'static str;
}
+
+impl<Item> TypeName for Vec<Item>
+{
+ fn type_name(&self) -> &'static str
+ {
+ type_name::<Self>()
+ }
+}