use super::{FetchResource, ResourceQuery}; use crate::system::SystemId; use bevy_hecs::{Archetype, Ref, RefMut, TypeInfo}; use core::any::TypeId; use std::{collections::HashMap, ptr::NonNull}; /// A Resource type pub trait Resource: Send + Sync + 'static {} impl Resource for T {} pub(crate) struct ResourceData { archetype: Archetype, default_index: Option, system_id_to_archetype_index: HashMap, } pub enum ResourceIndex { Global, System(SystemId), } /// A collection of resource instances identified by their type. #[derive(Default)] pub struct Resources { pub(crate) resource_data: HashMap, } impl Resources { pub fn insert(&mut self, resource: T) { self.insert_resource(resource, ResourceIndex::Global); } pub fn contains(&self) -> bool { self.get_resource::(ResourceIndex::Global).is_some() } pub fn get(&self) -> Option> { self.get_resource(ResourceIndex::Global) } pub fn get_mut(&self) -> Option> { self.get_resource_mut(ResourceIndex::Global) } pub fn get_local<'a, T: Resource>(&'a self, id: SystemId) -> Option> { self.get_resource(ResourceIndex::System(id)) } pub fn get_local_mut<'a, T: Resource>(&'a self, id: SystemId) -> Option> { self.get_resource_mut(ResourceIndex::System(id)) } pub fn insert_local(&mut self, id: SystemId, resource: T) { self.insert_resource(resource, ResourceIndex::System(id)) } fn insert_resource(&mut self, mut resource: T, resource_index: ResourceIndex) { let type_id = TypeId::of::(); let data = self.resource_data.entry(type_id).or_insert_with(|| { let mut types = Vec::new(); types.push(TypeInfo::of::()); ResourceData { archetype: Archetype::new(types), default_index: None, system_id_to_archetype_index: HashMap::new(), } }); let archetype = &mut data.archetype; let mut added = false; let index = match resource_index { ResourceIndex::Global => *data.default_index.get_or_insert_with(|| { added = true; archetype.len() }), ResourceIndex::System(id) => *data .system_id_to_archetype_index .entry(id.0) .or_insert_with(|| { added = true; archetype.len() }), }; if index == archetype.len() { unsafe { archetype.allocate(index) }; } else if index > archetype.len() { panic!("attempted to access index beyond 'current_capacity + 1'") } unsafe { let resource_ptr = (&mut resource as *mut T).cast::(); archetype.put_dynamic( resource_ptr, type_id, core::mem::size_of::(), index, added, ); std::mem::forget(resource); } } fn get_resource(&self, resource_index: ResourceIndex) -> Option> { self.resource_data .get(&TypeId::of::()) .and_then(|data| unsafe { let index = match resource_index { ResourceIndex::Global => data.default_index?, ResourceIndex::System(id) => *data.system_id_to_archetype_index.get(&id.0)?, }; Ref::new(&data.archetype, index).ok() }) } fn get_resource_mut( &self, resource_index: ResourceIndex, ) -> Option> { self.resource_data .get(&TypeId::of::()) .and_then(|data| unsafe { let index = match resource_index { ResourceIndex::Global => data.default_index?, ResourceIndex::System(id) => *data.system_id_to_archetype_index.get(&id.0)?, }; RefMut::new(&data.archetype, index).ok() }) } pub fn query(&self) -> ::Item { unsafe { Q::Fetch::get(&self, None) } } pub fn query_system( &self, id: SystemId, ) -> ::Item { unsafe { Q::Fetch::get(&self, Some(id)) } } #[inline] pub unsafe fn get_unsafe_ref(&self, resource_index: ResourceIndex) -> NonNull { self.resource_data .get(&TypeId::of::()) .and_then(|data| { let index = match resource_index { ResourceIndex::Global => data.default_index?, ResourceIndex::System(id) => { data.system_id_to_archetype_index.get(&id.0).cloned()? } }; Some(NonNull::new_unchecked( data.archetype.get::()?.as_ptr().add(index as usize), )) }) .unwrap_or_else(|| panic!("Resource does not exist {}", std::any::type_name::())) } pub fn borrow(&self) { if let Some(data) = self.resource_data.get(&TypeId::of::()) { data.archetype.borrow::(); } } pub fn release(&self) { if let Some(data) = self.resource_data.get(&TypeId::of::()) { data.archetype.release::(); } } pub fn borrow_mut(&self) { if let Some(data) = self.resource_data.get(&TypeId::of::()) { data.archetype.borrow_mut::(); } } pub fn release_mut(&self) { if let Some(data) = self.resource_data.get(&TypeId::of::()) { data.archetype.release_mut::(); } } } unsafe impl Send for Resources {} unsafe impl Sync for Resources {} /// Creates `Self` using data from the `Resources` collection pub trait FromResources { /// Creates `Self` using data from the `Resources` collection fn from_resources(resources: &Resources) -> Self; } impl FromResources for T where T: Default, { fn from_resources(_resources: &Resources) -> Self { Self::default() } } #[cfg(test)] mod tests { use super::Resources; use crate::system::SystemId; #[test] fn resource() { let mut resources = Resources::default(); assert!(resources.get::().is_none()); resources.insert(123); assert_eq!(*resources.get::().expect("resource exists"), 123); resources.insert(456.0); assert_eq!(*resources.get::().expect("resource exists"), 456.0); resources.insert(789.0); assert_eq!(*resources.get::().expect("resource exists"), 789.0); { let mut value = resources.get_mut::().expect("resource exists"); assert_eq!(*value, 789.0); *value = -1.0; } assert_eq!(*resources.get::().expect("resource exists"), -1.0); assert!(resources.get_local::(SystemId(0)).is_none()); resources.insert_local(SystemId(0), 111); assert_eq!( *resources .get_local::(SystemId(0)) .expect("resource exists"), 111 ); assert_eq!(*resources.get::().expect("resource exists"), 123); resources.insert_local(SystemId(0), 222); assert_eq!( *resources .get_local::(SystemId(0)) .expect("resource exists"), 222 ); assert_eq!(*resources.get::().expect("resource exists"), 123); } #[test] #[should_panic(expected = "i32 already borrowed")] fn resource_double_mut_panic() { let mut resources = Resources::default(); resources.insert(123); let _x = resources.get_mut::(); let _y = resources.get_mut::(); } }