summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-12-09 14:05:33 +0100
committerHampusM <hampus@hampusmat.com>2024-12-09 14:05:33 +0100
commitdcc40c9205e5f4cf484523f97eb12a561d7b2b22 (patch)
tree2908b6ca3b2fa390a45b383b91edf7a72c42ef4b
parent158e36bf6bfcbc2ed0ffc670788ed8c0abd3f282 (diff)
refactor(ecs): use phases for system ordering
-rw-r--r--ecs/examples/event_loop.rs37
-rw-r--r--ecs/examples/extension.rs13
-rw-r--r--ecs/examples/multiple_queries.rs6
-rw-r--r--ecs/examples/optional_component.rs11
-rw-r--r--ecs/examples/relationship.rs6
-rw-r--r--ecs/examples/simple.rs6
-rw-r--r--ecs/examples/with_local.rs18
-rw-r--r--ecs/examples/with_sole.rs26
-rw-r--r--ecs/src/actions.rs41
-rw-r--r--ecs/src/component.rs28
-rw-r--r--ecs/src/event.rs95
-rw-r--r--ecs/src/event/component.rs54
-rw-r--r--ecs/src/event/start.rs7
-rw-r--r--ecs/src/extension.rs16
-rw-r--r--ecs/src/lib.rs311
-rw-r--r--ecs/src/phase.rs15
-rw-r--r--ecs/src/relationship.rs8
-rw-r--r--ecs/src/system.rs7
18 files changed, 327 insertions, 378 deletions
diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs
index 8d8d84f..c9f7757 100644
--- a/ecs/examples/event_loop.rs
+++ b/ecs/examples/event_loop.rs
@@ -1,6 +1,7 @@
use ecs::actions::Actions;
-use ecs::event::Event;
-use ecs::{Component, Query, World};
+use ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
+use ecs::relationship::{ChildOf, Relationship};
+use ecs::{static_entity, Component, Query, World};
#[derive(Component)]
struct Wool
@@ -64,28 +65,28 @@ fn age(query: Query<(Health, Name)>, mut actions: Actions)
}
}
-#[derive(Debug)]
-struct EventA;
+static_entity!(
+ SHEER_PHASE,
+ (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE))
+);
-impl Event for EventA {}
+static_entity!(
+ FEED_PHASE,
+ (Phase, <Relationship<ChildOf, Phase>>::new(*SHEER_PHASE))
+);
-#[derive(Debug)]
-struct EventB;
-
-impl Event for EventB {}
-
-#[derive(Debug)]
-struct EventC;
-
-impl Event for EventC {}
+static_entity!(
+ AGE_PHASE,
+ (Phase, <Relationship<ChildOf, Phase>>::new(*FEED_PHASE))
+);
fn main()
{
let mut world = World::new();
- world.register_system(EventA, sheer);
- world.register_system(EventB, feed);
- world.register_system(EventC, age);
+ world.register_system(*SHEER_PHASE, sheer);
+ world.register_system(*FEED_PHASE, feed);
+ world.register_system(*AGE_PHASE, age);
world.create_entity((
Wool { remaining: 30 },
@@ -93,5 +94,5 @@ fn main()
Name { name: "Bessy" },
));
- world.event_loop::<(EventA, EventB, EventC)>();
+ world.start_loop();
}
diff --git a/ecs/examples/extension.rs b/ecs/examples/extension.rs
index 2022b05..ddde32c 100644
--- a/ecs/examples/extension.rs
+++ b/ecs/examples/extension.rs
@@ -1,6 +1,6 @@
use ecs::actions::Actions;
-use ecs::event::Event;
use ecs::extension::{Collector as ExtensionCollector, Extension};
+use ecs::phase::UPDATE as UPDATE_PHASE;
use ecs::{Component, Query, World};
#[derive(Debug, Component)]
@@ -19,11 +19,6 @@ enum EvilnessLevel
Medium,
}
-#[derive(Debug)]
-struct Update;
-
-impl Event for Update {}
-
fn spawn_enemies(
spawner_query: Query<(EnemySpawnSource, Position)>,
enemies_query: Query<(EvilnessLevel,)>,
@@ -57,7 +52,7 @@ impl Extension for EnemySpawningExtension
{
fn collect(self, mut collector: ExtensionCollector<'_>)
{
- collector.add_system(Update, spawn_enemies);
+ collector.add_system(*UPDATE_PHASE, spawn_enemies);
collector.add_entity((Position { x: 187, y: 30 }, EnemySpawnSource));
}
@@ -70,8 +65,6 @@ fn main()
world.add_extension(EnemySpawningExtension);
for _ in 0..7 {
- world.emit(Update);
-
- world.perform_queued_actions();
+ world.step();
}
}
diff --git a/ecs/examples/multiple_queries.rs b/ecs/examples/multiple_queries.rs
index 2736bce..1ae9d19 100644
--- a/ecs/examples/multiple_queries.rs
+++ b/ecs/examples/multiple_queries.rs
@@ -1,6 +1,6 @@
use std::fmt::Display;
-use ecs::event::start::Start as StartEvent;
+use ecs::phase::START as START_PHASE;
use ecs::{Component, Query, World};
#[derive(Component)]
@@ -61,7 +61,7 @@ fn main()
{
let mut world = World::new();
- world.register_system(StartEvent, do_attacks);
+ world.register_system(*START_PHASE, do_attacks);
world.create_entity((
Health { health: 100 },
@@ -81,5 +81,5 @@ fn main()
world.create_entity((AttackStrength::Strong,));
world.create_entity((AttackStrength::Weak,));
- world.emit(StartEvent);
+ world.step();
}
diff --git a/ecs/examples/optional_component.rs b/ecs/examples/optional_component.rs
index e47bf2e..bcc0c54 100644
--- a/ecs/examples/optional_component.rs
+++ b/ecs/examples/optional_component.rs
@@ -1,4 +1,4 @@
-use ecs::event::Event;
+use ecs::phase::UPDATE as UPDATE_PHASE;
use ecs::{Component, Query, World};
#[derive(Debug, Component)]
@@ -48,16 +48,11 @@ fn pet_cats(query: Query<(CatName, PettingCapacity, Option<Aggressivity>)>)
}
}
-#[derive(Debug)]
-struct PettingTime;
-
-impl Event for PettingTime {}
-
fn main()
{
let mut world = World::new();
- world.register_system(PettingTime, pet_cats);
+ world.register_system(*UPDATE_PHASE, pet_cats);
world.create_entity((
CatName { name: "Jasper".to_string() },
@@ -82,5 +77,5 @@ fn main()
Aggressivity::Low,
));
- world.emit(PettingTime);
+ world.step();
}
diff --git a/ecs/examples/relationship.rs b/ecs/examples/relationship.rs
index e8fb327..1b3d1de 100644
--- a/ecs/examples/relationship.rs
+++ b/ecs/examples/relationship.rs
@@ -1,4 +1,4 @@
-use ecs::event::start::Start as StartEvent;
+use ecs::phase::START as START_PHASE;
use ecs::relationship::Relationship;
use ecs::{Component, Query, World};
@@ -34,7 +34,7 @@ fn main()
{
let mut world = World::new();
- world.register_system(StartEvent, print_player_stats);
+ world.register_system(*START_PHASE, print_player_stats);
let sword_uid = world.create_entity((Sword { attack_strength: 17 },));
@@ -44,5 +44,5 @@ fn main()
Relationship::<Holding, Sword>::new(sword_uid),
));
- world.emit(StartEvent);
+ world.step();
}
diff --git a/ecs/examples/simple.rs b/ecs/examples/simple.rs
index 4057c84..6429035 100644
--- a/ecs/examples/simple.rs
+++ b/ecs/examples/simple.rs
@@ -1,4 +1,4 @@
-use ecs::event::start::Start as StartEvent;
+use ecs::phase::START as START_PHASE;
use ecs::{Component, Query, World};
#[derive(Component)]
@@ -24,7 +24,7 @@ fn main()
{
let mut world = World::new();
- world.register_system(StartEvent, say_hello);
+ world.register_system(*START_PHASE, say_hello);
world.create_entity((
SomeData { num: 987_654 },
@@ -38,5 +38,5 @@ fn main()
Greeting { greeting: "Good evening".to_string() },
));
- world.emit(StartEvent);
+ world.step();
}
diff --git a/ecs/examples/with_local.rs b/ecs/examples/with_local.rs
index 5890b90..0872dfc 100644
--- a/ecs/examples/with_local.rs
+++ b/ecs/examples/with_local.rs
@@ -1,5 +1,5 @@
use ecs::component::local::Local;
-use ecs::event::Event;
+use ecs::phase::UPDATE as UPDATE_PHASE;
use ecs::system::{Into, System};
use ecs::{Component, Query, World};
@@ -42,24 +42,19 @@ fn say_whats_up(query: Query<(SomeData, Name)>, mut state: Local<SayHelloState>)
}
}
-#[derive(Debug)]
-struct Update;
-
-impl Event for Update {}
-
fn main()
{
let mut world = World::new();
world.register_system(
- Update,
+ *UPDATE_PHASE,
say_hello
.into_system()
.initialize((SayHelloState { cnt: 0 },)),
);
world.register_system(
- Update,
+ *UPDATE_PHASE,
say_whats_up
.into_system()
.initialize((SayHelloState { cnt: 0 },)),
@@ -69,9 +64,6 @@ fn main()
world.create_entity((SomeData { num: 345 },));
- world.emit(Update);
-
- println!("Haha");
-
- world.emit(Update);
+ world.step();
+ world.step();
}
diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs
index a387bea..47aa0b3 100644
--- a/ecs/examples/with_sole.rs
+++ b/ecs/examples/with_sole.rs
@@ -1,6 +1,7 @@
-use ecs::event::Event;
+use ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
+use ecs::relationship::{ChildOf, Relationship};
use ecs::sole::Single;
-use ecs::{Component, Query, Sole, World};
+use ecs::{static_entity, Component, Query, Sole, World};
#[derive(Component)]
struct Ammo
@@ -30,22 +31,17 @@ fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>)
assert_eq!(ammo_counter.counter, 19);
}
-#[derive(Debug)]
-struct EventA;
-
-impl Event for EventA {}
-
-#[derive(Debug)]
-struct EventB;
-
-impl Event for EventB {}
+static_entity!(
+ PRINT_AMMO_COUNT_PHASE,
+ (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE_PHASE))
+);
fn main()
{
let mut world = World::new();
- world.register_system(EventA, count_ammo);
- world.register_system(EventB, print_total_ammo_count);
+ world.register_system(*UPDATE_PHASE, count_ammo);
+ world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count);
world.create_entity((Ammo { ammo_left: 4 },));
world.create_entity((Ammo { ammo_left: 7 },));
@@ -53,7 +49,5 @@ fn main()
world.add_sole(AmmoCounter::default()).unwrap();
- world.emit(EventA);
-
- world.emit(EventB);
+ world.step();
}
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs
index f7b00d3..35e9569 100644
--- a/ecs/src/actions.rs
+++ b/ecs/src/actions.rs
@@ -20,36 +20,45 @@ pub struct Actions<'world>
impl<'world> Actions<'world>
{
- /// Adds a spawning a new entity to the action queue.
+ /// Queues up a entity to spawn at the end of the current tick.
pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps)
{
- self.action_queue.push(Action::Spawn(components.into_vec()));
+ self.action_queue.push(Action::Spawn(
+ components.into_vec(),
+ EventIds { ids: Comps::added_event_ids() },
+ ));
}
- /// Adds component(s) to a entity.
+ /// Queues up adding component(s) to a entity at the end of the current tick.
pub fn add_components<Comps>(&mut self, entity_uid: Uid, components: Comps)
where
Comps: ComponentSequence,
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
- self.action_queue
- .push(Action::AddComponents(entity_uid, components.into_vec()));
+ self.action_queue.push(Action::AddComponents(
+ entity_uid,
+ components.into_vec(),
+ EventIds { ids: Comps::added_event_ids() },
+ ));
}
- /// Removes component(s) from a entity.
+ /// Queues up removing component(s) from a entity at the end of the current tick.
pub fn remove_components<Comps>(&mut self, entity_uid: Uid)
where
Comps: ComponentSequence,
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
- self.action_queue
- .push(Action::RemoveComponents(entity_uid, Comps::metadata()));
+ self.action_queue.push(Action::RemoveComponents(
+ entity_uid,
+ Comps::metadata(),
+ EventIds { ids: Comps::removed_event_ids() },
+ ));
}
- /// Adds stopping the loop in [`Engine::event_loop`] at the next opportune time to the
- /// action queue.
+ /// Stops the [`World`]. The world will finish the current tick and that tick will be
+ /// the last.
pub fn stop(&mut self)
{
self.action_queue.push(Action::Stop);
@@ -135,12 +144,18 @@ impl<'weak_ref> Ref<'weak_ref>
}
}
+#[derive(Debug)]
+pub(crate) struct EventIds
+{
+ pub(crate) ids: Vec<Uid>,
+}
+
/// A action for a [`System`] to perform.
#[derive(Debug)]
pub(crate) enum Action
{
- Spawn(Vec<Box<dyn Component>>),
- AddComponents(Uid, Vec<Box<dyn Component>>),
- RemoveComponents(Uid, Vec<ComponentMetadata>),
+ Spawn(Vec<Box<dyn Component>>, EventIds),
+ AddComponents(Uid, Vec<Box<dyn Component>>, EventIds),
+ RemoveComponents(Uid, Vec<ComponentMetadata>, EventIds),
Stop,
}
diff --git a/ecs/src/component.rs b/ecs/src/component.rs
index a9894b7..0f64695 100644
--- a/ecs/src/component.rs
+++ b/ecs/src/component.rs
@@ -3,6 +3,10 @@ use std::fmt::Debug;
use seq_macro::seq;
+use crate::event::component::{
+ Added as ComponentAddedEvent,
+ Removed as ComponentRemovedEvent,
+};
use crate::lock::{ReadGuard, WriteGuard};
use crate::system::{ComponentRef, ComponentRefMut, Input as SystemInput};
use crate::type_name::TypeName;
@@ -20,11 +24,11 @@ pub trait Component: SystemInput + Any + TypeName
where
Self: Sized;
- type RefMut<'component>
+ type RefMut<'component>: FromOptionalMut<'component>
where
Self: Sized;
- type Ref<'component>
+ type Ref<'component>: FromOptional<'component>
where
Self: Sized;
@@ -159,6 +163,10 @@ pub trait Sequence
fn metadata() -> Vec<Metadata>;
+ fn added_event_ids() -> Vec<Uid>;
+
+ fn removed_event_ids() -> Vec<Uid>;
+
fn from_components_mut<'component>(
components: impl Iterator<Item = &'component EntityComponent>,
world: &'component World,
@@ -265,12 +273,26 @@ macro_rules! inner {
#(
Metadata {
id: Comp~I::id(),
- is_optional: Comp~I::is_optional()
+ is_optional: Comp~I::is_optional(),
},
)*
]
}
+ fn added_event_ids() -> Vec<Uid>
+ {
+ vec![
+ #(ComponentAddedEvent::<Comp~I>::id(),)*
+ ]
+ }
+
+ fn removed_event_ids() -> Vec<Uid>
+ {
+ vec![
+ #(ComponentRemovedEvent::<Comp~I>::id(),)*
+ ]
+ }
+
fn from_components_mut<'component>(
components: impl Iterator<Item = &'component EntityComponent>,
world: &'component World,
diff --git a/ecs/src/event.rs b/ecs/src/event.rs
index 1a4edcc..9cea807 100644
--- a/ecs/src/event.rs
+++ b/ecs/src/event.rs
@@ -1,96 +1 @@
-use std::any::TypeId;
-use std::fmt::Debug;
-use std::hash::{DefaultHasher, Hash, Hasher};
-
-use seq_macro::seq;
-
pub mod component;
-
-pub trait Event: Debug + 'static
-{
- /// Returns the ID of this event.
- #[must_use]
- fn id() -> Id
- where
- Self: Sized,
- {
- Id::new::<Self, ()>(None)
- }
-}
-
-pub mod start;
-
-/// The ID of a [`Event`].
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Id
-{
- inner: TypeId,
- extra: Option<u64>,
-}
-
-impl Id
-{
- #[must_use]
- pub fn new<EventT, Extra>(extra: Option<Extra>) -> Self
- where
- EventT: Event,
- Extra: Hash,
- {
- Self {
- inner: TypeId::of::<EventT>(),
- extra: extra.map(|extra| {
- let mut hasher = DefaultHasher::new();
-
- extra.hash(&mut hasher);
-
- hasher.finish()
- }),
- }
- }
-}
-
-pub trait Ids
-{
- type Iter<'a>: Iterator<Item = &'a Id>
- where
- Self: 'a;
-
- fn iter(&self) -> Self::Iter<'_>;
-}
-
-/// A sequence of events.
-pub trait Sequence
-{
- type Ids: Ids;
-
- fn ids() -> Self::Ids;
-}
-
-macro_rules! impl_sequence {
- ($c: tt) => {
- seq!(I in 0..=$c {
- impl Ids for [Id; $c + 1]
- {
- type Iter<'a> = std::slice::Iter<'a, Id>;
-
- fn iter(&self) -> Self::Iter<'_> {
- self.into_iter()
- }
- }
-
- impl<#(Event~I: Event,)*> Sequence for (#(Event~I,)*) {
- type Ids = [Id; $c + 1];
-
- fn ids() -> Self::Ids {
- [#(
- Event~I::id(),
- )*]
- }
- }
- });
- };
-}
-
-seq!(C in 0..=64 {
- impl_sequence!(C);
-});
diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs
index 8b066a7..5b40c39 100644
--- a/ecs/src/event/component.rs
+++ b/ecs/src/event/component.rs
@@ -6,13 +6,11 @@ use std::marker::PhantomData;
use ecs_macros::Component;
use crate::component::Component;
-use crate::uid::Uid;
-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.
+#[derive(Clone, Component)]
pub struct Added<ComponentT>
where
ComponentT: Component,
@@ -43,19 +41,8 @@ where
}
}
-impl<ComponentT> Event for Added<ComponentT>
-where
- ComponentT: Component,
-{
- fn id() -> Id
- where
- Self: Sized,
- {
- Id::new::<Added<ComponentForId>, _>(Some(ComponentT::id()))
- }
-}
-
/// Event emitted when a `ComponentT` component is removed from a entity.
+#[derive(Clone, Component)]
pub struct Removed<ComponentT>
where
ComponentT: Component,
@@ -85,40 +72,3 @@ where
Self { _pd: PhantomData }
}
}
-
-impl<ComponentT> Event for Removed<ComponentT>
-where
- ComponentT: Component,
-{
- fn id() -> Id
- where
- Self: Sized,
- {
- Id::new::<Removed<ComponentForId>, _>(Some(ComponentT::id()))
- }
-}
-
-#[must_use]
-pub fn create_added_id(component_id: Uid) -> Id
-{
- Id::new::<Added<ComponentForId>, _>(Some(component_id))
-}
-
-#[must_use]
-pub fn create_removed_id(component_id: Uid) -> Id
-{
- Id::new::<Removed<ComponentForId>, _>(Some(component_id))
-}
-
-pub struct TypeTransformComponentsToAddedEvents;
-
-impl<ComponentT: Component, Accumulator>
- TupleReduceElement<Accumulator, TypeTransformComponentsToAddedEvents> for ComponentT
-where
- Accumulator: TupleWith<Added<Self>>,
-{
- type Return = Accumulator::With;
-}
-
-#[derive(Debug, Component)]
-struct ComponentForId;
diff --git a/ecs/src/event/start.rs b/ecs/src/event/start.rs
deleted file mode 100644
index 248dd50..0000000
--- a/ecs/src/event/start.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use crate::event::Event;
-
-/// Start event.
-#[derive(Debug, Clone, Copy)]
-pub struct Start;
-
-impl Event for Start {}
diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs
index a945d89..42ebef9 100644
--- a/ecs/src/extension.rs
+++ b/ecs/src/extension.rs
@@ -1,9 +1,7 @@
use crate::component::Sequence as ComponentSequence;
-use crate::event::component::TypeTransformComponentsToAddedEvents;
-use crate::event::{Event, Sequence as EventSequence};
use crate::sole::Sole;
use crate::system::System;
-use crate::tuple::Reduce as TupleReduce;
+use crate::uid::Uid;
use crate::{SoleAlreadyExistsError, World};
/// A collection of systems, entities & soles that can be added to a [`World`].
@@ -27,21 +25,19 @@ impl<'world> Collector<'world>
}
/// Adds a system to the [`World`].
- pub fn add_system<'this, EventT, SystemImpl>(
+ pub fn add_system<'this, SystemImpl>(
&'this mut self,
- event: EventT,
+ phase_euid: Uid,
system: impl System<'this, SystemImpl>,
- ) where
- EventT: Event,
+ )
{
- self.world.register_system(event, system);
+ self.world.register_system(phase_euid, system);
}
/// Adds a entity to the [`World`].
pub fn add_entity<Comps>(&mut self, components: Comps)
where
- Comps: ComponentSequence + TupleReduce<TypeTransformComponentsToAddedEvents>,
- Comps::Out: EventSequence,
+ Comps: ComponentSequence,
{
self.world.create_entity(components);
}
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index 78e526f..01e0cde 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -10,22 +10,21 @@ use std::sync::Arc;
use crate::actions::Action;
use crate::component::storage::Storage as ComponentStorage;
-use crate::component::{Component, Sequence as ComponentSequence};
-use crate::entity::CREATE_STATIC_ENTITIES;
-use crate::event::component::{
- create_added_id as create_component_added_event_id,
- create_removed_id as create_component_removed_event_id,
- TypeTransformComponentsToAddedEvents,
+use crate::component::{
+ Component,
+ IsOptional as ComponentIsOptional,
+ Metadata as ComponentMetadata,
+ Sequence as ComponentSequence,
};
-use crate::event::start::Start as StartEvent;
-use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence};
+use crate::entity::CREATE_STATIC_ENTITIES;
use crate::extension::{Collector as ExtensionCollector, Extension};
use crate::lock::{Lock, WriteGuard};
-use crate::query::options::Options as QueryOptions;
+use crate::phase::{Phase, START as START_PHASE};
+use crate::query::options::{Not, Options as QueryOptions, With};
+use crate::relationship::{ChildOf, DependsOn, Relationship};
use crate::sole::Sole;
use crate::stats::Stats;
-use crate::system::{System, TypeErased as TypeErasedSystem};
-use crate::tuple::Reduce as TupleReduce;
+use crate::system::{System, SystemComponent};
use crate::type_name::TypeName;
use crate::uid::{Kind as UidKind, Uid};
@@ -35,6 +34,7 @@ pub mod entity;
pub mod event;
pub mod extension;
pub mod lock;
+pub mod phase;
pub mod query;
pub mod relationship;
pub mod sole;
@@ -57,9 +57,9 @@ pub use crate::query::Query;
#[derive(Debug, Default)]
pub struct World
{
- systems: Vec<TypeErasedSystem>,
data: WorldData,
stop: AtomicBool,
+ is_first_tick: AtomicBool,
}
impl World
@@ -71,6 +71,10 @@ impl World
world.add_sole(Stats::default()).ok();
+ for create_static_entity in CREATE_STATIC_ENTITIES {
+ create_static_entity(&world);
+ }
+
world
}
@@ -80,8 +84,7 @@ impl World
/// Will panic if mutable internal lock cannot be acquired.
pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid
where
- Comps: ComponentSequence + TupleReduce<TypeTransformComponentsToAddedEvents>,
- Comps::Out: EventSequence,
+ Comps: ComponentSequence,
{
let entity_uid = Uid::new_unique(UidKind::Entity);
@@ -94,8 +97,7 @@ impl World
#[doc(hidden)]
pub fn create_entity_with_uid<Comps>(&self, components: Comps, entity_uid: Uid)
where
- Comps: ComponentSequence + TupleReduce<TypeTransformComponentsToAddedEvents>,
- Comps::Out: EventSequence,
+ Comps: ComponentSequence,
{
debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
@@ -113,8 +115,8 @@ impl World
return;
};
- for component_added_event_id in <Comps::Out as EventSequence>::ids().iter() {
- self.emit_event_by_id(*component_added_event_id);
+ for added_event_id in Comps::added_event_ids() {
+ self.emit_event_by_id(added_event_id);
}
}
@@ -129,22 +131,29 @@ impl World
self.data.sole_storage.insert(sole)
}
- pub fn register_system<'this, EventT, SystemImpl>(
+ pub fn register_system<'this, SystemImpl>(
&'this mut self,
- event: EventT,
+ phase_euid: Uid,
system: impl System<'this, SystemImpl>,
- ) where
- EventT: Event,
+ )
{
- self.systems.push(system.into_type_erased());
-
- self.data
- .events
- .entry(EventT::id())
- .or_default()
- .push(self.systems.len() - 1);
+ self.create_entity((
+ SystemComponent { system: system.into_type_erased() },
+ Relationship::<DependsOn, Phase>::new(phase_euid),
+ ));
+ }
- drop(event);
+ pub fn register_observer_system<'this, SystemImpl, Event>(
+ &'this mut self,
+ system: impl System<'this, SystemImpl>,
+ event: Event,
+ ) where
+ Event: Component,
+ {
+ self.create_entity::<(SystemComponent, Event)>((
+ SystemComponent { system: system.into_type_erased() },
+ event,
+ ));
}
/// Adds a extensions.
@@ -158,20 +167,6 @@ impl World
extension.collect(extension_collector);
}
- /// Emits a event, running all systems listening to the event for each compatible
- /// entity.
- ///
- /// # Panics
- /// Will panic if a system has dissapeared.
- pub fn emit<EventT>(&self, event: EventT)
- where
- EventT: Event,
- {
- self.emit_event_by_id(EventT::id());
-
- drop(event);
- }
-
pub fn query<Comps, OptionsT>(&self) -> Query<Comps, OptionsT>
where
Comps: ComponentSequence,
@@ -180,12 +175,121 @@ impl World
Query::new(self)
}
- /// Peforms the actions that have been queued up using [`Actions`].
+ /// Performs a single tick.
+ ///
+ /// # Panics
+ /// Will panic if a internal lock cannot be acquired.
+ pub fn step(&self) -> StepResult
+ {
+ if self.stop.load(Ordering::Relaxed) {
+ return StepResult::Stop;
+ }
+
+ if self
+ .is_first_tick
+ .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ self.query_and_run_systems(*START_PHASE);
+ }
+
+ self.perform_phases();
+
+ self.perform_queued_actions();
+
+ if self.stop.load(Ordering::Relaxed) {
+ return StepResult::Stop;
+ }
+
+ let mut stats_lock = self
+ .data
+ .sole_storage
+ .get::<Stats>()
+ .expect("No stats sole found")
+ .write_nonblock()
+ .expect("Failed to aquire read-write stats sole lock");
+
+ let stats = stats_lock
+ .downcast_mut::<Stats>()
+ .expect("Casting stats sole to Stats type failed");
+
+ stats.current_tick += 1;
+
+ StepResult::Continue
+ }
+
+ /// Starts a loop which calls [`Self::step`] until the world is stopped.
///
/// # Panics
- /// Will panic if a mutable internal lock cannot be acquired.
+ /// Will panic if a internal lock cannot be acquired.
+ pub fn start_loop(&self)
+ {
+ while let StepResult::Continue = self.step() {}
+ }
+
+ fn query_and_run_systems(&self, phase_euid: Uid)
+ {
+ let system_comps_query =
+ self.query::<(SystemComponent, Relationship<DependsOn, Phase>), ()>();
+
+ let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| {
+ phase_rel
+ .target_uids()
+ .any(|target_uid| target_uid == phase_euid)
+ });
+
+ for (system_component, _) in system_iter {
+ // SAFETY: The world lives long enough
+ unsafe {
+ system_component.system.run(self);
+ }
+ }
+ }
+
+ fn perform_child_phases(&self, parent_phase_euid: Uid)
+ {
+ let phase_query = self.query::<(Phase, Relationship<ChildOf, Phase>), ()>();
+
+ for (index, (_, phase_rel)) in phase_query.iter().enumerate() {
+ if !phase_rel
+ .target_uids()
+ .any(|phase_euid| phase_euid == parent_phase_euid)
+ {
+ continue;
+ }
+
+ let phase_euid = phase_query
+ .get_entity_uid(index)
+ .expect("Cannot get current query iteration entity UID");
+
+ self.query_and_run_systems(phase_euid);
+
+ self.perform_child_phases(phase_euid);
+ }
+ }
+
+ fn perform_phases(&self)
+ {
+ let phase_query =
+ self.query::<(Phase,), Not<With<Relationship<ChildOf, Phase>>>>();
+
+ for (index, (_,)) in phase_query.iter().enumerate() {
+ let child_phase_euid = phase_query
+ .get_entity_uid(index)
+ .expect("Cannot get current query iteration entity UID");
+
+ if child_phase_euid == *START_PHASE {
+ continue;
+ }
+
+ self.query_and_run_systems(child_phase_euid);
+
+ self.perform_child_phases(child_phase_euid);
+ }
+ }
+
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
- pub fn perform_queued_actions(&self)
+ fn perform_queued_actions(&self)
{
let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
{
@@ -204,14 +308,9 @@ impl World
for action in active_action_queue.drain(..) {
match action {
- Action::Spawn(components) => {
+ Action::Spawn(components, component_added_event_ids) => {
let mut component_storage_lock = self.lock_component_storage_rw();
- let component_ids = components
- .iter()
- .map(|component| component.self_id())
- .collect::<Vec<_>>();
-
#[allow(unused_variables)]
if let Err(err) = component_storage_lock
.push_entity(Uid::new_unique(UidKind::Entity), components)
@@ -228,20 +327,17 @@ impl World
self.swap_event_queue(&mut has_swapped_active_queue);
}
- for component_id in component_ids {
- self.emit_event_by_id(create_component_added_event_id(
- component_id,
- ));
+ for comp_added_event_id in component_added_event_ids.ids {
+ self.emit_event_by_id(comp_added_event_id);
}
}
- Action::AddComponents(entity_uid, components) => {
+ Action::AddComponents(
+ entity_uid,
+ components,
+ component_added_event_ids,
+ ) => {
let mut component_storage_lock = self.lock_component_storage_rw();
- let component_ids = components
- .iter()
- .map(|component| component.self_id())
- .collect::<Vec<_>>();
-
component_storage_lock
.add_components_to_entity(entity_uid, components);
@@ -251,13 +347,15 @@ impl World
self.swap_event_queue(&mut has_swapped_active_queue);
}
- for component_id in component_ids {
- self.emit_event_by_id(create_component_added_event_id(
- component_id,
- ));
+ for comp_added_event_id in component_added_event_ids.ids {
+ self.emit_event_by_id(comp_added_event_id);
}
}
- Action::RemoveComponents(entity_uid, components_metadata) => {
+ Action::RemoveComponents(
+ entity_uid,
+ components_metadata,
+ component_removed_event_ids,
+ ) => {
let mut component_storage_lock = self.lock_component_storage_rw();
component_storage_lock.remove_components_from_entity(
@@ -273,10 +371,8 @@ impl World
self.swap_event_queue(&mut has_swapped_active_queue);
}
- for component_metadata in components_metadata {
- self.emit_event_by_id(create_component_removed_event_id(
- component_metadata.id,
- ));
+ for comp_removed_event_id in component_removed_event_ids.ids {
+ self.emit_event_by_id(comp_removed_event_id);
}
}
Action::Stop => {
@@ -286,60 +382,18 @@ impl World
}
}
- /// A event loop which runs until a stop is issued with [`Flags::stop`]. Before the
- /// loop begins, [`StartEvent`] is emitted.
- ///
- /// # Panics
- /// Will panic if a internal lock cannot be acquired.
- pub fn event_loop<EventSeq: EventSequence>(&self)
+ fn emit_event_by_id(&self, event_id: Uid)
{
- for create_static_entity in CREATE_STATIC_ENTITIES {
- create_static_entity(self);
- }
-
- self.emit(StartEvent);
-
- let event_seq = EventSeq::ids();
-
- loop {
- for event_id in event_seq.iter() {
- self.emit_event_by_id(*event_id);
- }
-
- self.perform_queued_actions();
-
- if self.stop.load(Ordering::Relaxed) {
- break;
- }
-
- let mut stats_lock = self
- .data
- .sole_storage
- .get::<Stats>()
- .expect("No stats sole found")
- .write_nonblock()
- .expect("Failed to aquire read-write stats sole lock");
-
- let stats = stats_lock
- .downcast_mut::<Stats>()
- .expect("Casting stats sole to Stats type failed");
-
- stats.current_tick += 1;
- }
- }
-
- fn emit_event_by_id(&self, event_id: EventId)
- {
- let Some(system_indices) = self.data.events.get(&event_id) else {
- return;
- };
-
- for system_index in system_indices {
- let system = self.systems.get(*system_index).unwrap();
-
+ for (system,) in self
+ .query::<(SystemComponent,), ()>()
+ .iter_with_extra_comps([ComponentMetadata {
+ id: event_id,
+ is_optional: ComponentIsOptional::No,
+ }])
+ {
// SAFETY: The world lives long enough
unsafe {
- system.run(self);
+ system.system.run(self);
}
}
}
@@ -365,10 +419,19 @@ impl World
}
}
+/// The result of calling [`World::step`].
+pub enum StepResult
+{
+ /// Another step can be made.
+ Continue,
+
+ /// The world have been stopped so no step can be made again.
+ Stop,
+}
+
#[derive(Debug, Default)]
pub struct WorldData
{
- events: HashMap<EventId, Vec<usize>>,
component_storage: Arc<Lock<ComponentStorage>>,
sole_storage: SoleStorage,
action_queue: Arc<ActionQueue>,
diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs
new file mode 100644
index 0000000..b8660f2
--- /dev/null
+++ b/ecs/src/phase.rs
@@ -0,0 +1,15 @@
+use ecs_macros::Component;
+
+use crate::relationship::{ChildOf, Relationship};
+use crate::static_entity;
+
+#[derive(Debug, Default, Clone, Copy, Component)]
+pub struct Phase;
+
+static_entity!(pub START, (Phase,));
+
+static_entity!(pub PRE_UPDATE, (Phase,));
+
+static_entity!(pub UPDATE, (Phase, <Relationship<ChildOf, Phase>>::new(*PRE_UPDATE)));
+
+static_entity!(pub PRESENT, (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE)));
diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs
index 44ed93a..7088613 100644
--- a/ecs/src/relationship.rs
+++ b/ecs/src/relationship.rs
@@ -422,3 +422,11 @@ where
self.relation.get(index)
}
}
+
+/// Relationship kind denoting a dependency to another entity
+#[derive(Debug, Default, Clone, Copy)]
+pub struct DependsOn;
+
+/// Relationship kind denoting being the child of another entity.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ChildOf;
diff --git a/ecs/src/system.rs b/ecs/src/system.rs
index 046d25b..3ba693b 100644
--- a/ecs/src/system.rs
+++ b/ecs/src/system.rs
@@ -5,6 +5,7 @@ use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::panic::{RefUnwindSafe, UnwindSafe};
+use ecs_macros::Component;
use seq_macro::seq;
use crate::component::{
@@ -314,3 +315,9 @@ impl<'a, ComponentT: Component> Deref for ComponentRef<'a, ComponentT>
self.inner.downcast_ref().unwrap()
}
}
+
+#[derive(Debug, Component)]
+pub(crate) struct SystemComponent
+{
+ pub(crate) system: TypeErased,
+}