Convert package to a lib, get resources from views
This commit is contained in:
parent
08a458eb9c
commit
6fedb270b9
|
@ -32,7 +32,9 @@
|
||||||
"--no-run",
|
"--no-run",
|
||||||
"--bin=lyra-ecs",
|
"--bin=lyra-ecs",
|
||||||
"--package=lyra-ecs",
|
"--package=lyra-ecs",
|
||||||
|
"query::resource::tests::query",
|
||||||
"--",
|
"--",
|
||||||
|
"--exact",
|
||||||
"--nocapture"
|
"--nocapture"
|
||||||
],
|
],
|
||||||
"filter": {
|
"filter": {
|
||||||
|
|
|
@ -19,7 +19,7 @@ I couldn't find anything that fulfilled my needs, specifically an ECS that can s
|
||||||
- [ ] Mutable views
|
- [ ] Mutable views
|
||||||
- [ ] Make it possible so that ONLY `ViewMut` can borrow mutably
|
- [ ] Make it possible so that ONLY `ViewMut` can borrow mutably
|
||||||
- [x] Resources
|
- [x] Resources
|
||||||
- [ ] Get resources in views somehow
|
- [x] Get resources in views somehow
|
||||||
- [ ] Relationships (maybe this can be done through queries, idk)
|
- [ ] Relationships (maybe this can be done through queries, idk)
|
||||||
- [ ] Dynamic queries
|
- [ ] Dynamic queries
|
||||||
* Needed for scripting engines that can create their own components that Rust does not know the types of.
|
* Needed for scripting engines that can create their own components that Rust does not know the types of.
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::world::World;
|
||||||
|
|
||||||
|
mod archetype;
|
||||||
|
pub use archetype::*;
|
||||||
|
|
||||||
|
pub mod world;
|
||||||
|
pub use world::*;
|
||||||
|
|
||||||
|
mod bundle;
|
||||||
|
pub use bundle::*;
|
||||||
|
|
||||||
|
mod component;
|
||||||
|
pub use component::*;
|
||||||
|
|
||||||
|
mod query;
|
||||||
|
pub use query::*;
|
||||||
|
|
||||||
|
mod component_info;
|
||||||
|
pub use component_info::*;
|
||||||
|
|
||||||
|
mod resource;
|
||||||
|
pub use resource::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
|
@ -1,22 +0,0 @@
|
||||||
use crate::world::World;
|
|
||||||
|
|
||||||
mod archetype;
|
|
||||||
mod world;
|
|
||||||
mod bundle;
|
|
||||||
mod component;
|
|
||||||
mod query;
|
|
||||||
mod component_info;
|
|
||||||
mod resource;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Position2d(i32, i32);
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut world = World::new();
|
|
||||||
|
|
||||||
let pos = Position2d(836, 348);
|
|
||||||
let _e = world.spawn((pos,));
|
|
||||||
}
|
|
|
@ -37,9 +37,9 @@ where
|
||||||
/// A Query for borrowing components from archetypes.
|
/// A Query for borrowing components from archetypes.
|
||||||
///
|
///
|
||||||
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
|
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
|
||||||
/// ```rust
|
/// ```nobuild
|
||||||
/// for ts in world.view::<&T>() {
|
/// for ts in world.view::<&T>() {
|
||||||
/// println!("Got an &T!");
|
/// println!("Got a &T!");
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct QueryBorrow<T> {
|
pub struct QueryBorrow<T> {
|
||||||
|
@ -134,9 +134,9 @@ where
|
||||||
/// A Query for mutably borrowing components from archetypes.
|
/// A Query for mutably borrowing components from archetypes.
|
||||||
///
|
///
|
||||||
/// Since [`AsQuery`] is implemented for `&mut T`, you can use this query like this:
|
/// Since [`AsQuery`] is implemented for `&mut T`, you can use this query like this:
|
||||||
/// ```rust
|
/// ```nobuild
|
||||||
/// for ts in world.view::<&mut T>() {
|
/// for ts in world.view::<&mut T>() {
|
||||||
/// println!("Got an &T!");
|
/// println!("Got an &mut T!");
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct QueryBorrowMut<T> {
|
pub struct QueryBorrowMut<T> {
|
||||||
|
|
|
@ -15,6 +15,10 @@ pub mod tuple;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use tuple::*;
|
pub use tuple::*;
|
||||||
|
|
||||||
|
pub mod resource;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use resource::*;
|
||||||
|
|
||||||
/// A [`Fetch`]er implementation gets data out of an archetype.
|
/// A [`Fetch`]er implementation gets data out of an archetype.
|
||||||
pub trait Fetch<'a> {
|
pub trait Fetch<'a> {
|
||||||
/// The type that this Fetch yields
|
/// The type that this Fetch yields
|
||||||
|
@ -39,16 +43,25 @@ pub trait Query {
|
||||||
/// The fetcher used for this query
|
/// The fetcher used for this query
|
||||||
type Fetch<'a>: Fetch<'a, Item = Self::Item<'a>>;
|
type Fetch<'a>: Fetch<'a, Item = Self::Item<'a>>;
|
||||||
|
|
||||||
|
/// A constant that signifies if this Query will always fetch something.
|
||||||
|
/// [`QueryResource`] has this set to true, since they will almost always fetch
|
||||||
|
/// something.
|
||||||
|
///
|
||||||
|
/// [`View`] uses this to determine if it should continue to iterate this Query.
|
||||||
|
const ALWAYS_FETCHES: bool = false;
|
||||||
|
|
||||||
/// Returns true if the archetype should be visited or skipped.
|
/// Returns true if the archetype should be visited or skipped.
|
||||||
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
|
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
|
||||||
|
|
||||||
/// Returns true if the Query can visit the world
|
|
||||||
/* fn can_visit_world(&self, world: &'a World) -> bool {
|
|
||||||
let _ = world; // compiler warnings
|
|
||||||
false
|
|
||||||
} */
|
|
||||||
|
|
||||||
unsafe fn fetch<'a>(&self, world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>;
|
unsafe fn fetch<'a>(&self, world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>;
|
||||||
|
|
||||||
|
/// Attempt to fetch only from the world.
|
||||||
|
///
|
||||||
|
/// This is used in [`QueryResource`].
|
||||||
|
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
|
||||||
|
let _ = world;
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for getting the query of a type.
|
/// A trait for getting the query of a type.
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::Ref};
|
||||||
|
|
||||||
|
use crate::{world::World, resource::ResourceObject};
|
||||||
|
|
||||||
|
use super::{Query, Fetch, AsQuery, DefaultQuery};
|
||||||
|
|
||||||
|
pub struct FetchResource<'a, T> {
|
||||||
|
world: Option<&'a World>,
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'a + 'static> Fetch<'a> for FetchResource<'a, T> {
|
||||||
|
type Item = Ref<'a, T>;
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
Self {
|
||||||
|
world: None,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
|
let w = self.world.unwrap();
|
||||||
|
w.get_resource::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A query type for getting Resources
|
||||||
|
///
|
||||||
|
/// Resources are stored in an archetype with the id of [`world::RESOURCE_ARCHETYPE_ID`]. There is only one instance of a type of resource.
|
||||||
|
/// The resources are stored in that archetype in the first entity in the column.
|
||||||
|
pub struct QueryResource<T> {
|
||||||
|
_phantom: PhantomData<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Resource<T> = QueryResource<T>;
|
||||||
|
|
||||||
|
impl<T: 'static> Query for QueryResource<T> {
|
||||||
|
type Item<'a> = Ref<'a, T>;
|
||||||
|
|
||||||
|
type Fetch<'a> = FetchResource<'a, T>;
|
||||||
|
|
||||||
|
const ALWAYS_FETCHES: bool = true;
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, _archetype: &crate::archetype::Archetype) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, world: &'a World, _arch_id: crate::archetype::ArchetypeId, _archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
|
||||||
|
self.fetch_world(world).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
|
||||||
|
Some(FetchResource {
|
||||||
|
world: Some(world),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ResourceObject> AsQuery for QueryResource<R> {
|
||||||
|
type Query = QueryResource<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ResourceObject> DefaultQuery for QueryResource<R> {
|
||||||
|
fn default_query() -> Self::Query {
|
||||||
|
QueryResource::<R> {
|
||||||
|
_phantom: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{world::World, tests::{Vec2, Vec3}};
|
||||||
|
|
||||||
|
use super::QueryResource;
|
||||||
|
|
||||||
|
struct SomeCounter(u32);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_query() {
|
||||||
|
let mut world = World::new();
|
||||||
|
{
|
||||||
|
let counter = SomeCounter(0);
|
||||||
|
world.add_resource(counter);
|
||||||
|
println!("Added resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res_iter = world.view::<QueryResource<SomeCounter>>();
|
||||||
|
let res = res_iter.next().unwrap();
|
||||||
|
assert_eq!(res.0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex_query() {
|
||||||
|
let mut world = World::new();
|
||||||
|
{
|
||||||
|
let counter = SomeCounter(0);
|
||||||
|
|
||||||
|
world.spawn((Vec2::rand(),));
|
||||||
|
world.spawn((Vec2::rand(),));
|
||||||
|
world.spawn((Vec2::rand(),));
|
||||||
|
world.spawn((Vec3::rand(),));
|
||||||
|
|
||||||
|
world.add_resource(counter);
|
||||||
|
println!("Added resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = world.view::<(QueryResource<SomeCounter>, &Vec2)>();
|
||||||
|
assert_eq!(i.count(), 3);
|
||||||
|
let i = world.view::<(&Vec2, QueryResource<SomeCounter>)>();
|
||||||
|
assert_eq!(i.count(), 3);
|
||||||
|
|
||||||
|
for (res, e) in world.view::<(QueryResource<SomeCounter>, &Vec2)>() {
|
||||||
|
println!("Got res {}! and entity at {:?}", res.0, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = world.view::<QueryResource<SomeCounter>>();
|
||||||
|
assert_eq!(i.count(), 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ where
|
||||||
ViewIter {
|
ViewIter {
|
||||||
world: self.world,
|
world: self.world,
|
||||||
query: self.query,
|
query: self.query,
|
||||||
fetcher: Q::Fetch::dangling(),
|
fetcher: None,
|
||||||
archetypes: self.archetypes,
|
archetypes: self.archetypes,
|
||||||
next_archetype: 0,
|
next_archetype: 0,
|
||||||
component_indices: 0..0,
|
component_indices: 0..0,
|
||||||
|
@ -46,7 +46,7 @@ where
|
||||||
pub struct ViewIter<'a, Q: Query> {
|
pub struct ViewIter<'a, Q: Query> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
query: Q,
|
query: Q,
|
||||||
fetcher: Q::Fetch<'a>,
|
fetcher: Option<Q::Fetch<'a>>,
|
||||||
archetypes: Vec<&'a Archetype>,
|
archetypes: Vec<&'a Archetype>,
|
||||||
next_archetype: usize,
|
next_archetype: usize,
|
||||||
component_indices: Range<u64>,
|
component_indices: Range<u64>,
|
||||||
|
@ -60,12 +60,27 @@ where
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
|
if Q::ALWAYS_FETCHES {
|
||||||
|
// only fetch this query once.
|
||||||
|
// fetcher gets set to Some after this `next` call.
|
||||||
|
if self.fetcher.is_none() {
|
||||||
|
if let Some(mut fetch) = unsafe { self.query.fetch_world(self.world) } {
|
||||||
|
let res = unsafe { Some(fetch.get_item(ArchetypeEntityId(0))) };
|
||||||
|
self.fetcher = Some(fetch);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(entity_index) = self.component_indices.next() {
|
if let Some(entity_index) = self.component_indices.next() {
|
||||||
|
let fetcher = self.fetcher.as_mut().unwrap();
|
||||||
let entity_index = ArchetypeEntityId(entity_index);
|
let entity_index = ArchetypeEntityId(entity_index);
|
||||||
if !self.fetcher.can_visit_item(entity_index) {
|
if !fetcher.can_visit_item(entity_index) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let i = unsafe { self.fetcher.get_item(entity_index) };
|
let i = unsafe { fetcher.get_item(entity_index) };
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,7 +100,7 @@ where
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fetcher = unsafe { self.query.fetch(self.world, ArchetypeId(arch_id as u64), arch) };
|
self.fetcher = unsafe { Some(self.query.fetch(self.world, ArchetypeId(arch_id as u64), arch)) };
|
||||||
self.component_indices = 0..arch.entities.len() as u64;
|
self.component_indices = 0..arch.entities.len() as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{any::TypeId, alloc::Layout, cell::{RefCell, Ref, RefMut}, ptr::NonNull, alloc};
|
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.
|
/// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually.
|
||||||
/* pub trait Resource: Send + Sync + 'static {}
|
pub trait ResourceObject: Send + Sync + 'static {}
|
||||||
impl<T: Send + Sync + 'static> Resource for T {} */
|
impl<T: Send + Sync + 'static> ResourceObject for T {}
|
||||||
|
|
||||||
/// A type erased storage for a Resource.
|
/// A type erased storage for a Resource.
|
||||||
///
|
///
|
||||||
|
|
|
@ -42,4 +42,15 @@ impl Vec3 {
|
||||||
z
|
z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rand() -> Self {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let range = 30.0..1853.0;
|
||||||
|
|
||||||
|
Vec3 {
|
||||||
|
x: rng.gen_range(range.clone()),
|
||||||
|
y: rng.gen_range(range.clone()),
|
||||||
|
z: rng.gen_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue