diff options
author | HampusM <hampus@hampusmat.com> | 2024-03-12 20:54:42 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-03-12 20:54:42 +0100 |
commit | 01066718b0f13846587d26b1869f03e3713082c6 (patch) | |
tree | 0ef39b49b26330ab1ed2526105a15c7a0cba7c85 | |
parent | 251beb34720d2e7d60ceaddc811a65f52f15bdbd (diff) |
feat(ecs): make components internally mutable
-rw-r--r-- | ecs/examples/multiple_queries.rs | 10 | ||||
-rw-r--r-- | ecs/src/actions.rs | 12 | ||||
-rw-r--r-- | ecs/src/component.rs | 97 | ||||
-rw-r--r-- | ecs/src/lib.rs | 160 | ||||
-rw-r--r-- | ecs/src/system.rs | 104 | ||||
-rw-r--r-- | ecs/src/system/stateful.rs | 59 |
6 files changed, 208 insertions, 234 deletions
diff --git a/ecs/examples/multiple_queries.rs b/ecs/examples/multiple_queries.rs index ed02a69..e914cc6 100644 --- a/ecs/examples/multiple_queries.rs +++ b/ecs/examples/multiple_queries.rs @@ -32,18 +32,18 @@ impl Display for EnemyName fn do_attacks( attacker_query: Query<(AttackStrength,)>, - mut enemy_query: Query<(Health, EnemyName)>, + enemy_query: Query<(Health, EnemyName)>, ) { for (attack_strength,) in &attacker_query { - for (health, enemy_name) in enemy_query.iter_mut() { - let damage = match attack_strength { + for (mut health, enemy_name) in &enemy_query { + let damage = match *attack_strength { AttackStrength::Strong => 20, AttackStrength::Weak => 10, }; if health.health <= damage { - println!("Enemy '{enemy_name}' died"); + println!("Enemy '{}' died", *enemy_name); health.health = 0; @@ -52,7 +52,7 @@ fn do_attacks( health.health -= damage; - println!("Enemy '{enemy_name}' took {damage} damage"); + println!("Enemy '{}' took {damage} damage", *enemy_name); } } } diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index d67f895..edfee55 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -9,7 +9,7 @@ use crate::WorldData; #[derive(Debug)] pub struct Actions<'world> { - world_data: &'world mut WorldData, + world_data: &'world WorldData, } impl<'world> Actions<'world> @@ -19,6 +19,7 @@ impl<'world> Actions<'world> { self.world_data .action_queue + .borrow_mut() .push(Action::Spawn(components.into_vec())); } } @@ -28,13 +29,16 @@ unsafe impl<'world> SystemParam<'world> for Actions<'world> type Flags = NoInitParamFlag; type Input = TupleFilterExclude; - fn initialize<SystemImpl>(_system: &mut impl System<SystemImpl>, _input: Self::Input) + fn initialize<SystemImpl>( + _system: &mut impl System<'world, SystemImpl>, + _input: Self::Input, + ) { } fn new<SystemImpl>( - _system: &'world mut impl System<SystemImpl>, - world_data: &'world mut WorldData, + _system: &'world impl System<'world, SystemImpl>, + world_data: &'world WorldData, ) -> Self { Self { world_data } diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 7a997c5..07701c8 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,10 +1,16 @@ use std::any::{Any, TypeId}; +use std::cell::{RefCell, RefMut}; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; use seq_macro::seq; -use crate::system::{Input as SystemInput, Param as SystemParam, System}; +use crate::system::{ + ComponentRefMut, + Input as SystemInput, + Param as SystemParam, + System, +}; use crate::WorldData; pub trait Component: SystemInput + Any @@ -49,35 +55,27 @@ pub trait Sequence where Self: 'component; - type MutRefs<'component> - where - Self: 'component; - fn into_vec(self) -> Vec<Box<dyn Component>>; fn type_ids() -> Vec<TypeId>; - fn from_components(components: &[Box<dyn Component>]) -> Self::Refs<'_>; - - fn from_components_mut(components: &mut [Box<dyn Component>]) -> Self::MutRefs<'_>; + fn from_components(components: &[RefCell<Box<dyn Component>>]) -> Self::Refs<'_>; } macro_rules! inner { ($c: tt) => { seq!(I in 0..=$c { impl<#(Comp~I: Component,)*> Sequence for (#(Comp~I,)*) { - type Refs<'component> = (#(&'component Comp~I,)*) + type Refs<'component> = (#(ComponentRefMut<'component, Comp~I>,)*) where Self: 'component; - type MutRefs<'component> = (#(&'component mut Comp~I,)*) - where Self: 'component; - - - fn into_vec(self) -> Vec<Box<dyn Component>> { + fn into_vec(self) -> Vec<Box<dyn Component>> + { Vec::from_iter([#(Box::new(self.I) as Box<dyn Component>,)*]) } - fn type_ids() -> Vec<TypeId> { + fn type_ids() -> Vec<TypeId> + { vec![ #( TypeId::of::<Comp~I>(), @@ -85,46 +83,34 @@ macro_rules! inner { ] } - fn from_components(components: &[Box<dyn Component>]) -> Self::Refs<'_> + fn from_components( + components: &[RefCell<Box<dyn Component>>] + ) -> Self::Refs<'_> { #( - let mut comp_~I = None; + let mut comp_~I: Option<RefMut<Box<dyn Component>>> = None; )* for comp in components { - #( - if comp.is::<Comp~I>() { - comp_~I = Some(comp); - continue; - } - )* - } - - (#( - comp_~I.unwrap().downcast_ref::<Comp~I>().unwrap(), - )*) - - } - - fn from_components_mut( - components: &mut [Box<dyn Component>], - ) -> Self::MutRefs<'_> - { - #( - let mut comp_~I = None; - )* + let Ok(comp_ref) = comp.try_borrow_mut() else { + continue; + }; - for comp in components.iter_mut() { #( - if comp.is::<Comp~I>() { - comp_~I = Some(comp); + if comp_ref.is::<Comp~I>() { + comp_~I = Some(comp_ref); continue; } )* } (#( - comp_~I.unwrap().downcast_mut::<Comp~I>().unwrap(), + ComponentRefMut::new( + RefMut::filter_map( + comp_~I.unwrap(), + |component| component.downcast_mut::<Comp~I>() + ).expect("Failed to downcast component") + ), )*) } } @@ -140,17 +126,7 @@ seq!(C in 0..=64 { #[derive(Debug)] pub struct Local<'world, LocalComponent: Component> { - local_component: &'world mut LocalComponent, -} - -impl<'world, LocalComponent> Local<'world, LocalComponent> -where - LocalComponent: Component, -{ - fn new(local_component: &'world mut LocalComponent) -> Self - { - Self { local_component } - } + local_component: ComponentRefMut<'world, LocalComponent>, } unsafe impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> @@ -160,21 +136,24 @@ where type Flags = (); type Input = LocalComponent; - fn initialize<SystemImpl>(system: &mut impl System<SystemImpl>, input: Self::Input) + fn initialize<SystemImpl>( + system: &mut impl System<'world, SystemImpl>, + input: Self::Input, + ) { system.set_local_component(input); } fn new<SystemImpl>( - system: &'world mut impl System<SystemImpl>, - _world_data: &'world mut WorldData, + system: &'world impl System<'world, SystemImpl>, + _world_data: &'world WorldData, ) -> Self { let local_component = system .get_local_component_mut::<LocalComponent>() .expect("Local component is uninitialized"); - Self::new(local_component) + Self { local_component } } fn is_compatible<Other: SystemParam<'world>>() -> bool @@ -202,7 +181,7 @@ where fn deref(&self) -> &Self::Target { - self.local_component + &self.local_component } } @@ -212,6 +191,6 @@ where { fn deref_mut(&mut self) -> &mut Self::Target { - self.local_component + &mut self.local_component } } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 6b9373c..d781b3e 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,10 +1,11 @@ #![deny(clippy::all, clippy::pedantic)] use std::any::{Any, TypeId}; +use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::marker::PhantomData; -use std::slice::{Iter as SliceIter, IterMut as SliceIterMut}; +use std::slice::Iter as SliceIter; use crate::actions::Action; use crate::component::{Component, Sequence as ComponentSequence}; @@ -28,7 +29,7 @@ pub use ecs_macros::Component; #[derive(Debug, Default)] struct Entity { - components: Vec<Box<dyn Component>>, + components: Vec<RefCell<Box<dyn Component>>>, } #[derive(Debug, Default)] @@ -50,18 +51,20 @@ impl World where Comps: ComponentSequence, { - self.data - .component_storage - .entities - .push(Entity { components: components.into_vec() }); + self.data.component_storage.entities.push(Entity { + components: components + .into_vec() + .into_iter() + .map(RefCell::new) + .collect(), + }); } - pub fn register_system<TSystem, SystemImpl>( - &mut self, + pub fn register_system<'this, SystemImpl>( + &'this mut self, event: &impl Event, - system: TSystem, - ) where - TSystem: System<SystemImpl>, + system: impl System<'this, SystemImpl>, + ) { self.systems.push(system.into_type_erased()); @@ -77,36 +80,38 @@ impl World /// /// # Panics /// Will panic if a system has dissapeared. - pub fn emit(&mut self, event: &impl Event) + pub fn emit(&self, event: &impl Event) { let Some(system_indices) = self.data.events.get(&event.id()).cloned() else { return; }; for system_index in system_indices { - let system = self.systems.get_mut(system_index).unwrap(); + let system = self.systems.get(system_index).unwrap(); - system.run(&mut self.data); + // SAFETY: The world data lives long enough + unsafe { + system.run(&self.data); + } } } - pub fn query<Comps>(&mut self) -> Query<Comps> + pub fn query<Comps>(&self) -> Query<Comps> where Comps: ComponentSequence, { - Query::new(&mut self.data) + Query::new(&self.data) } /// Peforms the actions that have been queued up using [`Actions`]. pub fn perform_queued_actions(&mut self) { - for action in self.data.action_queue.drain(..) { + for action in self.data.action_queue.borrow_mut().drain(..) { match action { Action::Spawn(components) => { - self.data - .component_storage - .entities - .push(Entity { components }); + self.data.component_storage.entities.push(Entity { + components: components.into_iter().map(RefCell::new).collect(), + }); } } } @@ -118,7 +123,7 @@ pub struct WorldData { events: HashMap<EventId, Vec<usize>>, component_storage: ComponentStorage, - action_queue: Vec<Action>, + action_queue: RefCell<Vec<Action>>, } #[derive(Debug)] @@ -126,7 +131,7 @@ pub struct Query<'world, Comps> where Comps: ComponentSequence, { - world_data: &'world mut WorldData, + world_data: &'world WorldData, comps_pd: PhantomData<Comps>, } @@ -134,17 +139,7 @@ impl<'world, Comps> Query<'world, Comps> where Comps: ComponentSequence, { - fn new(world_data: &'world mut WorldData) -> Self - { - Self { world_data, comps_pd: PhantomData } - } -} - -impl<'world, Comps> Query<'world, Comps> -where - Comps: ComponentSequence, -{ - pub fn iter(&self) -> QueryComponentIter<Comps> + pub fn iter(&self) -> QueryComponentIter<'world, Comps> { QueryComponentIter { entity_iter: self.world_data.component_storage.entities.iter(), @@ -153,13 +148,9 @@ where } } - pub fn iter_mut(&mut self) -> QueryComponentMutIter<Comps> + fn new(world_data: &'world WorldData) -> Self { - QueryComponentMutIter { - entity_iter: self.world_data.component_storage.entities.iter_mut(), - component_type_ids: Comps::type_ids(), - comps_pd: PhantomData, - } + Self { world_data, comps_pd: PhantomData } } } @@ -176,19 +167,6 @@ where } } -impl<'world, Comps> IntoIterator for &'world mut Query<'world, Comps> -where - Comps: ComponentSequence, -{ - type IntoIter = QueryComponentMutIter<'world, Comps>; - type Item = Comps::MutRefs<'world>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter_mut() - } -} - unsafe impl<'world, Comps> SystemParam<'world> for Query<'world, Comps> where Comps: ComponentSequence, @@ -196,13 +174,16 @@ where type Flags = NoInitSystemParamFlag; type Input = TupleFilterExclude; - fn initialize<SystemImpl>(_system: &mut impl System<SystemImpl>, _input: Self::Input) + fn initialize<SystemImpl>( + _system: &mut impl System<'world, SystemImpl>, + _input: Self::Input, + ) { } fn new<SystemImpl>( - _system: &'world mut impl System<SystemImpl>, - world_data: &'world mut WorldData, + _system: &'world impl System<'world, SystemImpl>, + world_data: &'world WorldData, ) -> Self { Self::new(world_data) @@ -268,75 +249,26 @@ where fn next(&mut self) -> Option<Self::Item> { - let matching_entity = find_entity_with_components::<&Entity>( - &mut self.entity_iter, - &self.component_type_ids, - )?; + let matching_entity = + find_entity_with_components(&mut self.entity_iter, &self.component_type_ids)?; Some(Comps::from_components(&matching_entity.components)) } } -pub struct QueryComponentMutIter<'world, Comps> -{ - entity_iter: SliceIterMut<'world, Entity>, - component_type_ids: Vec<TypeId>, - comps_pd: PhantomData<Comps>, -} - -impl<'world, Comps> Iterator for QueryComponentMutIter<'world, Comps> -where - Comps: ComponentSequence + 'world, -{ - type Item = Comps::MutRefs<'world>; - - fn next(&mut self) -> Option<Self::Item> - { - let matching_entity = find_entity_with_components::<&mut Entity>( - &mut self.entity_iter, - &self.component_type_ids, - )?; - - Some(Comps::from_components_mut(&mut matching_entity.components)) - } -} - -trait EntityRef -{ - fn components(&self) -> &[Box<dyn Component>]; -} - -impl EntityRef for &Entity -{ - fn components(&self) -> &[Box<dyn Component>] - { - &self.components - } -} - -impl EntityRef for &mut Entity -{ - fn components(&self) -> &[Box<dyn Component>] - { - &self.components - } -} - -fn find_entity_with_components<EntityRefT>( - entity_iter: &mut impl Iterator<Item = EntityRefT>, +fn find_entity_with_components<'world>( + entity_iter: &mut impl Iterator<Item = &'world Entity>, component_type_ids: &[TypeId], -) -> Option<EntityRefT> -where - EntityRefT: EntityRef, +) -> Option<&'world Entity> { // TODO: This is a really dumb and slow way to do this. Refactor the world // to store components in archetypes entity_iter.find(|entity| { - let entity_components: HashSet<_> = entity - .components() + let entity_components = entity + .components .iter() - .map(|component| component.as_ref().type_id()) - .collect(); + .filter_map(|component| Some(component.try_borrow().ok()?.as_ref().type_id())) + .collect::<HashSet<_>>(); if component_type_ids .iter() diff --git a/ecs/src/system.rs b/ecs/src/system.rs index 96f0254..76db2b5 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,7 +1,9 @@ use std::any::Any; +use std::cell::RefMut; use std::convert::Infallible; use std::fmt::Debug; -use std::ptr::addr_of_mut; +use std::ops::{Deref, DerefMut}; +use std::ptr::addr_of; use seq_macro::seq; @@ -14,20 +16,22 @@ pub mod stateful; mod util; -pub trait System<Impl>: 'static +pub trait System<'world, Impl>: 'static { type Input; #[must_use] fn initialize(self, input: Self::Input) -> Self; - fn run(&mut self, world_data: &mut WorldData); + fn run<'this>(&'this self, world_data: &'world WorldData) + where + 'this: 'world; fn into_type_erased(self) -> TypeErased; fn get_local_component_mut<LocalComponent: Component>( - &mut self, - ) -> Option<&mut LocalComponent>; + &self, + ) -> Option<ComponentRefMut<LocalComponent>>; fn set_local_component<LocalComponent: Component>( &mut self, @@ -38,7 +42,7 @@ pub trait System<Impl>: 'static macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<fn(#(TParam~I,)*)> + impl<'world, Func, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*)> for Func where Func: Fn(#(TParam~I,)*) + Copy + 'static, @@ -51,7 +55,9 @@ macro_rules! impl_system { self } - fn run(&mut self, world_data: &mut WorldData) + fn run<'this>(&'this self, world_data: &'world WorldData) + where + 'this: 'world { #( check_params_are_compatible!(I, TParam~I, $c); @@ -60,17 +66,7 @@ macro_rules! impl_system { let func = *self; func(#({ - // SAFETY: All parameters are compatible so this is fine - let this = unsafe { - &mut *addr_of_mut!(*self) - }; - - // SAFETY: All parameters are compatible so this is fine - let world_data = unsafe { - &mut *addr_of_mut!(*world_data) - }; - - TParam~I::new(this, world_data) + TParam~I::new(self, world_data) },)*); } @@ -79,7 +75,19 @@ macro_rules! impl_system { TypeErased { data: Box::new(self), func: Box::new(|data, world_data| { - let me = data.downcast_mut::<Func>().unwrap(); + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let data = unsafe { &*addr_of!(*data) }; + + let me = data + .downcast_ref::<Func>() + .expect("Function downcast failed"); + + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let world_data = unsafe { + &*(world_data as *const WorldData) + }; me.run(world_data); }), @@ -87,8 +95,8 @@ macro_rules! impl_system { } fn get_local_component_mut<LocalComponent: Component>( - &mut self, - ) -> Option<&mut LocalComponent> + &self, + ) -> Option<ComponentRefMut<LocalComponent>> { panic!("System does not have any local components"); } @@ -123,9 +131,16 @@ pub struct TypeErased impl TypeErased { - pub fn run(&mut self, world_data: &mut WorldData) + /// Runs the system. + /// + /// # Safety + /// `world_data` must live at least as long as the [`World`] the system belongs to. + pub unsafe fn run(&self, world_data: &WorldData) { - (self.func)(self.data.as_mut(), world_data); + // You have to dereference for downcasting to work for some reason + let data = &*self.data; + + (self.func)(data, world_data); } } @@ -138,7 +153,7 @@ impl Debug for TypeErased } /// Function in [`TypeErased`] used to run the system. -type TypeErasedFunc = dyn Fn(&mut dyn Any, &mut WorldData); +type TypeErasedFunc = dyn Fn(&dyn Any, &WorldData); /// A parameter to a [`System`]. /// @@ -149,11 +164,14 @@ pub unsafe trait Param<'world> type Input; type Flags; - fn initialize<SystemImpl>(system: &mut impl System<SystemImpl>, input: Self::Input); + fn initialize<SystemImpl>( + system: &mut impl System<'world, SystemImpl>, + input: Self::Input, + ); fn new<SystemImpl>( - system: &'world mut impl System<SystemImpl>, - world_data: &'world mut WorldData, + system: &'world impl System<'world, SystemImpl>, + world_data: &'world WorldData, ) -> Self; fn is_compatible<Other: Param<'world>>() -> bool; @@ -172,3 +190,35 @@ where { type Tuple = Tup::With; } + +#[derive(Debug)] +pub struct ComponentRefMut<'a, ComponentT: Component> +{ + inner: RefMut<'a, ComponentT>, +} + +impl<'a, ComponentT: Component> ComponentRefMut<'a, ComponentT> +{ + pub(crate) fn new(inner: RefMut<'a, ComponentT>) -> Self + { + Self { inner } + } +} + +impl<'a, ComponentT: Component> Deref for ComponentRefMut<'a, ComponentT> +{ + type Target = ComponentT; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<'a, ComponentT: Component> DerefMut for ComponentRefMut<'a, ComponentT> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.inner + } +} diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 3f71000..7ce87fa 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,12 +1,12 @@ -use std::any::{type_name, TypeId}; +use std::any::{type_name, Any, TypeId}; +use std::cell::{RefCell, RefMut}; use std::collections::HashMap; -use std::ptr::addr_of_mut; use seq_macro::seq; use crate::component::Component; use crate::system::util::check_params_are_compatible; -use crate::system::{Into as IntoSystem, Param, System, TypeErased}; +use crate::system::{ComponentRefMut, Into as IntoSystem, Param, System, TypeErased}; use crate::tuple::{ Filter as TupleFilter, FilterExclude as TupleFilterExclude, @@ -20,13 +20,13 @@ use crate::WorldData; pub struct Stateful<Func> { func: Func, - local_components: HashMap<TypeId, Box<dyn Component>>, + local_components: HashMap<TypeId, RefCell<Box<dyn Component>>>, } macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<fn(&'world (), #(TParam~I,)*)> + impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> where Func: Fn(#(TParam~I,)*) + Copy + 'static, @@ -76,7 +76,9 @@ macro_rules! impl_system { self } - fn run(&mut self, world_data: &mut WorldData) + fn run<'this>(&'this self, world_data: &'world WorldData) + where + 'this: 'world { #( check_params_are_compatible!(I, TParam~I, $c); @@ -85,17 +87,7 @@ macro_rules! impl_system { let func = self.func; func(#({ - // SAFETY: All parameters are compatible so this is fine - let this = unsafe { - &mut *addr_of_mut!(*self) - }; - - // SAFETY: All parameters are compatible so this is fine - let world_data = unsafe { - &mut *addr_of_mut!(*world_data) - }; - - TParam~I::new(this, world_data) + TParam~I::new(self, world_data) },)*); } @@ -104,7 +96,17 @@ macro_rules! impl_system { TypeErased { data: Box::new(self), func: Box::new(|data, world_data| { - let me = data.downcast_mut::<Self>().unwrap(); + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let data = unsafe { &*(data as *const dyn Any) }; + + let me = data.downcast_ref::<Self>().unwrap(); + + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let world_data = unsafe { + &*(world_data as *const WorldData) + }; me.run(world_data); }), @@ -112,12 +114,17 @@ macro_rules! impl_system { } fn get_local_component_mut<LocalComponent: Component>( - &mut self, - ) -> Option<&mut LocalComponent> + &self, + ) -> Option<ComponentRefMut<LocalComponent>> { - self.local_components - .get_mut(&TypeId::of::<LocalComponent>())? - .downcast_mut() + let local_component = self.local_components + .get(&TypeId::of::<LocalComponent>())? + .borrow_mut(); + + Some(ComponentRefMut::new(RefMut::filter_map( + local_component, + |local_component| local_component.downcast_mut() + ).ok()?)) } fn set_local_component<LocalComponent: Component>( @@ -126,8 +133,10 @@ macro_rules! impl_system { ) { self.local_components - .insert(TypeId::of::<LocalComponent>(), - Box::new(local_component)); + .insert( + TypeId::of::<LocalComponent>(), + RefCell::new(Box::new(local_component)) + ); } } |