Convert package to a lib, get resources from views

This commit is contained in:
SeanOMik 2023-11-30 23:05:06 -05:00
parent 08a458eb9c
commit 6fedb270b9
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
10 changed files with 210 additions and 40 deletions

View File

@ -32,7 +32,9 @@
"--no-run",
"--bin=lyra-ecs",
"--package=lyra-ecs",
"query::resource::tests::query",
"--",
"--exact",
"--nocapture"
],
"filter": {

View File

@ -19,7 +19,7 @@ I couldn't find anything that fulfilled my needs, specifically an ECS that can s
- [ ] Mutable views
- [ ] Make it possible so that ONLY `ViewMut` can borrow mutably
- [x] Resources
- [ ] Get resources in views somehow
- [x] Get resources in views somehow
- [ ] Relationships (maybe this can be done through queries, idk)
- [ ] Dynamic queries
* Needed for scripting engines that can create their own components that Rust does not know the types of.

25
lyra-ecs/src/lib.rs Normal file
View File

@ -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;

View File

@ -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,));
}

View File

@ -37,9 +37,9 @@ where
/// A Query for borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
/// ```rust
/// ```nobuild
/// for ts in world.view::<&T>() {
/// println!("Got an &T!");
/// println!("Got a &T!");
/// }
/// ```
pub struct QueryBorrow<T> {
@ -134,9 +134,9 @@ where
/// A Query for mutably borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&mut T`, you can use this query like this:
/// ```rust
/// ```nobuild
/// for ts in world.view::<&mut T>() {
/// println!("Got an &T!");
/// println!("Got an &mut T!");
/// }
/// ```
pub struct QueryBorrowMut<T> {

View File

@ -15,6 +15,10 @@ pub mod tuple;
#[allow(unused_imports)]
pub use tuple::*;
pub mod resource;
#[allow(unused_imports)]
pub use resource::*;
/// A [`Fetch`]er implementation gets data out of an archetype.
pub trait Fetch<'a> {
/// The type that this Fetch yields
@ -39,16 +43,25 @@ pub trait Query {
/// The fetcher used for this query
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.
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>;
/// 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.

View File

@ -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);
}
}

View File

@ -35,7 +35,7 @@ where
ViewIter {
world: self.world,
query: self.query,
fetcher: Q::Fetch::dangling(),
fetcher: None,
archetypes: self.archetypes,
next_archetype: 0,
component_indices: 0..0,
@ -46,7 +46,7 @@ where
pub struct ViewIter<'a, Q: Query> {
world: &'a World,
query: Q,
fetcher: Q::Fetch<'a>,
fetcher: Option<Q::Fetch<'a>>,
archetypes: Vec<&'a Archetype>,
next_archetype: usize,
component_indices: Range<u64>,
@ -60,12 +60,27 @@ where
fn next(&mut self) -> Option<Self::Item> {
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() {
let fetcher = self.fetcher.as_mut().unwrap();
let entity_index = ArchetypeEntityId(entity_index);
if !self.fetcher.can_visit_item(entity_index) {
if !fetcher.can_visit_item(entity_index) {
continue;
} else {
let i = unsafe { self.fetcher.get_item(entity_index) };
let i = unsafe { fetcher.get_item(entity_index) };
return Some(i);
}
} else {
@ -85,7 +100,7 @@ where
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;
}
}

View File

@ -1,8 +1,8 @@
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 {} */
pub trait ResourceObject: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> ResourceObject for T {}
/// A type erased storage for a Resource.
///

View File

@ -42,4 +42,15 @@ impl Vec3 {
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)
}
}
}