Create an early scripting engine #2
|
@ -13,6 +13,8 @@ lyra-ecs-derive = { path = "./lyra-ecs-derive" }
|
||||||
lyra-math = { path = "../lyra-math", optional = true }
|
lyra-math = { path = "../lyra-math", optional = true }
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
thiserror = "1.0.50"
|
thiserror = "1.0.50"
|
||||||
|
unique = "0.9.1"
|
||||||
|
paste = "1.0.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5" # used for tests
|
rand = "0.8.5" # used for tests
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::{DerefMut, Deref}};
|
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::{DerefMut, Deref}};
|
||||||
|
|
||||||
use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick};
|
use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ComponentColumn {
|
pub struct ComponentColumn {
|
||||||
|
@ -419,7 +419,7 @@ mod tests {
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::{tests::{Vec2, Vec3}, world::{Entity, EntityId}, bundle::Bundle, ComponentInfo, MemoryLayout, DynTypeId, DynamicBundle, Tick};
|
use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, MemoryLayout, Tick};
|
||||||
|
|
||||||
use super::Archetype;
|
use super::Archetype;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}};
|
||||||
|
|
||||||
|
use unique::Unique;
|
||||||
|
|
||||||
|
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
||||||
|
|
||||||
|
pub trait Command {
|
||||||
|
fn run(self, world: &mut World) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Command for F
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut World) -> anyhow::Result<()>
|
||||||
|
{
|
||||||
|
fn run(self, world: &mut World) -> anyhow::Result<()> {
|
||||||
|
self(world)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunCommand = unsafe fn(cmd: Unique<()>, world: &mut World) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>);
|
||||||
|
|
||||||
|
pub struct Commands<'a, 'b> {
|
||||||
|
queue: &'b mut CommandQueue,
|
||||||
|
entities: &'a mut Entities,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Commands<'a, 'b> {
|
||||||
|
pub fn new(queue: &'b mut CommandQueue, world: &'a mut World) -> Self {
|
||||||
|
Self {
|
||||||
|
queue,
|
||||||
|
entities: &mut world.entities,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a command to the end of the command queue
|
||||||
|
pub fn add<C: Command>(&mut self, mut cmd: C) {
|
||||||
|
// get an owned pointer to the command, then forget it to ensure its destructor isn't ran
|
||||||
|
let ptr = Unique::from(&mut cmd).cast::<()>();
|
||||||
|
mem::forget(cmd);
|
||||||
|
|
||||||
|
let run_fn = |cmd_ptr: Unique<()>, world: &mut World| unsafe {
|
||||||
|
let cmd = cmd_ptr.cast::<C>();
|
||||||
|
let cmd = ptr::read(cmd.as_ptr());
|
||||||
|
cmd.run(world)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
self.queue.0.push_back((run_fn, ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn<B: Bundle>(&mut self, mut bundle: B) -> Entity {
|
||||||
|
let e = self.entities.reserve();
|
||||||
|
|
||||||
|
let bundle_ptr = Unique::from(&mut bundle);
|
||||||
|
mem::forget(bundle);
|
||||||
|
//let bundle_box = Box::new(bundle);
|
||||||
|
|
||||||
|
self.add(move |world: &mut World| {
|
||||||
|
let bundle = unsafe { ptr::read(bundle_ptr.as_ptr()) };
|
||||||
|
world.spawn_into(e, bundle);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute all commands in the queue, in order of insertion
|
||||||
|
pub fn execute(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
|
while let Some((cmd_fn, cmd_ptr)) = self.queue.0.pop_front() {
|
||||||
|
unsafe {
|
||||||
|
cmd_fn(cmd_ptr, world)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnArgFetcher for Commands<'_, '_> {
|
||||||
|
type State = CommandQueue;
|
||||||
|
type Arg<'a, 'state> = Commands<'a, 'state>;
|
||||||
|
|
||||||
|
fn world_access(&self) -> Access {
|
||||||
|
Access::Write
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get<'a, 'state>(state: &'state mut Self::State, mut world_ptr: ptr::NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
|
let world = world_ptr.as_mut();
|
||||||
|
Commands::new(state, world)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State {
|
||||||
|
CommandQueue::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_deferred<'a>(mut state: Self::State, mut world_ptr: NonNull<World>) {
|
||||||
|
let world = unsafe { world_ptr.as_mut() };
|
||||||
|
|
||||||
|
let mut cmds = Commands::new(&mut state, world);
|
||||||
|
// safety: Commands has a mut borrow to entities in the world
|
||||||
|
let world = unsafe { world_ptr.as_mut() };
|
||||||
|
cmds.execute(world).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut<Commands>) -> anyhow::Result<()> {
|
||||||
|
commands.execute(world)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
use crate::Record;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EntityId(pub u64);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Entity {
|
||||||
|
pub(crate) id: EntityId,
|
||||||
|
pub(crate) generation: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Entities {
|
||||||
|
pub(crate) arch_index: HashMap<EntityId, Record>,
|
||||||
|
dead: VecDeque<Entity>,
|
||||||
|
next_id: EntityId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Entities {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
arch_index: Default::default(),
|
||||||
|
dead: Default::default(),
|
||||||
|
next_id: EntityId(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entities {
|
||||||
|
pub fn reserve(&mut self) -> Entity {
|
||||||
|
match self.dead.pop_front() {
|
||||||
|
Some(mut e) => {
|
||||||
|
e.generation += 1;
|
||||||
|
e
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!("id is {}", self.next_id.0);
|
||||||
|
let new_id = self.next_id;
|
||||||
|
self.next_id.0 += 1;
|
||||||
|
|
||||||
|
Entity {
|
||||||
|
id: new_id,
|
||||||
|
generation: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the Archetype Record for the entity
|
||||||
|
pub(crate) fn entity_record(&self, entity: Entity) -> Option<Record> {
|
||||||
|
self.arch_index.get(&entity.id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert_entity_record(&mut self, entity: Entity, record: Record) {
|
||||||
|
self.arch_index.insert(entity.id, record);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,19 @@ pub(crate) mod lyra_engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod archetype;
|
pub mod archetype;
|
||||||
|
use std::ops::BitOr;
|
||||||
|
|
||||||
pub use archetype::*;
|
pub use archetype::*;
|
||||||
|
|
||||||
|
pub mod entity;
|
||||||
|
pub use entity::*;
|
||||||
|
|
||||||
pub mod world;
|
pub mod world;
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
||||||
|
pub mod command;
|
||||||
|
pub use command::*;
|
||||||
|
|
||||||
pub mod bundle;
|
pub mod bundle;
|
||||||
pub use bundle::*;
|
pub use bundle::*;
|
||||||
|
|
||||||
|
@ -49,3 +57,17 @@ pub enum Access {
|
||||||
Read,
|
Read,
|
||||||
Write,
|
Write,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BitOr for Access {
|
||||||
|
type Output = Access;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
if self == Access::Write || rhs == Access::Write {
|
||||||
|
Access::Write
|
||||||
|
} else if self == Access::Read || rhs == Access::Read {
|
||||||
|
Access::Read
|
||||||
|
} else {
|
||||||
|
Access::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -221,7 +221,7 @@ impl<T: Component> AsQuery for &mut T {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
|
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
|
||||||
|
|
||||||
use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::{View, Fetch}, tests::Vec2, bundle::Bundle, DynTypeId, Tick};
|
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, View}, tests::Vec2, world::World, DynTypeId, Entity, EntityId, Tick};
|
||||||
|
|
||||||
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
|
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{world::{Entity, World}, archetype::Archetype};
|
use crate::{archetype::Archetype, world::World, Entity};
|
||||||
|
|
||||||
use super::{Fetch, Query, AsQuery};
|
use super::{Fetch, Query, AsQuery};
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> Option<Q::Item<'a>> {
|
pub fn get(&self) -> Option<Q::Item<'a>> {
|
||||||
if let Some(record) = self.world.entity_index.get(&self.entity) {
|
if let Some(record) = self.world.entities.arch_index.get(&self.entity) {
|
||||||
let arch = self.world.archetypes.get(&record.id)
|
let arch = self.world.archetypes.get(&record.id)
|
||||||
.expect("An invalid record was specified for an entity");
|
.expect("An invalid record was specified for an entity");
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,10 @@ impl System for BatchedSystem {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_deferred(&mut self, _: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoSystem<()> for BatchedSystem {
|
impl IntoSystem<()> for BatchedSystem {
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
use std::{ptr::NonNull, marker::PhantomData};
|
use std::{ptr::NonNull, marker::PhantomData};
|
||||||
|
|
||||||
|
use unique::Unique;
|
||||||
|
use paste::paste;
|
||||||
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
|
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
|
||||||
|
|
||||||
use super::{System, IntoSystem};
|
use super::{System, IntoSystem};
|
||||||
|
|
||||||
pub trait FnArgFetcher {
|
pub trait FnArgFetcher {
|
||||||
type Arg<'a>: FnArg<Fetcher = Self>;
|
/// stores data that persists after an execution of a system
|
||||||
|
type State: 'static;
|
||||||
|
|
||||||
fn new() -> Self;
|
type Arg<'a, 'state>: FnArgFetcher<State = Self::State>;
|
||||||
|
|
||||||
|
//fn new() -> Self;
|
||||||
|
fn create_state(world: NonNull<World>) -> Self::State;
|
||||||
|
|
||||||
/// Return the appropriate world access if this fetcher gets the world directly.
|
/// Return the appropriate world access if this fetcher gets the world directly.
|
||||||
/// Return [`Access::None`] if you're only fetching components, or resources.
|
/// Return [`Access::None`] if you're only fetching components, or resources.
|
||||||
|
@ -20,7 +26,10 @@ pub trait FnArgFetcher {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The system executor must ensure that on execution of the system, it will be safe to
|
/// The system executor must ensure that on execution of the system, it will be safe to
|
||||||
/// borrow the world.
|
/// borrow the world.
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a>;
|
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state>;
|
||||||
|
|
||||||
|
/// Apply some action after the system was ran.
|
||||||
|
fn apply_deferred(state: Self::State, world: NonNull<World>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FnArg {
|
pub trait FnArg {
|
||||||
|
@ -29,8 +38,10 @@ pub trait FnArg {
|
||||||
|
|
||||||
pub struct FnSystem<F, Args> {
|
pub struct FnSystem<F, Args> {
|
||||||
inner: F,
|
inner: F,
|
||||||
#[allow(dead_code)]
|
//#[allow(dead_code)]
|
||||||
args: Args,
|
//args: Args,
|
||||||
|
arg_state: Option<Vec<Unique<()>>>,
|
||||||
|
_marker: PhantomData<Args>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_fn_system_tuple {
|
macro_rules! impl_fn_system_tuple {
|
||||||
|
@ -38,47 +49,87 @@ macro_rules! impl_fn_system_tuple {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
impl<F, $($name: FnArgFetcher,)+> System for FnSystem<F, ($($name,)+)>
|
impl<F, $($name: FnArgFetcher,)+> System for FnSystem<F, ($($name,)+)>
|
||||||
where
|
where
|
||||||
F: for<'a> FnMut($($name::Arg<'a>,)+) -> anyhow::Result<()>,
|
F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
||||||
{
|
{
|
||||||
fn world_access(&self) -> Access {
|
fn world_access(&self) -> Access {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
|
fn execute(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
|
||||||
$(let $name = unsafe { $name::new().get(world) };)+
|
unsafe {
|
||||||
|
paste! {
|
||||||
|
$(
|
||||||
|
// get the arg fetcher, create its state, and get the arg
|
||||||
|
|
||||||
(self.inner)($($name,)+)?;
|
let mut [<state_ $name:lower>]: $name::State = $name::create_state(world);
|
||||||
|
let [<$name:lower>] = $name::get(&mut [<state_ $name:lower>], world);
|
||||||
|
|
||||||
|
)+
|
||||||
|
|
||||||
|
(self.inner)($( [<$name:lower>] ),+)?;
|
||||||
|
|
||||||
|
let mut state = Vec::new();
|
||||||
|
$(
|
||||||
|
// type erase the now modified state, and store it
|
||||||
|
let [<state_ $name:lower _ptr>] = Unique::from(&mut [<state_ $name:lower>]);
|
||||||
|
std::mem::forget([<state_ $name:lower>]);
|
||||||
|
state.push([<state_ $name:lower _ptr>].cast::<()>());
|
||||||
|
)+
|
||||||
|
|
||||||
|
self.arg_state = Some(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
|
||||||
|
$(
|
||||||
|
let s = self.arg_state.as_mut().expect("Somehow there was no state").pop().unwrap();
|
||||||
|
let s = unsafe { std::ptr::read(s.cast::<$name::State>().as_ptr()) };
|
||||||
|
$name::apply_deferred(s, world);
|
||||||
|
)+
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* impl<F, $($name: FnArg,)+> IntoSystem for F
|
/* impl<F, $($name: FnArg,)+> IntoSystem<($($name,)+)> for F
|
||||||
where
|
where
|
||||||
|
/* for <'a> &'a mut F:
|
||||||
|
FnMut($($name,)+) -> anyhow::Result<()>,
|
||||||
|
FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */
|
||||||
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
||||||
F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>,
|
F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
||||||
{
|
{
|
||||||
type System = FnSystem<F, ($($name::Fetcher,)+)>;
|
type System = FnSystem<F, ($($name::Fetcher,)+)>;
|
||||||
|
|
||||||
fn into_system(self) -> Self::System {
|
fn into_system(self) -> Self::System {
|
||||||
FnSystem {
|
FnSystem {
|
||||||
args: ($($name::Fetcher::new(),)+),
|
//args: ($($name::Fetcher::new(),)+),
|
||||||
inner: self
|
inner: self,
|
||||||
|
arg_state: None,
|
||||||
|
_marker: PhantomData::<($($name::Fetcher,)+)>::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|
||||||
impl<F, $($name: FnArg,)+> IntoSystem<($($name,)+)> for F
|
impl<F, $($name: FnArgFetcher,)+> IntoSystem<($($name,)+)> for F
|
||||||
where
|
where
|
||||||
|
/* for <'a> &'a mut F:
|
||||||
|
FnMut($($name,)+) -> anyhow::Result<()>,
|
||||||
|
FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */
|
||||||
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
||||||
F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>,
|
F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
||||||
{
|
{
|
||||||
type System = FnSystem<F, ($($name::Fetcher,)+)>;
|
type System = FnSystem<F, ($($name,)+)>;
|
||||||
|
|
||||||
fn into_system(self) -> Self::System {
|
fn into_system(self) -> Self::System {
|
||||||
FnSystem {
|
FnSystem {
|
||||||
args: ($($name::Fetcher::new(),)+),
|
//args: ($($name::Fetcher::new(),)+),
|
||||||
inner: self
|
inner: self,
|
||||||
|
arg_state: None,
|
||||||
|
_marker: PhantomData::<($($name,)+)>::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,127 +154,116 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O }
|
||||||
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P }
|
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P }
|
||||||
|
|
||||||
/// An ArgFetcher implementation for query [`View`]s
|
/// An ArgFetcher implementation for query [`View`]s
|
||||||
pub struct ViewArgFetcher<Q: AsQuery> {
|
/* pub struct ViewArgFetcher<Q: AsQuery> {
|
||||||
query: Q::Query
|
query: Q::Query
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Q: AsQuery> FnArg for View<'a, Q> {
|
impl<'a, Q: AsQuery> FnArg for View<'a, Q> {
|
||||||
type Fetcher = ViewArgFetcher<Q>;
|
type Fetcher = ViewArgFetcher<Q>;
|
||||||
}
|
} */
|
||||||
|
|
||||||
impl<Q: AsQuery> FnArgFetcher for ViewArgFetcher<Q> {
|
impl<'c, Q> FnArgFetcher for View<'c, Q>
|
||||||
type Arg<'a> = View<'a, Q>;
|
where
|
||||||
|
Q: AsQuery,
|
||||||
fn new() -> Self {
|
<Q as AsQuery>::Query: 'static
|
||||||
ViewArgFetcher {
|
{
|
||||||
query: <Q::Query as Query>::new(),
|
type State = Q::Query;
|
||||||
}
|
type Arg<'a, 'state> = View<'a, Q>;
|
||||||
}
|
|
||||||
|
|
||||||
fn world_access(&self) -> Access {
|
fn world_access(&self) -> Access {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
|
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
let world = &*world.as_ptr();
|
let world = &*world.as_ptr();
|
||||||
let arch = world.archetypes.values().collect();
|
let arch = world.archetypes.values().collect();
|
||||||
let v = View::new(world, self.query, arch);
|
let v = View::new(world, state.clone(), arch);
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State {
|
||||||
|
<Q::Query as Query>::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An ArgFetcher implementation for borrowing the [`World`].
|
/// An ArgFetcher implementation for borrowing the [`World`].
|
||||||
pub struct WorldArgFetcher;
|
/* pub struct WorldArgFetcher;
|
||||||
|
|
||||||
impl<'a> FnArg for &'a World {
|
impl<'a> FnArg for &'a World {
|
||||||
type Fetcher = WorldArgFetcher;
|
type Fetcher = WorldArgFetcher;
|
||||||
}
|
} */
|
||||||
|
|
||||||
impl FnArgFetcher for WorldArgFetcher {
|
impl FnArgFetcher for &'_ World {
|
||||||
type Arg<'a> = &'a World;
|
type State = ();
|
||||||
|
type Arg<'a, 'state> = &'a World;
|
||||||
fn new() -> Self {
|
|
||||||
WorldArgFetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
fn world_access(&self) -> Access {
|
fn world_access(&self) -> Access {
|
||||||
Access::Read
|
Access::Read
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
|
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
&*world.as_ptr()
|
&*world.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State { () }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An ArgFetcher implementation for mutably borrowing the [`World`].
|
impl FnArgFetcher for &'_ mut World {
|
||||||
pub struct WorldMutArgFetcher;
|
type State = ();
|
||||||
|
type Arg<'a, 'state> = &'a mut World;
|
||||||
impl<'a> FnArg for &'a mut World {
|
|
||||||
type Fetcher = WorldMutArgFetcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FnArgFetcher for WorldMutArgFetcher {
|
|
||||||
type Arg<'a> = &'a mut World;
|
|
||||||
|
|
||||||
fn new() -> Self {
|
|
||||||
WorldMutArgFetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
fn world_access(&self) -> Access {
|
fn world_access(&self) -> Access {
|
||||||
Access::Write
|
Access::Write
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
|
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
&mut *world.as_ptr()
|
&mut *world.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State { () }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResourceArgFetcher<R: ResourceObject> {
|
/* pub struct ResourceArgFetcher<R: ResourceObject> {
|
||||||
phantom: PhantomData<fn() -> R>
|
phantom: PhantomData<fn() -> R>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R: ResourceObject> FnArg for Res<'a, R> {
|
impl<'a, R: ResourceObject> FnArg for Res<'a, R> {
|
||||||
type Fetcher = ResourceArgFetcher<R>;
|
type Fetcher = ResourceArgFetcher<R>;
|
||||||
}
|
} */
|
||||||
|
|
||||||
impl<R: ResourceObject> FnArgFetcher for ResourceArgFetcher<R> {
|
impl<R: ResourceObject> FnArgFetcher for Res<'_, R> {
|
||||||
type Arg<'a> = Res<'a, R>;
|
type State = ();
|
||||||
|
type Arg<'a, 'state> = Res<'a, R>;
|
||||||
|
|
||||||
fn new() -> Self {
|
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
ResourceArgFetcher {
|
|
||||||
phantom: PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
|
|
||||||
let world = world.as_ref();
|
let world = world.as_ref();
|
||||||
Res(world.get_resource::<R>())
|
Res(world.get_resource::<R>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State { () }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResourceMutArgFetcher<R: ResourceObject> {
|
impl<R: ResourceObject> FnArgFetcher for ResMut<'_, R> {
|
||||||
phantom: PhantomData<fn() -> R>
|
type State = ();
|
||||||
}
|
type Arg<'a, 'state> = ResMut<'a, R>;
|
||||||
|
|
||||||
impl<'a, R: ResourceObject> FnArg for ResMut<'a, R> {
|
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
type Fetcher = ResourceMutArgFetcher<R>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ResourceObject> FnArgFetcher for ResourceMutArgFetcher<R> {
|
|
||||||
type Arg<'a> = ResMut<'a, R>;
|
|
||||||
|
|
||||||
fn new() -> Self {
|
|
||||||
ResourceMutArgFetcher {
|
|
||||||
phantom: PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
|
|
||||||
let world = world.as_ref();
|
let world = world.as_ref();
|
||||||
ResMut(world.get_resource_mut::<R>())
|
ResMut(world.get_resource_mut::<R>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
|
fn create_state(_: NonNull<World>) -> Self::State { () }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,7 +9,9 @@ pub enum GraphExecutorError {
|
||||||
#[error("could not find a system's dependency named `{0}`")]
|
#[error("could not find a system's dependency named `{0}`")]
|
||||||
MissingSystem(String),
|
MissingSystem(String),
|
||||||
#[error("system `{0}` returned with an error: `{1}`")]
|
#[error("system `{0}` returned with an error: `{1}`")]
|
||||||
SystemError(String, anyhow::Error)
|
SystemError(String, anyhow::Error),
|
||||||
|
#[error("a command returned with an error: `{0}`")]
|
||||||
|
Command(anyhow::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single system in the graph.
|
/// A single system in the graph.
|
||||||
|
@ -56,7 +58,7 @@ impl GraphExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the systems in the graph
|
/// Executes the systems in the graph
|
||||||
pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
pub fn execute(&mut self, world_ptr: NonNull<World>, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
let mut visited = HashSet::new();
|
let mut visited = HashSet::new();
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ impl GraphExecutor {
|
||||||
while let Some(node) = stack.pop_front() {
|
while let Some(node) = stack.pop_front() {
|
||||||
let system = self.systems.get_mut(node.as_str()).unwrap();
|
let system = self.systems.get_mut(node.as_str()).unwrap();
|
||||||
|
|
||||||
if let Err(e) = system.system.execute(world)
|
if let Err(e) = system.system.execute(world_ptr)
|
||||||
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
||||||
if stop_on_error {
|
if stop_on_error {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
|
@ -78,6 +80,17 @@ impl GraphExecutor {
|
||||||
possible_errors.push(e);
|
possible_errors.push(e);
|
||||||
unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error
|
unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(e) = system.system.execute_deferred(world_ptr)
|
||||||
|
.map_err(|e| GraphExecutorError::Command(e)) {
|
||||||
|
|
||||||
|
if stop_on_error {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
possible_errors.push(e);
|
||||||
|
unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(possible_errors)
|
Ok(possible_errors)
|
||||||
|
|
|
@ -27,6 +27,8 @@ pub trait System {
|
||||||
let _ = world;
|
let _ = world;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoSystem<T> {
|
pub trait IntoSystem<T> {
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}, ptr::NonNull};
|
use std::{any::TypeId, cell::{Ref, RefMut}, collections::HashMap, ptr::NonNull};
|
||||||
|
|
||||||
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, query::{Query, ViewIter, View, AsQuery}, resource::ResourceData, query::{dynamic::DynamicView, ViewOne}, ComponentInfo, DynTypeId, TickTracker, Tick, ResourceObject};
|
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, View, ViewIter, ViewOne}, resource::ResourceData, ComponentInfo, DynTypeId, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct EntityId(pub u64);
|
|
||||||
|
|
||||||
/// The id of the entity for the Archetype.
|
/// The id of the entity for the Archetype.
|
||||||
/// The Archetype struct uses this as the index in the component columns
|
/// The Archetype struct uses this as the index in the component columns
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ArchetypeEntityId(pub u64);
|
pub struct ArchetypeEntityId(pub u64);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Entity {
|
|
||||||
pub(crate) id: EntityId,
|
|
||||||
pub(crate) generation: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Record {
|
pub struct Record {
|
||||||
pub id: ArchetypeId,
|
pub id: ArchetypeId,
|
||||||
|
@ -25,11 +16,9 @@ pub struct Record {
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub(crate) archetypes: HashMap<ArchetypeId, Archetype>,
|
pub(crate) archetypes: HashMap<ArchetypeId, Archetype>,
|
||||||
next_archetype_id: ArchetypeId,
|
next_archetype_id: ArchetypeId,
|
||||||
pub(crate) entity_index: HashMap<EntityId, Record>,
|
|
||||||
dead_entities: VecDeque<Entity>,
|
|
||||||
next_entity_id: EntityId,
|
|
||||||
resources: HashMap<TypeId, ResourceData>,
|
resources: HashMap<TypeId, ResourceData>,
|
||||||
tracker: TickTracker,
|
tracker: TickTracker,
|
||||||
|
pub(crate) entities: Entities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for World {
|
impl Default for World {
|
||||||
|
@ -37,11 +26,9 @@ impl Default for World {
|
||||||
Self {
|
Self {
|
||||||
archetypes: HashMap::new(),
|
archetypes: HashMap::new(),
|
||||||
next_archetype_id: ArchetypeId(0),
|
next_archetype_id: ArchetypeId(0),
|
||||||
entity_index: HashMap::new(),
|
|
||||||
dead_entities: VecDeque::new(),
|
|
||||||
next_entity_id: EntityId(0),
|
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
tracker: TickTracker::new(),
|
tracker: TickTracker::new(),
|
||||||
|
entities: Entities::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,32 +38,30 @@ impl World {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a new Entity, will recycle dead entities and increment their generation.
|
/// Reserves an entity in the world
|
||||||
fn get_new_entity(&mut self) -> Entity {
|
pub fn reserve_entity(&mut self) -> Entity {
|
||||||
match self.dead_entities.pop_front() {
|
self.entities.reserve()
|
||||||
Some(mut e) => {
|
|
||||||
e.generation += 1;
|
|
||||||
e
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
let new_id = self.next_entity_id;
|
|
||||||
self.next_entity_id.0 += 1;
|
|
||||||
|
|
||||||
Entity {
|
|
||||||
id: new_id,
|
|
||||||
generation: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns a new entity and inserts the component `bundle` into it.
|
|
||||||
pub fn spawn<B>(&mut self, bundle: B) -> Entity
|
pub fn spawn<B>(&mut self, bundle: B) -> Entity
|
||||||
|
where
|
||||||
|
B: Bundle
|
||||||
|
{
|
||||||
|
let new_entity = self.reserve_entity();
|
||||||
|
self.spawn_into(new_entity, bundle);
|
||||||
|
new_entity
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawn the components into a reserved entity. Only do this with entities that
|
||||||
|
/// were 'reserved' with [`World::reserve`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Do not use this method with an entity that is currently alive, it WILL cause undefined behavior.
|
||||||
|
pub fn spawn_into<B>(&mut self, entity: Entity, bundle: B)
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
let bundle_types = bundle.type_ids();
|
let bundle_types = bundle.type_ids();
|
||||||
let new_entity = self.get_new_entity();
|
|
||||||
|
|
||||||
let tick = self.tick();
|
let tick = self.tick();
|
||||||
|
|
||||||
|
@ -86,7 +71,11 @@ impl World {
|
||||||
.find(|a| a.is_archetype_for(&bundle_types));
|
.find(|a| a.is_archetype_for(&bundle_types));
|
||||||
|
|
||||||
if let Some(archetype) = archetype {
|
if let Some(archetype) = archetype {
|
||||||
let arche_idx = archetype.add_entity(new_entity, bundle, &tick);
|
// make at just one check to ensure you're not spawning twice
|
||||||
|
debug_assert!(!archetype.entities.contains_key(&entity),
|
||||||
|
"You attempted to spawn components into an entity that already exists!");
|
||||||
|
|
||||||
|
let arche_idx = archetype.add_entity(entity, bundle, &tick);
|
||||||
|
|
||||||
// Create entity record and store it
|
// Create entity record and store it
|
||||||
let record = Record {
|
let record = Record {
|
||||||
|
@ -94,16 +83,17 @@ impl World {
|
||||||
index: arche_idx,
|
index: arche_idx,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.entity_index.insert(new_entity.id, record);
|
self.entities.insert_entity_record(entity, record);
|
||||||
}
|
}
|
||||||
// create a new archetype if one isn't found
|
// create a new archetype if one isn't found
|
||||||
else {
|
else {
|
||||||
// create archetype
|
// create archetype
|
||||||
let new_arch_id = self.next_archetype_id.increment();
|
let new_arch_id = self.next_archetype_id.increment();
|
||||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
|
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
|
||||||
let entity_arch_id = archetype.add_entity(new_entity, bundle, &tick);
|
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
|
||||||
|
|
||||||
// store archetype
|
// store archetype
|
||||||
|
println!("About to store arch, cap is {}, len is {}", self.archetypes.capacity(), self.archetypes.len());
|
||||||
self.archetypes.insert(new_arch_id, archetype);
|
self.archetypes.insert(new_arch_id, archetype);
|
||||||
|
|
||||||
// Create entity record and store it
|
// Create entity record and store it
|
||||||
|
@ -113,27 +103,25 @@ impl World {
|
||||||
index: entity_arch_id,
|
index: entity_arch_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.entity_index.insert(new_entity.id, record);
|
self.entities.insert_entity_record(entity, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Despawn an entity from the World
|
/// Despawn an entity from the World
|
||||||
pub fn despawn(&mut self, entity: Entity) {
|
pub fn despawn(&mut self, entity: Entity) {
|
||||||
// Tick the tracker if the entity is spawned. This is done here instead of the `if let`
|
// Tick the tracker if the entity is spawned. This is done here instead of the `if let`
|
||||||
// below due to the borrow checker complaining about multiple mutable borrows to self.
|
// below due to the borrow checker complaining about multiple mutable borrows to self.
|
||||||
let tick = if self.entity_index.contains_key(&entity.id) {
|
let tick = if self.entities.arch_index.contains_key(&entity.id) {
|
||||||
Some(self.tick())
|
Some(self.tick())
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
if let Some(record) = self.entity_index.get_mut(&entity.id) {
|
if let Some(record) = self.entities.arch_index.get_mut(&entity.id) {
|
||||||
let tick = tick.unwrap();
|
let tick = tick.unwrap();
|
||||||
let arch = self.archetypes.get_mut(&record.id).unwrap();
|
let arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||||
|
|
||||||
if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) {
|
if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) {
|
||||||
// replace the archetype index of the moved index with its new index.
|
// replace the archetype index of the moved index with its new index.
|
||||||
self.entity_index.get_mut(&moved.id).unwrap().index = new_index;
|
self.entities.arch_index.get_mut(&moved.id).unwrap().index = new_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +140,7 @@ impl World {
|
||||||
|
|
||||||
let tick = self.tick();
|
let tick = self.tick();
|
||||||
|
|
||||||
let record = *self.entity_index.get(&entity.id).unwrap();
|
let record = self.entities.entity_record(entity).unwrap();
|
||||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||||
|
|
||||||
let mut col_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id).collect();
|
let mut col_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id).collect();
|
||||||
|
@ -188,7 +176,7 @@ impl World {
|
||||||
id: arch.id,
|
id: arch.id,
|
||||||
index: res_index,
|
index: res_index,
|
||||||
};
|
};
|
||||||
self.entity_index.insert(entity.id, new_record);
|
self.entities.insert_entity_record(entity, new_record);
|
||||||
} else {
|
} else {
|
||||||
let new_arch_id = self.next_archetype_id.increment();
|
let new_arch_id = self.next_archetype_id.increment();
|
||||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos);
|
let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos);
|
||||||
|
@ -202,7 +190,7 @@ impl World {
|
||||||
index: entity_arch_id,
|
index: entity_arch_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.entity_index.insert(entity.id, record);
|
self.entities.insert_entity_record(entity, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||||
|
@ -210,12 +198,12 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity_archetype(&self, entity: Entity) -> Option<&Archetype> {
|
pub fn entity_archetype(&self, entity: Entity) -> Option<&Archetype> {
|
||||||
self.entity_index.get(&entity.id)
|
self.entities.entity_record(entity)
|
||||||
.and_then(|record| self.archetypes.get(&record.id))
|
.and_then(|record| self.archetypes.get(&record.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity_archetype_mut(&mut self, entity: Entity) -> Option<&mut Archetype> {
|
pub fn entity_archetype_mut(&mut self, entity: Entity) -> Option<&mut Archetype> {
|
||||||
self.entity_index.get_mut(&entity.id)
|
self.entities.entity_record(entity)
|
||||||
.and_then(|record| self.archetypes.get_mut(&record.id))
|
.and_then(|record| self.archetypes.get_mut(&record.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +243,14 @@ impl World {
|
||||||
.get_mut()
|
.get_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a resource from the world, or insert it into the world as its default.
|
||||||
|
pub fn get_resource_or_default<T: Default + 'static>(&mut self) -> RefMut<T>
|
||||||
|
{
|
||||||
|
self.resources.entry(TypeId::of::<T>())
|
||||||
|
.or_insert_with(|| ResourceData::new(T::default()))
|
||||||
|
.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a resource from the World.
|
/// Gets a resource from the World.
|
||||||
///
|
///
|
||||||
/// Will panic if the resource is not in the world. See [`try_get_resource`] for
|
/// Will panic if the resource is not in the world. See [`try_get_resource`] for
|
||||||
|
@ -264,6 +260,11 @@ impl World {
|
||||||
.get()
|
.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns boolean indicating if the World contains a resource of type `T`.
|
||||||
|
pub fn has_resource<T: 'static>(&self) -> bool {
|
||||||
|
self.resources.contains_key(&TypeId::of::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to get a resource from the World.
|
/// Attempts to get a resource from the World.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the resource was not found.
|
/// Returns `None` if the resource was not found.
|
||||||
|
@ -364,7 +365,7 @@ mod tests {
|
||||||
|
|
||||||
world.despawn(middle_en);
|
world.despawn(middle_en);
|
||||||
|
|
||||||
let record = world.entity_index.get(&last_en.id).unwrap();
|
let record = world.entities.entity_record(last_en).unwrap();
|
||||||
assert_eq!(record.index.0, 1);
|
assert_eq!(record.index.0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue