diff options
| -rw-r--r-- | engine-ecs-macros/src/lib.rs | 175 | ||||
| -rw-r--r-- | engine-ecs/src/component.rs | 17 | ||||
| -rw-r--r-- | engine-ecs/src/lib.rs | 87 | ||||
| -rw-r--r-- | engine-ecs/src/sole.rs | 57 |
4 files changed, 142 insertions, 194 deletions
diff --git a/engine-ecs-macros/src/lib.rs b/engine-ecs-macros/src/lib.rs index a304706..106d350 100644 --- a/engine-ecs-macros/src/lib.rs +++ b/engine-ecs-macros/src/lib.rs @@ -6,7 +6,6 @@ use quote::{format_ident, quote, ToTokens}; use syn::spanned::Spanned; use syn::{ parse, - Attribute, Generics, Ident, Item, @@ -148,6 +147,17 @@ pub fn component_derive(input: TokenStream) -> TokenStream #where_clause { } + + impl #ecs_path::component::IntoParts for #item_ident + { + fn into_parts(self) -> #ecs_path::component::Parts + { + #ecs_path::component::Parts::builder() + .name(self.name()) + .type_reflection(<Self as Component>::type_reflection()) + .build(Self::id(), self) + } + } } } .into() @@ -156,50 +166,118 @@ pub fn component_derive(input: TokenStream) -> TokenStream /// Generates a `Sole` implementation. /// /// # Panics -/// Will panic if not attributed to a type item or if parsing the user crate's -/// `Cargo.toml` file fails. -#[proc_macro_derive(Sole, attributes(sole))] +/// Will panic if: +/// - Not attributed to a type item +/// - The attributed-to type item is generic +/// - If parsing the user crate's `Cargo.toml` file fails. +#[proc_macro_derive(Sole)] pub fn sole_derive(input: TokenStream) -> TokenStream { let item: TypeItem = parse::<Item>(input).unwrap().try_into().unwrap(); let item_ident = item.ident(); - let sole_attr = item.attribute::<SoleAttribute>("sole").unwrap_or_default(); + let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs)); - let drop_last = sole_attr.drop_last; + assert!( + item.generics().params.is_empty(), + "Generic types are not supported as sole components" + ); - let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl(); + let id_var_ident = format_ident!("{}_ID", item_ident.to_string().to_uppercase()); - let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs)); + let id_var = quote! { + static #id_var_ident: LazyLock<Uid> = LazyLock::new(|| { + Uid::new_unique() + }); + }; + + let mod_ident = format_ident!( + "__ecs_priv_sole_impl_{}", + item_ident.to_string().to_lowercase() + ); quote! { - impl #impl_generics #ecs_path::sole::Sole for #item_ident #type_generics - #where_clause - { - fn drop_last(&self) -> bool - { - #drop_last - } + mod #mod_ident { + use ::std::any::{Any, TypeId}; + use ::std::sync::{LazyLock, Mutex}; + + use #ecs_path::sole::Sole; + use #ecs_path::uid::Uid; + use #ecs_path::system::Input as SystemInput; + + use super::*; + + #id_var - fn as_any_mut(&mut self) -> &mut dyn std::any::Any + impl Sole for #item_ident { - self + fn id() -> Uid + { + *#id_var_ident + } + + fn type_reflection() -> Option<&'static #ecs_path::reflection::Type> + { + struct SpecializationTarget<T>(std::marker::PhantomData<T>); + + trait HasReflection + { + fn type_reflection(&self) + -> Option<&'static #ecs_path::reflection::Type>; + } + + trait NoReflection + { + fn type_reflection(&self) + -> Option<&'static #ecs_path::reflection::Type>; + } + + impl<T> NoReflection for &SpecializationTarget<T> + { + fn type_reflection(&self) + -> Option<&'static #ecs_path::reflection::Type> + { + None + } + } + + impl<T> HasReflection for SpecializationTarget<T> + where + T: #ecs_path::reflection::Reflection + { + fn type_reflection(&self) + -> Option<&'static #ecs_path::reflection::Type> + { + Some(T::type_reflection()) + } + } + + (&SpecializationTarget::<Self>(std::marker::PhantomData)) + .type_reflection() + } + + fn name(&self) -> &'static str + { + std::any::type_name::<Self>() + } } - fn as_any(&self) -> &dyn std::any::Any + impl #ecs_path::component::IntoParts for #item_ident { - self + fn into_parts(self) -> #ecs_path::component::Parts + { + #ecs_path::component::Parts::builder() + .name(self.name()) + .type_reflection(<Self as Sole>::type_reflection()) + .build(Self::id(), self) + } } } } .into() } -trait FromAttribute: Sized -{ - fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>; -} enum TypeItem { @@ -219,27 +297,6 @@ impl TypeItem } } - fn attribute<Attr: FromAttribute>(&self, attr_ident: &str) -> Option<Attr> - { - let item_attrs = match &self { - Self::Struct(struct_item) => &struct_item.attrs, - &Self::Enum(enum_item) => &enum_item.attrs, - &Self::Union(union_item) => &union_item.attrs, - }; - - let mut attr: Option<&Attribute> = None; - - for item_attr in item_attrs { - assert!(attr.is_none(), "Expected only one {attr_ident} attribute"); - - if item_attr.path().get_ident()? == attr_ident { - attr = Some(item_attr); - } - } - - Some(Attr::from_attribute(attr?).unwrap()) - } - fn generics(&self) -> &Generics { match self { @@ -280,36 +337,6 @@ impl ToTokens for TypeItem } } -#[derive(Debug, Default)] -struct SoleAttribute -{ - drop_last: bool, -} - -impl FromAttribute for SoleAttribute -{ - fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error> - { - let mut drop_last = false; - - attribute.parse_nested_meta(|meta| { - if meta - .path - .get_ident() - .is_some_and(|flag| flag == "drop_last") - { - drop_last = true; - - return Ok(()); - } - - Err(meta.error("Unrecognized token")) - })?; - - Ok(Self { drop_last }) - } -} - fn find_engine_ecs_crate_path() -> Option<Path> { let cargo_manifest_dir = FsPathBuf::from(std::env::var("CARGO_MANIFEST_DIR").ok()?); diff --git a/engine-ecs/src/component.rs b/engine-ecs/src/component.rs index 157d79c..25ec101 100644 --- a/engine-ecs/src/component.rs +++ b/engine-ecs/src/component.rs @@ -1,4 +1,4 @@ -use std::any::{type_name, Any}; +use std::any::Any; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; @@ -24,7 +24,7 @@ pub mod local; pub(crate) mod storage; -pub trait Component: SystemInput + Any +pub trait Component: SystemInput + Any + IntoParts { /// Returns the ID of this component. fn id() -> Uid @@ -286,19 +286,6 @@ pub trait IntoParts fn into_parts(self) -> Parts; } -impl<ComponentT> IntoParts for ComponentT -where - ComponentT: Component, -{ - fn into_parts(self) -> Parts - { - Parts::builder() - .name(type_name::<Self>()) - .type_reflection(Self::type_reflection()) - .build(Self::id(), self) - } -} - /// The parts of a component. #[derive(Debug)] #[non_exhaustive] diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs index 1c157e3..25866ca 100644 --- a/engine-ecs/src/lib.rs +++ b/engine-ecs/src/lib.rs @@ -1,14 +1,10 @@ #![deny(clippy::all, clippy::pedantic)] -use std::any::{type_name, Any, TypeId}; +use std::any::Any; use std::fmt::Debug; use std::hint::cold_path; -use std::mem::ManuallyDrop; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; - -use hashbrown::HashMap; use crate::actions::Action; use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; @@ -156,7 +152,7 @@ impl World entity_decl.create(self); } - /// Adds a globally shared singleton value. + /// Adds a singleton. /// /// # Errors /// Returns `Err` if this [`Sole`] has already been added. @@ -164,7 +160,9 @@ impl World where SoleT: Sole, { - self.data.sole_storage.insert(sole) + self.create_ent(SoleT::id(), [sole.into_parts()]); + + Ok(()) } pub fn register_observer<'this, SystemImpl, ObserverT>( @@ -253,7 +251,9 @@ impl World pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>> { - Some(Single::new(self.data.sole_storage.get::<SoleT>()?)) + let ent = self.get_entity(SoleT::id())?; + + Some(Single::new(ent.get_with_id_mut(SoleT::id())?)) } pub fn event_submitter(&self) -> EventSubmitter<'_> @@ -701,7 +701,6 @@ pub enum StepResult struct WorldData { component_storage: ComponentStorage, - sole_storage: SoleStorage, action_queue: Rc<ActionQueue>, new_events: Lock<NewEvents>, } @@ -765,73 +764,3 @@ impl ActionQueue #[error("Sole {0} already exists")] pub struct SoleAlreadyExistsError(pub &'static str); -#[derive(Debug)] -struct StoredSole -{ - sole: Arc<Lock<Box<dyn Sole>>>, - drop_last: bool, -} - -#[derive(Debug, Default)] -struct SoleStorage -{ - storage: HashMap<TypeId, ManuallyDrop<StoredSole>>, -} - -impl SoleStorage -{ - fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>> - { - self.storage - .get(&TypeId::of::<SoleT>()) - .map(|sole| &sole.sole) - } - - fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - { - let sole_type_id = TypeId::of::<SoleT>(); - - if self.storage.contains_key(&sole_type_id) { - return Err(SoleAlreadyExistsError(type_name::<SoleT>())); - } - - let drop_last = sole.drop_last(); - - // TODO: Reconsider this maybe? - #[allow(clippy::arc_with_non_send_sync)] - self.storage.insert( - sole_type_id, - ManuallyDrop::new(StoredSole { - sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())), - drop_last, - }), - ); - - Ok(()) - } -} - -impl Drop for SoleStorage -{ - fn drop(&mut self) - { - let mut soles_to_drop_last = Vec::new(); - - for sole in self.storage.values_mut() { - if sole.drop_last { - soles_to_drop_last.push(sole); - continue; - } - - unsafe { - ManuallyDrop::drop(sole); - } - } - - for sole in &mut soles_to_drop_last { - unsafe { - ManuallyDrop::drop(sole); - } - } - } -} diff --git a/engine-ecs/src/sole.rs b/engine-ecs/src/sole.rs index 9e27fee..9409bcb 100644 --- a/engine-ecs/src/sole.rs +++ b/engine-ecs/src/sole.rs @@ -1,33 +1,43 @@ use std::any::{type_name, Any}; use std::fmt::Debug; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use std::sync::Arc; -use crate::lock::{Lock, WriteGuard}; +use crate::uid::Uid; +use crate::component::HandleMut as ComponentHandleMut; +use crate::component::IntoParts as IntoComponentParts; use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::World; /// A type which has a single instance and is shared globally. -pub trait Sole: Any +pub trait Sole: Any + IntoComponentParts { - fn drop_last(&self) -> bool; + fn id() -> Uid + where + Self: Sized; - fn as_any_mut(&mut self) -> &mut dyn Any; + fn type_reflection() -> Option<&'static crate::reflection::Type> + where + Self: Sized; - fn as_any(&self) -> &dyn Any; + /// Returns the name of this component. + fn name(&self) -> &'static str; } impl dyn Sole { pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real> { - self.as_any_mut().downcast_mut() + (self as &mut dyn Any).downcast_mut() } pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real> { - self.as_any().downcast_ref() + (self as &dyn Any).downcast_ref() + } + + pub fn is<Other: 'static>(&self) -> bool + { + (self as &dyn Any).is::<Other>() } } @@ -43,25 +53,17 @@ impl Debug for dyn Sole #[derive(Debug)] pub struct Single<'world, SoleT: Sole> { - sole: WriteGuard<'world, Box<dyn Sole>>, - _ph: PhantomData<SoleT>, + sole: ComponentHandleMut<'world, SoleT>, } + impl<'world, SoleT> Single<'world, SoleT> where SoleT: Sole, { - pub(crate) fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self + pub(crate) fn new(sole: ComponentHandleMut<'world, SoleT>) -> Self { - Self { - sole: sole.write_nonblock().unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock to single component {}", - type_name::<SoleT>() - ) - }), - _ph: PhantomData, - } + Self { sole } } } @@ -73,9 +75,12 @@ where fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { - let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| { - panic!("Sole {} was not found in world", type_name::<SoleT>()) - }); + let sole = world + .get_entity(SoleT::id()) + .and_then(|ent| ent.get_with_id_mut::<SoleT>(SoleT::id())) + .unwrap_or_else(|| { + panic!("Sole component {} was not found in world", type_name::<SoleT>()) + }); Self::new(sole) } @@ -89,7 +94,7 @@ where fn deref(&self) -> &Self::Target { - self.sole.downcast_ref().unwrap() + &*self.sole } } @@ -99,6 +104,6 @@ where { fn deref_mut(&mut self) -> &mut Self::Target { - self.sole.downcast_mut().unwrap() + &mut *self.sole } } |
