| @@ -0,0 +1,47 @@ | |||||
| use crate::{access::WriteStorage, component::Component, system::SystemData, world::World}; | |||||
| use super::Entity; | |||||
| pub struct Builder<'a> { | |||||
| world: &'a World, | |||||
| entity: Entity, | |||||
| built: bool, | |||||
| } | |||||
| impl<'a> Builder<'a> { | |||||
| pub fn new(world: &'a World) -> Self { | |||||
| let entity = world.entities_mut().allocate(); | |||||
| Self { | |||||
| world, | |||||
| entity, | |||||
| built: false, | |||||
| } | |||||
| } | |||||
| #[inline] | |||||
| pub fn with<T: Component>(self, c: T) -> Self { | |||||
| { | |||||
| let mut storage = WriteStorage::<T>::fetch(&self.world); | |||||
| storage.insert(self.entity, c).unwrap(); | |||||
| } | |||||
| self | |||||
| } | |||||
| #[inline] | |||||
| pub fn build(mut self) -> Entity { | |||||
| self.built = true; | |||||
| self.entity | |||||
| } | |||||
| } | |||||
| impl Drop for Builder<'_> { | |||||
| fn drop(&mut self) { | |||||
| if !self.built { | |||||
| self.world.entities_mut().kill(self.entity); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,53 @@ | |||||
| use hibitset::BitSet; | |||||
| use super::Entity; | |||||
| #[derive(Default)] | |||||
| pub struct Entities { | |||||
| alive: BitSet, | |||||
| cache: Vec<u32>, | |||||
| generations: Vec<u32>, | |||||
| } | |||||
| impl Entities { | |||||
| pub fn is_alive(&self, entity: Entity) -> bool { | |||||
| let i = entity.index(); | |||||
| let g = entity.generation(); | |||||
| self.alive.contains(i) && self.generations.get(i as usize) == Some(&g) | |||||
| } | |||||
| pub fn allocate(&mut self) -> Entity { | |||||
| let i = match self.cache.pop() { | |||||
| Some(i) => i, | |||||
| None => { | |||||
| let i = self.generations.len() as u32; | |||||
| let c = i.checked_add(1).expect("No entity left to allocate"); | |||||
| self.generations.resize(c as usize, 0); | |||||
| i | |||||
| } | |||||
| }; | |||||
| let g = self.generations[i as usize].wrapping_add(1); | |||||
| self.generations[i as usize] = g; | |||||
| self.alive.add(i); | |||||
| Entity::from_parts(i, g) | |||||
| } | |||||
| pub fn kill(&mut self, entity: Entity) -> bool { | |||||
| if self.is_alive(entity) { | |||||
| let i = entity.index(); | |||||
| self.alive.remove(i); | |||||
| self.cache.push(i); | |||||
| true | |||||
| } else { | |||||
| false | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,99 @@ | |||||
| use std::cmp::Ordering; | |||||
| use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; | |||||
| use std::hash::{Hash, Hasher}; | |||||
| #[derive(Clone, Copy)] | |||||
| pub struct Entity(EntityRaw); | |||||
| pub type Index = u32; | |||||
| pub type Generation = u32; | |||||
| #[repr(C, packed)] | |||||
| #[derive(Clone, Copy)] | |||||
| union EntityRaw { | |||||
| id: u64, | |||||
| data: EntityData, | |||||
| } | |||||
| #[repr(C, packed)] | |||||
| #[derive(Clone, Copy)] | |||||
| struct EntityData { | |||||
| index: Index, | |||||
| generation: Generation, | |||||
| } | |||||
| impl Entity { | |||||
| pub fn from_id(id: u64) -> Self { | |||||
| Self(EntityRaw { id }) | |||||
| } | |||||
| pub fn from_parts(index: Index, generation: Generation) -> Self { | |||||
| Self(EntityRaw { | |||||
| data: EntityData { index, generation }, | |||||
| }) | |||||
| } | |||||
| #[inline] | |||||
| pub fn id(&self) -> u64 { | |||||
| unsafe { self.0.id } | |||||
| } | |||||
| #[inline] | |||||
| pub fn index(&self) -> Index { | |||||
| unsafe { self.0.data.index } | |||||
| } | |||||
| #[inline] | |||||
| pub fn generation(&self) -> Generation { | |||||
| unsafe { self.0.data.generation } | |||||
| } | |||||
| } | |||||
| impl Display for Entity { | |||||
| fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { | |||||
| write!(f, "{:08X}", self.id()) | |||||
| } | |||||
| } | |||||
| impl Debug for Entity { | |||||
| fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { | |||||
| write!(f, "{:08X}", self.id()) | |||||
| } | |||||
| } | |||||
| impl Hash for Entity { | |||||
| fn hash<H: Hasher>(&self, state: &mut H) { | |||||
| self.index().hash(state); | |||||
| self.generation().hash(state); | |||||
| } | |||||
| } | |||||
| impl Eq for Entity {} | |||||
| impl PartialEq<Entity> for Entity { | |||||
| fn eq(&self, other: &Entity) -> bool { | |||||
| self.id() == other.id() | |||||
| } | |||||
| } | |||||
| impl Ord for Entity { | |||||
| fn cmp(&self, other: &Self) -> Ordering { | |||||
| if self.generation() < other.generation() { | |||||
| Ordering::Less | |||||
| } else if self.generation() > other.generation() { | |||||
| Ordering::Greater | |||||
| } else if self.index() < other.index() { | |||||
| Ordering::Less | |||||
| } else if self.index() > other.index() { | |||||
| Ordering::Greater | |||||
| } else { | |||||
| Ordering::Equal | |||||
| } | |||||
| } | |||||
| } | |||||
| impl PartialOrd for Entity { | |||||
| fn partial_cmp(&self, other: &Entity) -> Option<Ordering> { | |||||
| Some(Ord::cmp(self, other)) | |||||
| } | |||||
| } | |||||
| @@ -1,2 +1,8 @@ | |||||
| #[derive(Default)] | |||||
| pub struct Entities; | |||||
| pub mod builder; | |||||
| pub mod entities; | |||||
| #[allow(clippy::module_inception)] | |||||
| pub mod entity; | |||||
| pub use builder::Builder; | |||||
| pub use entities::Entities; | |||||
| pub use entity::{Entity, Generation, Index}; | |||||
| @@ -0,0 +1,9 @@ | |||||
| use thiserror::Error; | |||||
| use crate::entity::Entity; | |||||
| #[derive(Error, Debug)] | |||||
| pub enum Error { | |||||
| #[error("Entity is not alive: {0}!")] | |||||
| EntityIsNotAlive(Entity), | |||||
| } | |||||
| @@ -1,6 +1,7 @@ | |||||
| pub mod access; | pub mod access; | ||||
| pub mod component; | pub mod component; | ||||
| pub mod entity; | pub mod entity; | ||||
| pub mod error; | |||||
| pub mod misc; | pub mod misc; | ||||
| pub mod resource; | pub mod resource; | ||||
| pub mod storage; | pub mod storage; | ||||
| @@ -4,13 +4,6 @@ use async_ecs::{VecStorage, World}; | |||||
| use async_ecs_derive::Component; | use async_ecs_derive::Component; | ||||
| use rand::random; | use rand::random; | ||||
| /* | |||||
| use std::time::{Duration, Instant}; | |||||
| use async_ecs::Resources; | |||||
| */ | |||||
| fn main() { | fn main() { | ||||
| env_logger::builder() | env_logger::builder() | ||||
| .filter_level(log::LevelFilter::Info) | .filter_level(log::LevelFilter::Info) | ||||
| @@ -24,7 +17,6 @@ fn main() { | |||||
| world.register_component::<Velocity>(); | world.register_component::<Velocity>(); | ||||
| world.register_component::<Acceleration>(); | world.register_component::<Acceleration>(); | ||||
| /* | |||||
| for _ in 0..ENTITY_COUNT { | for _ in 0..ENTITY_COUNT { | ||||
| world | world | ||||
| .create_entity() | .create_entity() | ||||
| @@ -36,6 +28,7 @@ fn main() { | |||||
| info!("World initialized!"); | info!("World initialized!"); | ||||
| /* | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(Move, "move", &[]) | .with(Move, "move", &[]) | ||||
| .with(Accelerate, "accelerate", &["move"]) | .with(Accelerate, "accelerate", &["move"]) | ||||
| @@ -65,7 +58,7 @@ fn main() { | |||||
| */ | */ | ||||
| } | } | ||||
| const ENTITY_COUNT: usize = 3_000_000; | |||||
| const ENTITY_COUNT: usize = 100_000; | |||||
| const REPEAT_COUNT: u32 = 100; | const REPEAT_COUNT: u32 = 100; | ||||
| #[derive(Debug, Component)] | #[derive(Debug, Component)] | ||||
| @@ -33,9 +33,6 @@ where | |||||
| let inner = self.inner.or_insert_with(move || Cell::new(Box::new(f()))); | let inner = self.inner.or_insert_with(move || Cell::new(Box::new(f()))); | ||||
| let inner = inner.borrow_mut().map(Box::as_mut); | let inner = inner.borrow_mut().map(Box::as_mut); | ||||
| RefMut { | |||||
| inner, | |||||
| phantom: PhantomData, | |||||
| } | |||||
| RefMut::new(inner) | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,137 +1,18 @@ | |||||
| pub mod cell; | pub mod cell; | ||||
| pub mod entry; | pub mod entry; | ||||
| pub mod resources; | |||||
| pub use resources::{Ref, RefMut, Resources}; | |||||
| use std::any::TypeId; | use std::any::TypeId; | ||||
| use std::marker::PhantomData; | |||||
| use std::ops::{Deref, DerefMut}; | |||||
| use hashbrown::HashMap; | |||||
| use mopa::Any; | use mopa::Any; | ||||
| use cell::{Cell, Ref as CellRef, RefMut as CellRefMut}; | |||||
| use entry::Entry; | |||||
| #[derive(Default)] | |||||
| pub struct Resources { | |||||
| resources: HashMap<ResourceId, Cell<Box<dyn Resource>>>, | |||||
| } | |||||
| pub trait Resource: Any + Send + Sync + 'static {} | |||||
| #[derive(Debug, Hash, Eq, PartialEq)] | #[derive(Debug, Hash, Eq, PartialEq)] | ||||
| pub struct ResourceId(TypeId); | pub struct ResourceId(TypeId); | ||||
| pub trait Resource: Any + Send + Sync + 'static {} | |||||
| pub struct Ref<'a, R: 'a> { | |||||
| inner: CellRef<'a, dyn Resource>, | |||||
| phantom: PhantomData<&'a R>, | |||||
| } | |||||
| pub struct RefMut<'a, R: 'a> { | |||||
| inner: CellRefMut<'a, dyn Resource>, | |||||
| phantom: PhantomData<&'a R>, | |||||
| } | |||||
| macro_rules! fetch_panic { | |||||
| () => {{ | |||||
| panic!( | |||||
| "\ | |||||
| Tried to fetch resource from the resources map, but the resource does not exist.\n\ | |||||
| \n\ | |||||
| Resource: `{resource_name_full}`\n\ | |||||
| \n\ | |||||
| You may ensure the resource exists!\ | |||||
| ", | |||||
| resource_name_full = std::any::type_name::<R>(), | |||||
| ) | |||||
| }}; | |||||
| } | |||||
| /* Resources */ | |||||
| impl Resources { | |||||
| pub fn entry<R>(&mut self) -> Entry<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| Entry::new(self.resources.entry(ResourceId::new::<R>())) | |||||
| } | |||||
| pub fn insert<R>(&mut self, r: R) | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources | |||||
| .insert(ResourceId::new::<R>(), Cell::new(Box::new(r))); | |||||
| } | |||||
| pub fn remove<R>(&mut self) -> Option<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources | |||||
| .remove(&ResourceId::new::<R>()) | |||||
| .map(Cell::into_inner) | |||||
| .map(|x: Box<dyn Resource>| x.downcast()) | |||||
| .map(|x: Result<Box<R>, _>| x.ok().unwrap()) | |||||
| .map(|x| *x) | |||||
| } | |||||
| pub fn contains<R>(&self) -> bool | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.contains_key(&ResourceId::new::<R>()) | |||||
| } | |||||
| pub fn borrow<R>(&self) -> Ref<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.try_borrow().unwrap_or_else(|| fetch_panic!()) | |||||
| } | |||||
| pub fn try_borrow<R>(&self) -> Option<Ref<R>> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.get(&ResourceId::new::<R>()).map(|r| Ref { | |||||
| inner: CellRef::map(r.borrow(), Box::as_ref), | |||||
| phantom: PhantomData, | |||||
| }) | |||||
| } | |||||
| pub fn borrow_mut<R>(&self) -> RefMut<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.try_borrow_mut().unwrap_or_else(|| fetch_panic!()) | |||||
| } | |||||
| pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut { | |||||
| inner: r.borrow_mut().map(Box::as_mut), | |||||
| phantom: PhantomData, | |||||
| }) | |||||
| } | |||||
| pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> { | |||||
| self.get_resource_mut(ResourceId::new::<R>()) | |||||
| .map(|res| res.downcast_mut().unwrap()) | |||||
| } | |||||
| pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> { | |||||
| self.resources | |||||
| .get_mut(&id) | |||||
| .map(Cell::get_mut) | |||||
| .map(Box::as_mut) | |||||
| } | |||||
| } | |||||
| /* ResourceId */ | |||||
| impl ResourceId { | impl ResourceId { | ||||
| pub fn new<R>() -> Self | pub fn new<R>() -> Self | ||||
| where | where | ||||
| @@ -140,116 +21,3 @@ impl ResourceId { | |||||
| Self(TypeId::of::<R>()) | Self(TypeId::of::<R>()) | ||||
| } | } | ||||
| } | } | ||||
| /* Resource */ | |||||
| impl<T> Resource for T where T: Any + Send + Sync {} | |||||
| mod __resource_mopafy_scope { | |||||
| #![allow(clippy::all)] | |||||
| use mopa::mopafy; | |||||
| use super::Resource; | |||||
| mopafy!(Resource); | |||||
| } | |||||
| /* Ref */ | |||||
| impl<'a, R> Deref for Ref<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| type Target = R; | |||||
| fn deref(&self) -> &R { | |||||
| unsafe { self.inner.downcast_ref_unchecked() } | |||||
| } | |||||
| } | |||||
| impl<'a, R> Clone for Ref<'a, R> { | |||||
| fn clone(&self) -> Self { | |||||
| Ref { | |||||
| inner: self.inner.clone(), | |||||
| phantom: PhantomData, | |||||
| } | |||||
| } | |||||
| } | |||||
| /* RefMut */ | |||||
| impl<'a, R> Deref for RefMut<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| type Target = R; | |||||
| fn deref(&self) -> &R { | |||||
| unsafe { self.inner.downcast_ref_unchecked() } | |||||
| } | |||||
| } | |||||
| impl<'a, R> DerefMut for RefMut<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| fn deref_mut(&mut self) -> &mut R { | |||||
| unsafe { self.inner.downcast_mut_unchecked() } | |||||
| } | |||||
| } | |||||
| #[cfg(test)] | |||||
| mod tests { | |||||
| use super::*; | |||||
| #[derive(Default)] | |||||
| struct Res; | |||||
| #[test] | |||||
| fn insert() { | |||||
| struct Foo; | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| assert!(!resources.contains::<Foo>()); | |||||
| } | |||||
| #[test] | |||||
| #[should_panic(expected = "but it was already borrowed")] | |||||
| fn read_write_fails() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| let _read = resources.borrow::<Res>(); | |||||
| let _write = resources.borrow_mut::<Res>(); | |||||
| } | |||||
| #[test] | |||||
| #[should_panic(expected = "but it was already borrowed mutably")] | |||||
| fn write_read_fails() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| let _write = resources.borrow_mut::<Res>(); | |||||
| let _read = resources.borrow::<Res>(); | |||||
| } | |||||
| #[test] | |||||
| fn remove_insert() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| resources.remove::<Res>().unwrap(); | |||||
| assert!(!resources.contains::<Res>()); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,256 @@ | |||||
| use std::marker::PhantomData; | |||||
| use std::ops::{Deref, DerefMut}; | |||||
| use hashbrown::HashMap; | |||||
| use mopa::Any; | |||||
| use super::{ | |||||
| cell::{Cell, Ref as CellRef, RefMut as CellRefMut}, | |||||
| entry::Entry, | |||||
| Resource, ResourceId, | |||||
| }; | |||||
| #[derive(Default)] | |||||
| pub struct Resources { | |||||
| resources: HashMap<ResourceId, Cell<Box<dyn Resource>>>, | |||||
| } | |||||
| pub struct Ref<'a, R: 'a> { | |||||
| inner: CellRef<'a, dyn Resource>, | |||||
| phantom: PhantomData<&'a R>, | |||||
| } | |||||
| pub struct RefMut<'a, R: 'a> { | |||||
| inner: CellRefMut<'a, dyn Resource>, | |||||
| phantom: PhantomData<&'a R>, | |||||
| } | |||||
| macro_rules! fetch_panic { | |||||
| () => {{ | |||||
| panic!( | |||||
| "\ | |||||
| Tried to fetch resource from the resources map, but the resource does not exist.\n\ | |||||
| \n\ | |||||
| Resource: `{resource_name_full}`\n\ | |||||
| \n\ | |||||
| You may ensure the resource exists!\ | |||||
| ", | |||||
| resource_name_full = std::any::type_name::<R>(), | |||||
| ) | |||||
| }}; | |||||
| } | |||||
| /* Resources */ | |||||
| impl Resources { | |||||
| pub fn entry<R>(&mut self) -> Entry<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| Entry::new(self.resources.entry(ResourceId::new::<R>())) | |||||
| } | |||||
| pub fn insert<R>(&mut self, r: R) | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources | |||||
| .insert(ResourceId::new::<R>(), Cell::new(Box::new(r))); | |||||
| } | |||||
| pub fn remove<R>(&mut self) -> Option<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources | |||||
| .remove(&ResourceId::new::<R>()) | |||||
| .map(Cell::into_inner) | |||||
| .map(|x: Box<dyn Resource>| x.downcast()) | |||||
| .map(|x: Result<Box<R>, _>| x.ok().unwrap()) | |||||
| .map(|x| *x) | |||||
| } | |||||
| pub fn contains<R>(&self) -> bool | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.contains_key(&ResourceId::new::<R>()) | |||||
| } | |||||
| pub fn borrow<R>(&self) -> Ref<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.try_borrow().unwrap_or_else(|| fetch_panic!()) | |||||
| } | |||||
| pub fn try_borrow<R>(&self) -> Option<Ref<R>> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.get(&ResourceId::new::<R>()).map(|r| Ref { | |||||
| inner: CellRef::map(r.borrow(), Box::as_ref), | |||||
| phantom: PhantomData, | |||||
| }) | |||||
| } | |||||
| pub fn borrow_mut<R>(&self) -> RefMut<R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.try_borrow_mut().unwrap_or_else(|| fetch_panic!()) | |||||
| } | |||||
| pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut { | |||||
| inner: r.borrow_mut().map(Box::as_mut), | |||||
| phantom: PhantomData, | |||||
| }) | |||||
| } | |||||
| pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> { | |||||
| self.get_resource_mut(ResourceId::new::<R>()) | |||||
| .map(|res| res.downcast_mut().unwrap()) | |||||
| } | |||||
| pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> { | |||||
| self.resources | |||||
| .get_mut(&id) | |||||
| .map(Cell::get_mut) | |||||
| .map(Box::as_mut) | |||||
| } | |||||
| } | |||||
| /* Resource */ | |||||
| impl<T> Resource for T where T: Any + Send + Sync {} | |||||
| mod __resource_mopafy_scope { | |||||
| #![allow(clippy::all)] | |||||
| use mopa::mopafy; | |||||
| use super::super::Resource; | |||||
| mopafy!(Resource); | |||||
| } | |||||
| /* Ref */ | |||||
| impl<'a, R> Ref<'a, R> { | |||||
| pub fn new(inner: CellRef<'a, dyn Resource>) -> Self { | |||||
| Self { | |||||
| inner, | |||||
| phantom: PhantomData, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<'a, R> Deref for Ref<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| type Target = R; | |||||
| fn deref(&self) -> &R { | |||||
| unsafe { self.inner.downcast_ref_unchecked() } | |||||
| } | |||||
| } | |||||
| impl<'a, R> Clone for Ref<'a, R> { | |||||
| fn clone(&self) -> Self { | |||||
| Ref { | |||||
| inner: self.inner.clone(), | |||||
| phantom: PhantomData, | |||||
| } | |||||
| } | |||||
| } | |||||
| /* RefMut */ | |||||
| impl<'a, R> RefMut<'a, R> { | |||||
| pub fn new(inner: CellRefMut<'a, dyn Resource>) -> Self { | |||||
| Self { | |||||
| inner, | |||||
| phantom: PhantomData, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<'a, R> Deref for RefMut<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| type Target = R; | |||||
| fn deref(&self) -> &R { | |||||
| unsafe { self.inner.downcast_ref_unchecked() } | |||||
| } | |||||
| } | |||||
| impl<'a, R> DerefMut for RefMut<'a, R> | |||||
| where | |||||
| R: Resource, | |||||
| { | |||||
| fn deref_mut(&mut self) -> &mut R { | |||||
| unsafe { self.inner.downcast_mut_unchecked() } | |||||
| } | |||||
| } | |||||
| #[cfg(test)] | |||||
| mod tests { | |||||
| use super::*; | |||||
| #[derive(Default)] | |||||
| struct Res; | |||||
| #[test] | |||||
| fn insert() { | |||||
| struct Foo; | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| assert!(!resources.contains::<Foo>()); | |||||
| } | |||||
| #[test] | |||||
| #[should_panic(expected = "but it was already borrowed")] | |||||
| fn read_write_fails() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| let _read = resources.borrow::<Res>(); | |||||
| let _write = resources.borrow_mut::<Res>(); | |||||
| } | |||||
| #[test] | |||||
| #[should_panic(expected = "but it was already borrowed mutably")] | |||||
| fn write_read_fails() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| let _write = resources.borrow_mut::<Res>(); | |||||
| let _read = resources.borrow::<Res>(); | |||||
| } | |||||
| #[test] | |||||
| fn remove_insert() { | |||||
| let mut resources = Resources::default(); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| resources.remove::<Res>().unwrap(); | |||||
| assert!(!resources.contains::<Res>()); | |||||
| resources.insert(Res); | |||||
| assert!(resources.contains::<Res>()); | |||||
| } | |||||
| } | |||||
| @@ -1,6 +1,8 @@ | |||||
| use std::mem::swap; | |||||
| use hibitset::BitSet; | use hibitset::BitSet; | ||||
| use crate::component::Component; | |||||
| use crate::{component::Component, entity::Entity, storage::Storage}; | |||||
| pub struct MaskedStorage<T: Component> { | pub struct MaskedStorage<T: Component> { | ||||
| mask: BitSet, | mask: BitSet, | ||||
| @@ -14,4 +16,19 @@ impl<T: Component> MaskedStorage<T> { | |||||
| inner, | inner, | ||||
| } | } | ||||
| } | } | ||||
| pub fn insert(&mut self, entity: Entity, mut component: T) -> Option<T> { | |||||
| let index = entity.index(); | |||||
| if self.mask.contains(index) { | |||||
| swap(&mut component, self.inner.get_mut(index)); | |||||
| Some(component) | |||||
| } else { | |||||
| self.mask.add(index); | |||||
| self.inner.insert(index, component); | |||||
| None | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -6,6 +6,10 @@ pub use masked_storage::MaskedStorage; | |||||
| pub use storage_wrapper::StorageWrapper; | pub use storage_wrapper::StorageWrapper; | ||||
| pub use vec_storage::VecStorage; | pub use vec_storage::VecStorage; | ||||
| use crate::misc::TryDefault; | |||||
| use crate::{entity::Index, misc::TryDefault}; | |||||
| pub trait Storage<T>: TryDefault {} | |||||
| pub trait Storage<T>: TryDefault { | |||||
| fn get(&self, index: Index) -> &T; | |||||
| fn get_mut(&mut self, index: Index) -> &mut T; | |||||
| fn insert(&mut self, index: Index, value: T); | |||||
| } | |||||
| @@ -1,6 +1,13 @@ | |||||
| use std::marker::PhantomData; | use std::marker::PhantomData; | ||||
| use std::ops::DerefMut; | |||||
| use crate::{entity::Entities, resource::Ref}; | |||||
| use crate::{ | |||||
| component::Component, | |||||
| entity::{Entities, Entity}, | |||||
| error::Error, | |||||
| resource::Ref, | |||||
| storage::MaskedStorage, | |||||
| }; | |||||
| pub struct StorageWrapper<'a, T, D> { | pub struct StorageWrapper<'a, T, D> { | ||||
| data: D, | data: D, | ||||
| @@ -17,3 +24,17 @@ impl<'a, T, D> StorageWrapper<'a, T, D> { | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| impl<'a, T, D> StorageWrapper<'a, T, D> | |||||
| where | |||||
| T: Component, | |||||
| D: DerefMut<Target = MaskedStorage<T>>, | |||||
| { | |||||
| pub fn insert(&mut self, entity: Entity, component: T) -> Result<Option<T>, Error> { | |||||
| if !self.entities.is_alive(entity) { | |||||
| return Err(Error::EntityIsNotAlive(entity)); | |||||
| } | |||||
| Ok(self.data.insert(entity, component)) | |||||
| } | |||||
| } | |||||
| @@ -1,10 +1,38 @@ | |||||
| use std::mem::MaybeUninit; | use std::mem::MaybeUninit; | ||||
| use crate::entity::Index; | |||||
| use super::Storage; | use super::Storage; | ||||
| pub struct VecStorage<T>(Vec<MaybeUninit<T>>); | pub struct VecStorage<T>(Vec<MaybeUninit<T>>); | ||||
| impl<T> Storage<T> for VecStorage<T> {} | |||||
| impl<T> Storage<T> for VecStorage<T> { | |||||
| fn get(&self, index: Index) -> &T { | |||||
| unsafe { &*self.0.get_unchecked(index as usize).as_ptr() } | |||||
| } | |||||
| fn get_mut(&mut self, index: Index) -> &mut T { | |||||
| unsafe { &mut *self.0.get_unchecked_mut(index as usize).as_mut_ptr() } | |||||
| } | |||||
| fn insert(&mut self, index: Index, value: T) { | |||||
| let index = index as usize; | |||||
| if self.0.len() <= index { | |||||
| let delta = index + 1 - self.0.len(); | |||||
| self.0.reserve(delta); | |||||
| unsafe { | |||||
| self.0.set_len(index + 1); | |||||
| } | |||||
| } | |||||
| unsafe { | |||||
| *self.0.get_unchecked_mut(index) = MaybeUninit::new(value); | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<T> Default for VecStorage<T> { | impl<T> Default for VecStorage<T> { | ||||
| fn default() -> Self { | fn default() -> Self { | ||||
| @@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut}; | |||||
| use crate::{ | use crate::{ | ||||
| access::{Read, ReadStorage, WriteStorage}, | access::{Read, ReadStorage, WriteStorage}, | ||||
| component::Component, | component::Component, | ||||
| entity::Entities, | |||||
| entity::{Builder, Entities}, | |||||
| resource::{Ref, RefMut, Resource, Resources}, | resource::{Ref, RefMut, Resource, Resources}, | ||||
| storage::MaskedStorage, | storage::MaskedStorage, | ||||
| system::SystemData, | system::SystemData, | ||||
| @@ -59,6 +59,10 @@ impl World { | |||||
| pub fn component_mut<T: Component>(&self) -> WriteStorage<T> { | pub fn component_mut<T: Component>(&self) -> WriteStorage<T> { | ||||
| WriteStorage::fetch(&self) | WriteStorage::fetch(&self) | ||||
| } | } | ||||
| pub fn create_entity(&mut self) -> Builder { | |||||
| Builder::new(self) | |||||
| } | |||||
| } | } | ||||
| impl Default for World { | impl Default for World { | ||||