Simple ECS resources
This commit is contained in:
parent
e8e2bc0c24
commit
7d94cf052f
|
@ -6,6 +6,7 @@ mod bundle;
|
||||||
mod component;
|
mod component;
|
||||||
mod query;
|
mod query;
|
||||||
mod component_info;
|
mod component_info;
|
||||||
|
mod resource;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
use std::{any::TypeId, alloc::Layout, cell::{RefCell, Ref, RefMut}, ptr::NonNull, alloc};
|
||||||
|
|
||||||
|
/// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually.
|
||||||
|
/* pub trait Resource: Send + Sync + 'static {}
|
||||||
|
impl<T: Send + Sync + 'static> Resource for T {} */
|
||||||
|
|
||||||
|
/// A type erased storage for a Resource.
|
||||||
|
///
|
||||||
|
/// A pointer instead of a generic is used since Rust may have no idea of the type of the data.
|
||||||
|
pub struct ResourceData {
|
||||||
|
data: RefCell<NonNull<u8>>,
|
||||||
|
type_id: TypeId,
|
||||||
|
layout: Layout,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceData {
|
||||||
|
pub fn new<T: 'static>(mut data: T) -> Self {
|
||||||
|
let layout = Layout::new::<T>();
|
||||||
|
|
||||||
|
let ptr = unsafe {
|
||||||
|
if let Some(ptr) = NonNull::new(alloc::alloc(layout)) {
|
||||||
|
let data = (&mut data as *mut T) as *mut u8; // thx i hate it
|
||||||
|
std::ptr::copy_nonoverlapping(data, ptr.as_ptr(), 1);
|
||||||
|
ptr
|
||||||
|
} else {
|
||||||
|
alloc::handle_alloc_error(layout)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
data: RefCell::new(ptr),
|
||||||
|
type_id: TypeId::of::<T>(),
|
||||||
|
layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean indicating whether or not T is of the same type that is inside this Resource
|
||||||
|
pub fn is<T: 'static>(&self) -> bool {
|
||||||
|
self.type_id == TypeId::of::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrow the data inside of the resource.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If the type of T is not the same as the expected type, it will panic.
|
||||||
|
pub fn get<'a, T: 'static>(&'a self) -> Ref<'a, T> {
|
||||||
|
assert!(TypeId::of::<T>() == self.type_id);
|
||||||
|
|
||||||
|
let data = self.data.borrow();
|
||||||
|
Ref::map(data, |ptr| unsafe { &*ptr.cast().as_ptr() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrow the data inside of the resource.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If the type of T is not the same as the expected type, it will panic.
|
||||||
|
pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> {
|
||||||
|
assert!(TypeId::of::<T>() == self.type_id);
|
||||||
|
|
||||||
|
let data = self.data.borrow_mut();
|
||||||
|
RefMut::map(data, |ptr| unsafe { &mut *ptr.cast().as_ptr() })
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}};
|
||||||
|
|
||||||
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::{ViewIter, View, DefaultQuery}};
|
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::{ViewIter, View, DefaultQuery}, resource::ResourceData};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct EntityId(pub u64);
|
pub struct EntityId(pub u64);
|
||||||
|
@ -27,6 +27,7 @@ pub struct World {
|
||||||
entity_index: HashMap<EntityId, Record>,
|
entity_index: HashMap<EntityId, Record>,
|
||||||
dead_entities: VecDeque<Entity>,
|
dead_entities: VecDeque<Entity>,
|
||||||
next_entity_id: EntityId,
|
next_entity_id: EntityId,
|
||||||
|
resources: HashMap<TypeId, ResourceData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
@ -37,6 +38,7 @@ impl World {
|
||||||
entity_index: HashMap::new(),
|
entity_index: HashMap::new(),
|
||||||
dead_entities: VecDeque::new(),
|
dead_entities: VecDeque::new(),
|
||||||
next_entity_id: EntityId(0),
|
next_entity_id: EntityId(0),
|
||||||
|
resources: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +120,44 @@ impl World {
|
||||||
let v = View::new(T::default_query(), archetypes);
|
let v = View::new(T::default_query(), archetypes);
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_resource<T: 'static>(&mut self, data: T) {
|
||||||
|
self.resources.insert(TypeId::of::<T>(), ResourceData::new(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a resource from the World.
|
||||||
|
///
|
||||||
|
/// Will panic if the resource is not in the world. See [`try_get_resource`] for
|
||||||
|
/// a function that returns an option.
|
||||||
|
pub fn get_resource<'a, T: 'static>(&'a self) -> Ref<'a, T> {
|
||||||
|
self.resources.get(&TypeId::of::<T>()).unwrap()
|
||||||
|
.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to get a resource from the World.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the resource was not found.
|
||||||
|
pub fn try_get_resource<'a, T: 'static>(&'a self) -> Option<Ref<'a, T>> {
|
||||||
|
self.resources.get(&TypeId::of::<T>())
|
||||||
|
.map(|r| r.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable borrow of a resource from the World.
|
||||||
|
///
|
||||||
|
/// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for
|
||||||
|
/// a function that returns an option.
|
||||||
|
pub fn get_resource_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> {
|
||||||
|
self.resources.get(&TypeId::of::<T>()).unwrap()
|
||||||
|
.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to get a mutable borrow of a resource from the World.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the resource was not found.
|
||||||
|
pub fn try_get_resource_mut<'a, T: 'static>(&'a self) -> Option<RefMut<'a, T>> {
|
||||||
|
self.resources.get(&TypeId::of::<T>())
|
||||||
|
.map(|r| r.get_mut())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -171,4 +211,28 @@ mod tests {
|
||||||
let record = world.entity_index.get(&last_en.id).unwrap();
|
let record = world.entity_index.get(&last_en.id).unwrap();
|
||||||
assert_eq!(record.index.0, 1);
|
assert_eq!(record.index.0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_resource() {
|
||||||
|
struct SimpleCounter(i32);
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
{
|
||||||
|
let counter = SimpleCounter(0);
|
||||||
|
world.add_resource(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let counter = world.get_resource::<SimpleCounter>();
|
||||||
|
assert!(counter.0 == 0);
|
||||||
|
drop(counter);
|
||||||
|
|
||||||
|
let mut counter = world.get_resource_mut::<SimpleCounter>();
|
||||||
|
counter.0 += 4582;
|
||||||
|
drop(counter);
|
||||||
|
|
||||||
|
let counter = world.get_resource::<SimpleCounter>();
|
||||||
|
assert!(counter.0 == 4582);
|
||||||
|
|
||||||
|
assert!(world.try_get_resource::<u32>().is_none());
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue