diff options
author | HampusM <hampus@hampusmat.com> | 2024-04-10 20:37:53 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-04-10 20:38:47 +0200 |
commit | ca51244e9d462c661d29dc60ce5bf6f9056c569b (patch) | |
tree | e5ee42ef550cc9a10feab918fa54fe342bb8dfed | |
parent | c70e06c6d879208eb2822f6207ea7b29d47c2087 (diff) |
chore(ecs): make shared singletons not components
-rw-r--r-- | ecs/examples/with_single.rs | 59 | ||||
-rw-r--r-- | ecs/src/component.rs | 1 | ||||
-rw-r--r-- | ecs/src/component/single.rs | 98 | ||||
-rw-r--r-- | ecs/src/lib.rs | 49 | ||||
-rw-r--r-- | ecs/src/sole.rs | 134 |
5 files changed, 154 insertions, 187 deletions
diff --git a/ecs/examples/with_single.rs b/ecs/examples/with_single.rs deleted file mode 100644 index d222f76..0000000 --- a/ecs/examples/with_single.rs +++ /dev/null @@ -1,59 +0,0 @@ -use ecs::component::single::Single; -use ecs::event::Event; -use ecs::{Component, Query, World}; - -#[derive(Component)] -struct Ammo -{ - ammo_left: u32, -} - -#[derive(Component, Default)] -struct AmmoCounter -{ - counter: u32, -} - -fn count_ammo(query: Query<(Ammo,)>, mut ammo_counter: Single<AmmoCounter>) -{ - for (ammo,) in &query { - println!("Found {} ammo", ammo.ammo_left); - - ammo_counter.counter += ammo.ammo_left; - } -} - -fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>) -{ - println!("Total ammo count: {}", ammo_counter.counter); - - assert_eq!(ammo_counter.counter, 19); -} - -#[derive(Debug)] -struct EventA; - -impl Event for EventA {} - -#[derive(Debug)] -struct EventB; - -impl Event for EventB {} - -fn main() -{ - let mut world = World::new(); - - world.register_system(EventA, count_ammo); - world.register_system(EventB, print_total_ammo_count); - - world.create_entity((Ammo { ammo_left: 4 },)); - world.create_entity((Ammo { ammo_left: 7 },)); - world.create_entity((Ammo { ammo_left: 8 },)); - - world.add_single_component(AmmoCounter::default()).unwrap(); - - world.emit(EventA); - - world.emit(EventB); -} diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 0e5f020..7a61f39 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -8,7 +8,6 @@ use crate::system::{ComponentRefMut, Input as SystemInput}; use crate::type_name::TypeName; pub mod local; -pub mod single; pub trait Component: SystemInput + Any + TypeName { diff --git a/ecs/src/component/single.rs b/ecs/src/component/single.rs deleted file mode 100644 index a63dbe3..0000000 --- a/ecs/src/component/single.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::any::{type_name, Any, TypeId}; -use std::ops::{Deref, DerefMut}; - -use crate::component::Component; -use crate::system::{ComponentRefMut, NoInitParamFlag, Param as SystemParam, System}; -use crate::tuple::FilterExclude as TupleFilterExclude; -use crate::WorldData; - -/// Holds a component which has a single instance and is shared globally. -#[derive(Debug)] -pub struct Single<'world, SingleComponent: Component> -{ - single_component: ComponentRefMut<'world, SingleComponent>, -} - -unsafe impl<'world, SingleComponent> SystemParam<'world> - for Single<'world, SingleComponent> -where - SingleComponent: Component, -{ - type Flags = NoInitParamFlag; - type Input = TupleFilterExclude; - - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world_data: &'world WorldData, - ) -> Self - { - let single_component = world_data - .single_component_storage - .get::<SingleComponent>() - .expect("Single component was not found in world") - .write_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock to single component {}", - type_name::<SingleComponent>() - ) - }); - - Self { - single_component: ComponentRefMut::new(single_component), - } - } - - fn is_compatible<Other: SystemParam<'world>>() -> bool - { - let other_comparable = Other::get_comparable(); - - let Some(comparable) = other_comparable.downcast_ref::<Comparable>() else { - // The other system param is not Single - return true; - }; - - TypeId::of::<SingleComponent>() != comparable.single_component_type_id - } - - fn get_comparable() -> Box<dyn Any> - { - Box::new(Comparable { - single_component_type_id: TypeId::of::<SingleComponent>(), - }) - } -} - -impl<'world, SingleComponent> Deref for Single<'world, SingleComponent> -where - SingleComponent: Component, -{ - type Target = SingleComponent; - - fn deref(&self) -> &Self::Target - { - &self.single_component - } -} - -impl<'world, SingleComponent> DerefMut for Single<'world, SingleComponent> -where - SingleComponent: Component, -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.single_component - } -} - -struct Comparable -{ - single_component_type_id: TypeId, -} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index c93781d..e568fb8 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -13,6 +13,7 @@ use crate::actions::Action; use crate::component::{Component, Sequence as ComponentSequence}; use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; use crate::lock::Lock; +use crate::sole::Sole; use crate::system::{System, TypeErased as TypeErasedSystem}; use crate::type_name::TypeName; @@ -21,6 +22,7 @@ pub mod component; pub mod event; pub mod lock; pub mod query; +pub mod sole; pub mod system; pub mod tuple; pub mod type_name; @@ -89,18 +91,15 @@ impl World }); } - /// Adds a single component. This component will be globally shared. + /// Adds a globally shared singleton value. /// /// # Errors - /// Returns `Err` if this component has already been added as a single component. - pub fn add_single_component<SingleComponent>( - &mut self, - single_component: SingleComponent, - ) -> Result<(), SingleComponentAlreadyExistsError> + /// Returns `Err` if this [`Sole`] has already been added. + pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> where - SingleComponent: Component, + SoleT: Sole, { - self.data.single_component_storage.insert(single_component) + self.data.sole_storage.insert(sole) } pub fn register_system<'this, EventT, SystemImpl>( @@ -227,7 +226,7 @@ pub struct WorldData { events: HashMap<EventId, Vec<usize>>, component_storage: Arc<Lock<ComponentStorage>>, - single_component_storage: SingleComponentStorage, + sole_storage: SoleStorage, action_queue: Arc<Lock<ActionQueue>>, } @@ -348,39 +347,31 @@ impl Drop for ComponentStorage } #[derive(Debug, thiserror::Error)] -#[error("Single component {0} already exists")] -pub struct SingleComponentAlreadyExistsError(pub &'static str); +#[error("Sole {0} already exists")] +pub struct SoleAlreadyExistsError(pub &'static str); #[derive(Debug, Default)] -struct SingleComponentStorage +struct SoleStorage { - storage: HashMap<TypeId, Lock<Box<dyn Component>>>, + storage: HashMap<TypeId, Lock<Box<dyn Sole>>>, } -impl SingleComponentStorage +impl SoleStorage { - fn get<SingleComponent: Component>(&self) -> Option<&Lock<Box<dyn Component>>> + fn get<SoleT: Sole>(&self) -> Option<&Lock<Box<dyn Sole>>> { - self.storage.get(&TypeId::of::<SingleComponent>()) + self.storage.get(&TypeId::of::<SoleT>()) } - fn insert<SingleComponent: Component>( - &mut self, - single_component: SingleComponent, - ) -> Result<(), SingleComponentAlreadyExistsError> + fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> { - let single_component_type_id = TypeId::of::<SingleComponent>(); + let sole_type_id = TypeId::of::<SoleT>(); - if self.storage.contains_key(&single_component_type_id) { - return Err(SingleComponentAlreadyExistsError(type_name::< - SingleComponent, - >())); + if self.storage.contains_key(&sole_type_id) { + return Err(SoleAlreadyExistsError(type_name::<SoleT>())); } - self.storage.insert( - single_component_type_id, - Lock::new(Box::new(single_component)), - ); + self.storage.insert(sole_type_id, Lock::new(Box::new(sole))); Ok(()) } diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs new file mode 100644 index 0000000..e5166e9 --- /dev/null +++ b/ecs/src/sole.rs @@ -0,0 +1,134 @@ +use std::any::{type_name, Any, TypeId}; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +use crate::lock::WriteGuard; +use crate::system::{NoInitParamFlag, Param as SystemParam, System}; +use crate::tuple::FilterExclude as TupleFilterExclude; +use crate::type_name::TypeName; +use crate::WorldData; + +/// A type which has a single instance and is shared globally. +pub trait Sole: Any + TypeName +{ + fn as_any_mut(&mut self) -> &mut dyn Any; + + fn as_any(&self) -> &dyn Any; +} + +impl dyn Sole +{ + pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real> + { + self.as_any_mut().downcast_mut() + } + + pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real> + { + self.as_any().downcast_ref() + } +} + +impl Debug for dyn Sole +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + formatter.debug_struct("Sole").finish_non_exhaustive() + } +} + +impl TypeName for Box<dyn Sole> +{ + fn type_name(&self) -> &'static str + { + self.as_ref().type_name() + } +} + +/// Holds a reference to a globally shared singleton value. +#[derive(Debug)] +pub struct Single<'world, SoleT: Sole> +{ + sole: WriteGuard<'world, Box<dyn Sole>>, + _ph: PhantomData<SoleT>, +} + +unsafe impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT> +where + SoleT: Sole, +{ + type Flags = NoInitParamFlag; + type Input = TupleFilterExclude; + + fn initialize<SystemImpl>( + _system: &mut impl System<'world, SystemImpl>, + _input: Self::Input, + ) + { + } + + fn new<SystemImpl>( + _system: &'world impl System<'world, SystemImpl>, + world_data: &'world WorldData, + ) -> Self + { + let sole = world_data + .sole_storage + .get::<SoleT>() + .expect("Sole was not found in world") + .write_nonblock() + .unwrap_or_else(|_| { + panic!( + "Failed to aquire read-write lock to single component {}", + type_name::<SoleT>() + ) + }); + + Self { sole, _ph: PhantomData } + } + + fn is_compatible<Other: SystemParam<'world>>() -> bool + { + let other_comparable = Other::get_comparable(); + + let Some(comparable) = other_comparable.downcast_ref::<Comparable>() else { + // The other system param is not Single + return true; + }; + + TypeId::of::<SoleT>() != comparable.sole_type_id + } + + fn get_comparable() -> Box<dyn Any> + { + Box::new(Comparable { sole_type_id: TypeId::of::<SoleT>() }) + } +} + +impl<'world, SoleT> Deref for Single<'world, SoleT> +where + SoleT: Sole, +{ + type Target = SoleT; + + fn deref(&self) -> &Self::Target + { + self.sole.downcast_ref().unwrap() + } +} + +impl<'world, SoleT> DerefMut for Single<'world, SoleT> +where + SoleT: Sole, +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + self.sole.downcast_mut().unwrap() + } +} + +struct Comparable +{ + sole_type_id: TypeId, +} |