diff --git a/ecs/src/get_disjoint_mut.rs b/ecs/src/get_disjoint_mut.rs new file mode 100644 index 0000000..50824de --- /dev/null +++ b/ecs/src/get_disjoint_mut.rs @@ -0,0 +1,5 @@ +pub trait GetDisjointMut<'a, T: 'a> { + type Error; + + fn get_mut(&'a mut self) -> std::result::Result; +} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 8708cc1..4c92fe0 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,6 +1,7 @@ mod entity; mod entity_object_manager; mod events; +pub mod get_disjoint_mut; mod resources; mod type_map; mod unsafe_component_store; @@ -16,3 +17,4 @@ pub use crate::type_map::{ pub use crate::unsafe_component_store::UnsafeComponentStore; pub use crate::updates::*; pub use crate::world::{World, WorldBuilder}; +pub use update_macros::Resource; diff --git a/ecs/src/resources.rs b/ecs/src/resources.rs index 1f7f662..79b267c 100644 --- a/ecs/src/resources.rs +++ b/ecs/src/resources.rs @@ -3,94 +3,162 @@ use std::{ collections::HashMap, }; +use anyhow::{Error, anyhow}; use utilities::prelude::{remove_life_time, remove_life_time_mut}; -type Untyped = dyn Any + Send + Sync; +use crate::get_disjoint_mut::GetDisjointMut; + +pub trait Resource: Any + Send + Sync {} + +impl dyn Resource { + pub fn downcast_owned(mut self: Box) -> Option { + self.downcast_mut() + .map(|raw| unsafe { *Box::from_raw(raw as *mut T) }) + } + + pub fn downcast_ref(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref() + } + + pub fn downcast_mut(&mut self) -> Option<&mut T> { + (self as &mut dyn Any).downcast_mut() + } +} #[derive(Default)] pub struct Resources { - map: HashMap>, + map: HashMap>, } impl Resources { - pub fn insert(&mut self, value: T) -> Option { + pub fn insert(&mut self, value: T) -> Option { self.map .insert(TypeId::of::(), Box::new(value)) - .map(|any| *Self::downcast_unchecked(any)) + .map(|any| any.downcast_owned()) + .flatten() } - pub fn insert_if_not_exists(&mut self) { + pub fn insert_if_not_exists(&mut self) { if !self.contains::() { self.insert(T::default()); } } - pub fn remove(&mut self) -> Option { + pub fn remove(&mut self) -> Option { self.map .remove(&TypeId::of::()) - .map(|any| *Self::downcast_unchecked(any)) + .map(|any| any.downcast_owned()) + .flatten() } - pub fn get(&self) -> &T { + pub fn get(&self) -> &T { self.get_opt::().unwrap() } - pub fn get_by_type_id(&self, type_id: TypeId) -> &T { + pub fn get_by_type_id(&self, type_id: TypeId) -> &T { self.get_opt_by_type_id(type_id).unwrap() } - pub fn get_unchecked<'a, T: Any + Send + Sync>(&self) -> &'a T { + pub fn get_unchecked<'a, T: Resource>(&self) -> &'a T { unsafe { remove_life_time(self.get::()) } } - pub fn get_opt(&self) -> Option<&T> { + pub fn get_opt(&self) -> Option<&T> { self.get_opt_by_type_id(TypeId::of::()) } - pub fn get_opt_by_type_id(&self, type_id: TypeId) -> Option<&T> { + pub fn get_opt_by_type_id(&self, type_id: TypeId) -> Option<&T> { debug_assert_eq!(type_id, TypeId::of::()); self.map .get(&type_id) - .map(|any| Self::downcast_ref_unchecked(any)) + .map(|any| any.downcast_ref()) + .flatten() } - pub fn get_mut(&mut self) -> &mut T { + pub fn get_mut(&mut self) -> &mut T { self.get_mut_opt::().unwrap() } - pub fn get_mut_by_type_id(&mut self, type_id: TypeId) -> &mut T { + pub fn get_mut_by_type_id(&mut self, type_id: TypeId) -> &mut T { self.get_mut_opt_by_type_id(type_id).unwrap() } - pub fn get_mut_by_type_id_untyped(&mut self, type_id: TypeId) -> &mut Untyped { + pub fn get_mut_by_type_id_untyped(&mut self, type_id: TypeId) -> &mut dyn Resource { self.get_mut_opt_by_type_id_untyped(type_id).unwrap() } - pub fn get_mut_unchecked<'a, T: Any + Send + Sync>(&mut self) -> &'a mut T { + pub fn get_mut_unchecked<'a, T: Resource>(&mut self) -> &'a mut T { unsafe { remove_life_time_mut(self.get_mut::()) } } - pub fn get_mut_opt(&mut self) -> Option<&mut T> { + pub fn get_mut_opt(&mut self) -> Option<&mut T> { self.get_mut_opt_by_type_id(TypeId::of::()) } - pub fn get_mut_opt_by_type_id( - &mut self, - type_id: TypeId, - ) -> Option<&mut T> { + pub fn get_mut_opt_by_type_id(&mut self, type_id: TypeId) -> Option<&mut T> { debug_assert_eq!(type_id, TypeId::of::()); self.map .get_mut(&type_id) - .map(|any| Self::downcast_mut_unchecked(any)) + .map(|any| any.downcast_mut()) + .flatten() } - pub fn get_mut_opt_by_type_id_untyped(&mut self, type_id: TypeId) -> Option<&mut Untyped> { + pub fn get_mut_opt_by_type_id_untyped(&mut self, type_id: TypeId) -> Option<&mut dyn Resource> { self.map.get_mut(&type_id).map(|any| any.as_mut()) } - pub fn contains(&self) -> bool { + pub fn contains(&self) -> bool { self.map.contains_key(&TypeId::of::()) } } + +impl<'a, T> GetDisjointMut<'a, &'a mut T> for Resources +where + T: Resource, +{ + type Error = Error; + + fn get_mut(&'a mut self) -> std::result::Result<&'a mut T, Self::Error> { + self.map + .get_mut(&TypeId::of::()) + .map(|any| any.downcast_mut().unwrap()) + .ok_or_else(|| anyhow!("failed downcasting {}", stringify!(T))) + } +} + +macro_rules! impl_get_disjoint_mut { + ($struct:ident<$($t:ident$(,)?)+>{$error:ident}) => { + impl<'a, $($t,)+> GetDisjointMut<'a, ($(&'a mut $t,)+)> for $struct + where + $( + $t: Resource + 'static, + )+ + { + type Error = $error; + + fn get_mut(&'a mut self) -> std::result::Result<($(&'a mut $t,)+), Self::Error> { + let mut types: std::collections::VecDeque<_> + = self.map.get_disjoint_mut([$(&TypeId::of::<$t>(),)+]).into_iter().collect(); + + Ok(($( + types + .pop_front() + .flatten() + .map(|any| any.downcast_mut().unwrap()) + .ok_or_else(|| anyhow!("failed downcasting {}", stringify!($t)))?, + )+)) + } + } + }; +} + +impl_get_disjoint_mut!(Resources < T, U > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W, X > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W, X, Y > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W, X, Y, Z > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W, X, Y, Z, A > { Error }); +impl_get_disjoint_mut!(Resources < T, U, V, W, X, Y, Z, A, B > { Error }); diff --git a/ecs/src/type_map.rs b/ecs/src/type_map.rs index ce0a28d..57ecb3d 100644 --- a/ecs/src/type_map.rs +++ b/ecs/src/type_map.rs @@ -10,7 +10,7 @@ use std::{ use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::World; +use crate::{World, get_disjoint_mut::GetDisjointMut}; pub trait EntityComponent: Any + Send + Sync { fn enable(&mut self, _world: &mut World) -> Result<()> { @@ -151,15 +151,13 @@ impl TypeMap { } } -pub trait GetTypeMap<'a, T: 'a> { - fn get_mut(&'a mut self) -> std::result::Result; -} - -impl<'a, T> GetTypeMap<'a, &'a mut T> for TypeMap +impl<'a, T> GetDisjointMut<'a, &'a mut T> for TypeMap where T: EntityComponent + ComponentDebug, { - fn get_mut(&'a mut self) -> std::result::Result<&'a mut T, ComponentNotFoundError> { + type Error = ComponentNotFoundError; + + fn get_mut(&'a mut self) -> std::result::Result<&'a mut T, Self::Error> { self.map .get_mut(&TypeId::of::()) .map(|any| any.downcast_mut().unwrap()) @@ -167,15 +165,17 @@ where } } -macro_rules! impl_get_type_map { - (<$($t:ident$(,)?)+>) => { - impl<'a, $($t,)+> GetTypeMap<'a, ($(&'a mut $t,)+)> for TypeMap +macro_rules! impl_get_disjoint_mut { + ($struct:ident<$($t:ident$(,)?)+>{$error:ident}) => { + impl<'a, $($t,)+> GetDisjointMut<'a, ($(&'a mut $t,)+)> for $struct where $( $t: EntityComponent + ComponentDebug + 'static, )+ { - fn get_mut(&'a mut self) -> std::result::Result<($(&'a mut $t,)+), ComponentNotFoundError> { + type Error = $error; + + fn get_mut(&'a mut self) -> std::result::Result<($(&'a mut $t,)+), Self::Error> { let mut types: std::collections::VecDeque<_> = self.map.get_disjoint_mut([$(&TypeId::of::<$t>(),)+]).into_iter().collect(); @@ -191,14 +191,16 @@ macro_rules! impl_get_type_map { }; } -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); -impl_get_type_map!(); +impl_get_disjoint_mut!(TypeMap < T, U > { ComponentNotFoundError }); +impl_get_disjoint_mut!(TypeMap < T, U, V > { ComponentNotFoundError }); +impl_get_disjoint_mut!(TypeMap < T, U, V, W > { ComponentNotFoundError }); +impl_get_disjoint_mut!(TypeMap < T, U, V, W, X > { ComponentNotFoundError }); +impl_get_disjoint_mut!(TypeMap < T, U, V, W, X, Y > { ComponentNotFoundError }); +impl_get_disjoint_mut!(TypeMap < T, U, V, W, X, Y, Z > { ComponentNotFoundError }); +#[rustfmt::skip] +impl_get_disjoint_mut!(TypeMap < T, U, V, W, X, Y, Z, A > { ComponentNotFoundError }); +#[rustfmt::skip] +impl_get_disjoint_mut!(TypeMap < T, U, V, W, X, Y, Z, A, B > { ComponentNotFoundError }); #[derive(Debug)] pub enum ComponentRequestType { diff --git a/update_macros/src/lib.rs b/update_macros/src/lib.rs index 097017f..2ea7edb 100644 --- a/update_macros/src/lib.rs +++ b/update_macros/src/lib.rs @@ -2,10 +2,10 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::{ + DeriveInput, Ident, LitInt, Result, parse::{Parse, ParseStream}, parse_macro_input, token::Comma, - Ident, LitInt, Result, }; struct InputInfo { @@ -115,3 +115,14 @@ pub fn implement_pair_update(input: TokenStream) -> TokenStream { )* }) } + +#[proc_macro_derive(Resource)] +pub fn derive_resource(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let implementor = ast.ident; + + TokenStream::from(quote! { + impl Resource for #implementor {} + }) +}