diff options
author | HampusM <hampus@hampusmat.com> | 2024-12-09 14:05:33 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-12-09 14:05:33 +0100 |
commit | dcc40c9205e5f4cf484523f97eb12a561d7b2b22 (patch) | |
tree | 2908b6ca3b2fa390a45b383b91edf7a72c42ef4b | |
parent | 158e36bf6bfcbc2ed0ffc670788ed8c0abd3f282 (diff) |
refactor(ecs): use phases for system ordering
-rw-r--r-- | ecs/examples/event_loop.rs | 37 | ||||
-rw-r--r-- | ecs/examples/extension.rs | 13 | ||||
-rw-r--r-- | ecs/examples/multiple_queries.rs | 6 | ||||
-rw-r--r-- | ecs/examples/optional_component.rs | 11 | ||||
-rw-r--r-- | ecs/examples/relationship.rs | 6 | ||||
-rw-r--r-- | ecs/examples/simple.rs | 6 | ||||
-rw-r--r-- | ecs/examples/with_local.rs | 18 | ||||
-rw-r--r-- | ecs/examples/with_sole.rs | 26 | ||||
-rw-r--r-- | ecs/src/actions.rs | 41 | ||||
-rw-r--r-- | ecs/src/component.rs | 28 | ||||
-rw-r--r-- | ecs/src/event.rs | 95 | ||||
-rw-r--r-- | ecs/src/event/component.rs | 54 | ||||
-rw-r--r-- | ecs/src/event/start.rs | 7 | ||||
-rw-r--r-- | ecs/src/extension.rs | 16 | ||||
-rw-r--r-- | ecs/src/lib.rs | 311 | ||||
-rw-r--r-- | ecs/src/phase.rs | 15 | ||||
-rw-r--r-- | ecs/src/relationship.rs | 8 | ||||
-rw-r--r-- | ecs/src/system.rs | 7 |
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, +} |