ecs: don't automatically tick the world, use Res and ResMut anywhere ecs resources are requested to track changes better

now the user must manually tick the world. The engine will do this before every update
This commit is contained in:
SeanOMik 2024-09-27 21:03:27 -04:00
parent 9b1cc8c364
commit f5aca87ede
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
10 changed files with 217 additions and 151 deletions

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use atomic_refcell::{AtomicRef, AtomicRefMut};
use crate::{World, resource::ResourceObject};
use crate::{resource::ResourceObject, Tick, TrackedResource, World};
use super::{Query, Fetch, AsQuery};
@ -22,12 +22,18 @@ impl<'a, T: ResourceObject + 'a> Fetch<'a> for FetchResource<'a, T> {
}
fn can_visit_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> bool {
true
let w = self.world.unwrap();
w.has_resource::<T>()
}
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
let w = self.world.unwrap();
Res(w.get_resource::<T>())
Res {
// this unwrap is safe since `can_visit_item` ensures the resource exists
inner: w.get_tracked_resource::<T>().unwrap(),
world_tick: w.current_tick(),
_marker: PhantomData::<T>,
}
}
}
@ -81,13 +87,37 @@ impl<R: ResourceObject> AsQuery for QueryResource<R> {
}
/// A struct used for querying resources from the World.
pub struct Res<'a, T: ResourceObject>(pub(crate) AtomicRef<'a, T>);
pub struct Res<'a, T: ResourceObject> {
pub(crate) inner: AtomicRef<'a, TrackedResource<dyn ResourceObject>>,
pub(crate) world_tick: Tick,
pub(crate) _marker: PhantomData<T>,
}
impl<'a, T: ResourceObject> std::ops::Deref for Res<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
self.inner.res.as_any().downcast_ref::<T>().unwrap()
}
}
impl<'a, T: ResourceObject> Res<'a, T> {
/// Get the inner [`AtomicRef`].
///
/// This inner type does not have change tracking. If you `DerefMut` it, the change will not be tracked!
#[inline]
pub fn get_inner(self) -> atomic_refcell::AtomicRef<'a, T> {
atomic_refcell::AtomicRef::map(self.inner, |r| r.res.as_any().downcast_ref().unwrap())
}
/// Returns a boolean indicating if the resource changed.
pub fn changed(&self) -> bool {
self.inner.tick >= self.world_tick
}
/// The tick that this resource was last modified at
pub fn tick(&self) -> Tick {
self.inner.tick
}
}
@ -111,12 +141,18 @@ impl<'a, T: ResourceObject + 'a> Fetch<'a> for FetchResourceMut<'a, T> {
}
fn can_visit_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> bool {
true
let w = self.world.unwrap();
w.has_resource::<T>()
}
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
let w = self.world.unwrap();
ResMut(w.get_resource_mut::<T>())
ResMut {
// this is safe since `can_visit_item` ensures that the resource exists.
inner: w.get_tracked_resource_mut::<T>().unwrap(),
world_tick: w.current_tick(),
_marker: PhantomData::<T>,
}
}
}
@ -169,20 +205,51 @@ impl<R: ResourceObject> AsQuery for QueryResourceMut<R> {
}
/// A struct used for querying resources from the World.
pub struct ResMut<'a, T: ResourceObject>(pub(crate) AtomicRefMut<'a, T>);
//pub struct ResMut<'a, T: ResourceObject>(pub(crate) AtomicRefMut<'a, T>);
pub struct ResMut<'a, T: ResourceObject> {
pub(crate) inner: AtomicRefMut<'a, TrackedResource<dyn ResourceObject>>,
pub(crate) world_tick: Tick,
pub(crate) _marker: PhantomData<T>,
}
impl<'a, T: ResourceObject> std::ops::Deref for ResMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
self.inner.res.as_any().downcast_ref::<T>().unwrap()
}
}
impl<'a, T: ResourceObject> std::ops::DerefMut for ResMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
self.mark_changed();
self.inner.res.as_any_mut().downcast_mut::<T>().unwrap()
}
}
impl<'a, T: ResourceObject> ResMut<'a, T> {
/// Get the inner [`AtomicRefMut`].
///
/// This inner type does not have change tracking. If you `DerefMut` it, the change will not be tracked!
pub fn get_inner(self) -> atomic_refcell::AtomicRefMut<'a, T> {
atomic_refcell::AtomicRefMut::map(self.inner, |r| r.res.as_any_mut().downcast_mut().unwrap())
}
pub fn changed(&self) -> bool {
self.inner.tick > self.world_tick
}
/// The tick that this resource was last modified at
pub fn tick(&self) -> Tick {
self.inner.tick
}
/// Manually mark the resource as changed.
///
/// This is useful if you have a resource with interior mutability and you want to
/// mark this resource as changed.
pub fn mark_changed(&mut self) {
self.inner.tick = self.world_tick
}
}

View File

@ -58,7 +58,6 @@ where
ViewIter {
world: self.world,
tick: self.world.current_tick(),
has_ticked: false,
query: self.query,
filter: self.filter,
fetcher: None,
@ -73,7 +72,6 @@ where
pub struct ViewIter<'a, Q: Query, F: Filter> {
world: &'a World,
tick: Tick,
has_ticked: bool,
query: Q,
filter: F,
fetcher: Option<Q::Fetch<'a>>,
@ -112,13 +110,6 @@ where
let entity_index = ArchetypeEntityId(entity_index);
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit_item(entity_index) {
// only tick the world if the filter has fetched, and when the world hasn't
// been ticked yet.
if Q::MUTATES && !self.has_ticked {
self.has_ticked = true;
self.tick = self.world.tick();
}
let i = unsafe { fetcher.get_item(entity_index) };
return Some(i);
}
@ -174,9 +165,6 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
if self.query.can_visit_archetype(arch) {
let mut fetch = unsafe { self.query.fetch(self.world, arch, self.tick) };
if fetch.can_visit_item(record.index) {
// only tick the world when something is actually fetched.
self.world.tick();
return Some(unsafe { fetch.get_item(record.index) });
}
}

View File

@ -20,22 +20,24 @@ impl<T: Send + Sync + Any> ResourceObject for T {
}
}
pub struct TrackedResource<T: ?Sized> {
pub tick: Tick,
pub res: T,
}
/// A type erased storage for a Resource.
#[derive(Clone)]
pub struct ResourceData {
pub(crate) data: Arc<AtomicRefCell<dyn ResourceObject>>,
pub(crate) data: Arc<AtomicRefCell<TrackedResource<dyn ResourceObject>>>,
type_id: TypeId,
// use a tick tracker which has interior mutability
pub(crate) tick: TickTracker,
}
impl ResourceData {
pub fn new<T: ResourceObject>(data: T, tick: Tick) -> Self {
Self {
data: Arc::new(AtomicRefCell::new(data)),
data: Arc::new(AtomicRefCell::new(TrackedResource { tick, res: data })),
type_id: TypeId::of::<T>(),
tick: TickTracker::from(*tick),
}
}
@ -51,7 +53,7 @@ impl ResourceData {
/// * If the data is already borrowed mutably, this will panic.
/// * If the type of `T` is not the same as the resource type.
pub fn get<T: ResourceObject>(&self) -> AtomicRef<T> {
AtomicRef::map(self.data.borrow(), |a| a.as_any().downcast_ref().unwrap())
AtomicRef::map(self.data.borrow(), |a| a.res.as_any().downcast_ref().unwrap())
}
/// Mutably borrow the data inside of the resource.
@ -61,7 +63,7 @@ impl ResourceData {
/// * If the data is already borrowed mutably, this will panic.
/// * If the type of `T` is not the same as the resource type.
pub fn get_mut<T: ResourceObject>(&self) -> AtomicRefMut<T> {
AtomicRefMut::map(self.data.borrow_mut(), |a| a.as_any_mut().downcast_mut().unwrap())
AtomicRefMut::map(self.data.borrow_mut(), |a| a.res.as_any_mut().downcast_mut().unwrap())
}
/// Borrow the data inside of the resource.
@ -71,7 +73,7 @@ impl ResourceData {
/// * If the type of `T` is not the same as the resource type.
pub fn try_get<T: ResourceObject>(&self) -> Option<AtomicRef<T>> {
self.data.try_borrow()
.map(|r| AtomicRef::map(r, |a| a.as_any().downcast_ref().unwrap()))
.map(|r| AtomicRef::map(r, |a| a.res.as_any().downcast_ref().unwrap()))
.ok()
}
@ -82,11 +84,11 @@ impl ResourceData {
/// * If the type of `T` is not the same as the resource type.
pub fn try_get_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<T>> {
self.data.try_borrow_mut()
.map(|r| AtomicRefMut::map(r, |a| a.as_any_mut().downcast_mut().unwrap()))
.map(|r| AtomicRefMut::map(r, |a| a.res.as_any_mut().downcast_mut().unwrap()))
.ok()
}
pub fn changed(&self, tick: Tick) -> bool {
self.tick.current() >= tick
self.data.borrow().tick >= tick
}
}

View File

@ -20,6 +20,7 @@ pub trait FnArgFetcher {
Access::Read
}
// TODO: check if the fetcher can fetch before getting.
/// Get the arg from the world
///
/// # Safety
@ -214,7 +215,9 @@ impl<R: ResourceObject> FnArgFetcher for Res<'_, R> {
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
Res(world.get_resource::<R>())
world.get_resource::<R>()
// TODO: check if the resource exists before attempting to fetch
.unwrap_or_else(|| panic!("world is missing resource: {}", std::any::type_name::<R>()))
}
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
@ -228,7 +231,9 @@ impl<R: ResourceObject> FnArgFetcher for ResMut<'_, R> {
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
ResMut(world.get_resource_mut::<R>())
world.get_resource_mut::<R>()
// TODO: check if the resource exists before attempting to fetch
.unwrap_or_else(|| panic!("world is missing resource: {}", std::any::type_name::<R>()))
}
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
@ -307,7 +312,8 @@ mod tests {
world.add_resource(SomeCounter(0));
let test_system = |world: &World| -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
let mut counter = world.get_resource_mut::<SomeCounter>()
.expect("Counter resource is missing");
counter.0 += 10;
Ok(())
@ -316,7 +322,8 @@ mod tests {
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let test_system = |world: &mut World| -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
let mut counter = world.get_resource_mut::<SomeCounter>()
.expect("Counter resource is missing");
counter.0 += 10;
Ok(())
@ -324,7 +331,8 @@ mod tests {
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let counter = world.get_resource::<SomeCounter>();
let counter = world.get_resource::<SomeCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 20);
}
@ -336,7 +344,8 @@ mod tests {
world.add_resource(SomeCounter(0));
let test_system = |world: &World| -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
let mut counter = world.get_resource_mut::<SomeCounter>()
.expect("Counter resource is missing");
counter.0 += 10;
Ok(())
@ -346,7 +355,8 @@ mod tests {
#[allow(dead_code)]
fn test_system(world: &mut World) -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
let mut counter = world.get_resource_mut::<SomeCounter>()
.expect("Counter resource is missing");
counter.0 += 10;
Ok(())
@ -354,7 +364,8 @@ mod tests {
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let counter = world.get_resource::<SomeCounter>();
let counter = world.get_resource::<SomeCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 20);
}
@ -366,16 +377,15 @@ mod tests {
world.add_resource(SomeCounter(0));
let test_system = |mut counter: ResMut<SomeCounter>| -> anyhow::Result<()> {
// .0 is twice here since ResMut's tuple field is pub(crate).
// Users wont need to do this
counter.0.0 += 10;
counter.0 += 10;
Ok(())
};
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let counter = world.get_resource::<SomeCounter>();
let counter = world.get_resource::<SomeCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 10);
}
@ -389,9 +399,7 @@ mod tests {
let test_system = |mut counter: ResMut<SomeCounter>, view: ViewState<QueryBorrow<Vec2>, ()>| -> anyhow::Result<()> {
for v2 in view.into_iter() {
println!("Got v2 at '{:?}'", v2);
// .0 is twice here since ResMut's tuple field is pub(crate).
// Users wont need to do this
counter.0.0 += 1;
counter.0 += 1;
}
Ok(())
@ -399,7 +407,8 @@ mod tests {
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let counter = world.get_resource::<SomeCounter>();
let counter = world.get_resource::<SomeCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 2);
}
}

View File

@ -103,7 +103,7 @@ impl GraphExecutor {
}
let world = unsafe { world_ptr.as_mut() };
if let Some(mut queue) = world.try_get_resource_mut::<CommandQueue>() {
if let Some(mut queue) = world.get_resource_mut::<CommandQueue>() {
// Safety: Commands only borrows world.entities when adding commands
let world = unsafe { world_ptr.as_mut() };
let mut commands = Commands::new(&mut queue, world);
@ -189,7 +189,8 @@ mod tests {
exec.execute(NonNull::from(&world), true).unwrap();
println!("Executed systems");
let order = world.get_resource::<Vec<String>>();
let order = world.get_resource::<Vec<String>>()
.expect("missing Vec<String> resource");
let mut order_iter = order.iter();
// ... but still executed in order

View File

@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
/// TickTracker is used for tracking changes of [`Component`](crate::Component)s and entities.
///
/// TickTracker stores an [`AtomicU64`], making all operations on `TickTracker`, atomic as well.
/// Note that [`Tick::clone`] only clones the inner value of atomic, and not the atomic itself.
/// Note that [`TickTracker::clone`] only clones the inner value of atomic, and not the atomic itself.
#[derive(Debug, Default)]
pub struct TickTracker {
tick: AtomicU64,

View File

@ -2,7 +2,7 @@ use std::{any::TypeId, collections::HashMap, ptr::NonNull};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, Res, ResMut, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker, TrackedResource};
/// The id of the entity for the Archetype.
///
@ -65,10 +65,9 @@ impl World {
where
B: Bundle
{
let tick = self.current_tick();
let bundle_types = bundle.type_ids();
let tick = self.tick();
// try to find an archetype
let archetype = self.archetypes
.values_mut()
@ -112,14 +111,8 @@ impl World {
/// Despawn an entity from the World
pub fn despawn(&mut self, entity: Entity) {
// 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.
let tick = if self.entities.arch_index.contains_key(&entity.id) {
Some(self.tick())
} else { None };
let tick = self.current_tick();
if let Some(record) = self.entities.arch_index.get_mut(&entity.id) {
let tick = tick.unwrap();
let arch = self.archetypes.get_mut(&record.id).unwrap();
if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) {
@ -138,8 +131,7 @@ impl World {
where
B: Bundle
{
let tick = self.tick();
let tick = self.current_tick();
let record = self.entities.entity_record(entity).unwrap();
let current_arch = self.archetypes.get(&record.id).unwrap();
let current_arch_len = current_arch.len();
@ -375,32 +367,24 @@ impl World {
}
/// Add a resource to the world.
///
/// Ticks the world.
pub fn add_resource<T: ResourceObject>(&mut self, data: T) {
let tick = self.tick();
self.resources.insert(TypeId::of::<T>(), ResourceData::new(data, tick));
self.resources.insert(TypeId::of::<T>(), ResourceData::new(data, self.current_tick()));
}
/// Add the default value of a resource.
///
/// Ticks the world.
///
/// > Note: This will replace existing values.
pub fn add_resource_default<T: ResourceObject + Default>(&mut self) {
let tick = self.tick();
self.resources.insert(TypeId::of::<T>(), ResourceData::new(T::default(), tick));
self.resources.insert(TypeId::of::<T>(), ResourceData::new(T::default(), self.current_tick()));
}
/// Add the default value of a resource if it does not already exist.
///
/// Returns a boolean indicating if the resource was added. Ticks the world if the resource
/// was added.
/// Returns a boolean indicating if the resource was added.
pub fn add_resource_default_if_absent<T: ResourceObject + Default>(&mut self) -> bool {
let id = TypeId::of::<T>();
if !self.resources.contains_key(&id) {
let tick = self.tick();
self.resources.insert(id, ResourceData::new(T::default(), tick));
self.resources.insert(id, ResourceData::new(T::default(), self.current_tick()));
true
} else {
@ -410,39 +394,60 @@ impl World {
/// Get a resource from the world, or insert it into the world with the provided
/// `fn` and return it.
///
/// Ticks the world.
pub fn get_resource_or_else<T: ResourceObject, F>(&mut self, f: F) -> AtomicRefMut<T>
pub fn get_resource_or_else<T: ResourceObject, F>(&mut self, f: F) -> ResMut<T>
where
F: Fn() -> T + 'static
{
let tick = self.tick();
let tick = self.current_tick();
let res = self.resources.entry(TypeId::of::<T>())
.or_insert_with(|| ResourceData::new(f(), tick));
res.tick.tick_to(&tick);
res.get_mut()
ResMut {
inner: res.data.borrow_mut(),
world_tick: tick,
_marker: std::marker::PhantomData::<T>,
}
}
/// Get a resource from the world, or insert it into the world as its default.
///
/// Ticks the world.
pub fn get_resource_or_default<T: ResourceObject + Default>(&mut self) -> AtomicRefMut<T>
/// Get a resource from the world, or insert its default value.
pub fn get_resource_or_default<T: ResourceObject + Default>(&mut self) -> ResMut<T>
{
let tick = self.tick();
let tick = self.current_tick();
let res = self.resources.entry(TypeId::of::<T>())
.or_insert_with(|| ResourceData::new(T::default(), tick));
res.tick.tick_to(&tick);
res.get_mut()
ResMut {
inner: res.data.borrow_mut(),
world_tick: tick,
_marker: std::marker::PhantomData::<T>,
}
}
/// Gets a resource from the World.
pub fn get_resource<T: ResourceObject>(&self) -> Option<Res<T>> {
self.get_tracked_resource::<T>().map(|r| Res {
inner: r,
world_tick: self.current_tick(),
_marker: std::marker::PhantomData::<T>,
})
}
/// Gets a reference to a change tracked resource.
///
/// Will panic if the resource is not in the world. See [`World::try_get_resource`] for
/// a function that returns an option.
pub fn get_resource<T: ResourceObject>(&self) -> AtomicRef<T> {
/// You will have to manually downcast the inner resource. Most people don't need this, see
/// [`World::get_resource`].
pub fn get_tracked_resource<T: ResourceObject>(&self) -> Option<AtomicRef<TrackedResource<dyn ResourceObject>>> {
self.resources.get(&TypeId::of::<T>())
.expect(&format!("World is missing resource of type '{}'", std::any::type_name::<T>()))
.get()
.map(|r| r.data.borrow())
}
/// Gets a mutable borrow to a change tracked resource.
///
/// You will have to manually downcast the inner resource. Most people don't need this, see
/// [`World::get_resource_mut`].
pub fn get_tracked_resource_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<TrackedResource<dyn ResourceObject>>> {
self.resources.get(&TypeId::of::<T>())
.map(|r| r.data.borrow_mut())
}
/// Returns a boolean indicating if the resource changed.
@ -458,7 +463,7 @@ impl World {
/// Returns the [`Tick`] that the resource was last modified at.
pub fn resource_tick<T: ResourceObject>(&self) -> Option<Tick> {
self.resources.get(&TypeId::of::<T>())
.map(|r| r.tick.current())
.map(|r| r.data.borrow().tick)
}
/// Returns boolean indicating if the World contains a resource of type `T`.
@ -466,56 +471,33 @@ impl World {
self.resources.contains_key(&TypeId::of::<T>())
}
/// Attempts to get a resource from the World.
///
/// Returns `None` if the resource was not found.
pub fn try_get_resource<T: ResourceObject>(&self) -> Option<AtomicRef<T>> {
self.resources.get(&TypeId::of::<T>())
.and_then(|r| r.try_get())
}
/// Gets a mutable borrow of a resource from the World.
///
/// Will panic if the resource is not in the world. See [`World::try_get_resource_mut`] for
/// a function that returns an option.
///
/// Ticks the world.
pub fn get_resource_mut<T: ResourceObject>(&self) -> AtomicRefMut<T> {
self.try_get_resource_mut::<T>()
.unwrap_or_else(|| panic!("World is missing resource of type '{}'", std::any::type_name::<T>()))
}
/// Attempts to get a mutable borrow of a resource from the World.
///
/// Returns `None` if the resource was not found. Ticks the world if the resource was found.
pub fn try_get_resource_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<T>> {
self.resources.get(&TypeId::of::<T>())
.and_then(|r| {
// now that the resource was retrieved, tick the world and the resource
let new_tick = self.tick();
r.tick.tick_to(&new_tick);
r.try_get_mut()
pub fn get_resource_mut<T: ResourceObject>(&self) -> Option<ResMut<T>> {
self.get_tracked_resource_mut::<T>().map(|r| ResMut {
inner: r,
world_tick: self.current_tick(),
_marker: std::marker::PhantomData::<T>,
})
}
/// Get the corresponding [`ResourceData`].
///
/// > Note: If you borrow the resource mutably, the world and the resource will not be ticked.
pub fn try_get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
pub fn get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
self.resources.get(&TypeId::of::<T>())
.map(|r| r.clone())
}
/// Increments the TickTracker which is used for tracking changes to components.
/// Increments the world current tick for tracking changes to components and resources.
///
/// Most users wont need to call this manually, its done for you through queries and views.
/// # Note:
/// For change tracking to work correctly, this must be ran each loop before you run world
/// systems.
pub fn tick(&self) -> Tick {
self.tracker.tick()
}
/// Gets the current tick that the world is at.
///
/// See [`TickTracker`]
/// See [`World::tick`].
pub fn current_tick(&self) -> Tick {
self.tracker.current()
}
@ -525,7 +507,7 @@ impl World {
}
/// Attempts to find a resource in the world and returns a NonNull pointer to it
pub unsafe fn try_get_resource_ptr<T: ResourceObject>(&self) -> Option<NonNull<T>> {
pub unsafe fn get_resource_ptr<T: ResourceObject>(&self) -> Option<NonNull<T>> {
self.resources.get(&TypeId::of::<T>())
.map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) })
}
@ -601,15 +583,17 @@ mod tests {
world.add_resource(counter);
}
let counter = world.get_resource::<SimpleCounter>();
let counter = world.get_resource::<SimpleCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 0);
drop(counter);
let mut counter = world.get_resource_mut::<SimpleCounter>();
let mut counter = world.get_resource_mut::<SimpleCounter>()
.expect("Counter resource is missing");
counter.0 += 4582;
drop(counter);
assert!(world.try_get_resource::<u32>().is_none());
assert!(world.get_resource::<u32>().is_none());
}
#[test]
@ -619,9 +603,11 @@ mod tests {
world.add_resource(counter);
// test multiple borrows at the same time
let counter = world.get_resource::<SimpleCounter>();
let counter = world.get_resource::<SimpleCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 4582);
let counter2 = world.get_resource::<SimpleCounter>();
let counter2 = world.get_resource::<SimpleCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 4582);
assert_eq!(counter2.0, 4582);
}
@ -635,9 +621,10 @@ mod tests {
}
// test that its only possible to get a single mutable borrow
let counter = world.get_resource_mut::<SimpleCounter>();
let counter = world.get_resource_mut::<SimpleCounter>()
.expect("Counter resource is missing");
assert_eq!(counter.0, 4582);
assert!(world.try_get_resource_mut::<SimpleCounter>().is_none());
assert!(world.get_resource_mut::<SimpleCounter>().is_none());
assert_eq!(counter.0, 4582);
}
@ -763,7 +750,8 @@ mod tests {
assert!(!world.has_resource_changed::<SimpleCounter>());
let mut counter = world.get_resource_mut::<SimpleCounter>();
let mut counter = world.get_resource_mut::<SimpleCounter>()
.expect("Counter resource is missing");
counter.0 += 100;
assert!(world.has_resource_changed::<SimpleCounter>());

View File

@ -2,7 +2,7 @@
use std::{any::TypeId, any::Any};
use lyra_ecs::{AtomicRef, AtomicRefMut, World};
use lyra_ecs::{query::{Res, ResMut}, AtomicRef, AtomicRefMut, World};
extern crate self as lyra_reflect;
@ -250,9 +250,9 @@ pub trait FromType<T> {
pub trait ReflectWorldExt {
/// Retrieves the type registry from the world.
fn get_type_registry(&self) -> AtomicRef<TypeRegistry>;
fn get_type_registry(&self) -> Option<Res<TypeRegistry>>;
/// Retrieves the type registry mutably from the world.
fn get_type_registry_mut(&self) -> AtomicRefMut<TypeRegistry>;
fn get_type_registry_mut(&self) -> Option<ResMut<TypeRegistry>>;
/// Get a registered type from the type registry. Returns `None` if the type is not registered
fn get_type<T>(&self, type_id: TypeId) -> Option<AtomicRef<RegisteredType>>;
/// Get a mutable registered type from the type registry in the world. returns `None` if the type is not registered.
@ -262,17 +262,19 @@ pub trait ReflectWorldExt {
}
impl ReflectWorldExt for World {
fn get_type_registry(&self) -> AtomicRef<TypeRegistry> {
fn get_type_registry(&self) -> Option<Res<TypeRegistry>> {
self.get_resource::<TypeRegistry>()
}
fn get_type_registry_mut(&self) -> AtomicRefMut<TypeRegistry> {
fn get_type_registry_mut(&self) -> Option<ResMut<TypeRegistry>> {
self.get_resource_mut::<TypeRegistry>()
}
fn get_type<T>(&self, type_id: TypeId) -> Option<AtomicRef<RegisteredType>> {
let r = self.get_resource::<TypeRegistry>();
let r = self.get_resource::<TypeRegistry>()
.expect("world is missing TypeRegistry resource");
if r.has_type(type_id) {
let r = r.get_inner();
Some(AtomicRef::map(r, |tr| tr.get_type(type_id).unwrap()))
} else {
None
@ -280,8 +282,10 @@ impl ReflectWorldExt for World {
}
fn get_type_mut<T>(&self, type_id: TypeId) -> Option<AtomicRefMut<RegisteredType>> {
let r = self.get_resource_mut::<TypeRegistry>();
let r = self.get_resource_mut::<TypeRegistry>()
.expect("world is missing TypeRegistry resource");
if r.has_type(type_id) {
let r = r.get_inner();
Some(AtomicRefMut::map(r, |tr| tr.get_type_mut(type_id).unwrap()))
} else {
None
@ -289,7 +293,9 @@ impl ReflectWorldExt for World {
}
fn get_type_or_default<T>(&self, type_id: TypeId) -> AtomicRefMut<RegisteredType> {
let r = self.get_resource_mut::<TypeRegistry>();
let r = self.get_resource_mut::<TypeRegistry>()
.expect("world is missing TypeRegistry resource");
let r = r.get_inner();
AtomicRefMut::map(r, |tr| tr.get_type_or_default(type_id))
}
}

View File

@ -40,19 +40,23 @@ impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource {
Self {
type_id: TypeId::of::<T>(),
fn_reflect: |world: &World| {
world.try_get_resource::<T>()
world.get_resource::<T>()
.map(|r| {
// TODO: figure out change tracking for reflected resource
let r = r.get_inner();
AtomicRef::map(r, |r| r as &dyn Reflect)
})
},
fn_reflect_mut: |world: &mut World| {
world.try_get_resource_mut::<T>()
world.get_resource_mut::<T>()
.map(|r| {
// TODO: figure out change tracking for reflected resource
let r = r.get_inner();
AtomicRefMut::map(r, |r| r as &mut dyn Reflect)
})
},
fn_reflect_ptr: |world: &mut World| unsafe {
world.try_get_resource_ptr::<T>()
world.get_resource_ptr::<T>()
.map(|ptr| ptr.cast::<u8>())
},
fn_refl_insert: |world: &mut World, this: Box<dyn Reflect>| {

View File

@ -59,7 +59,8 @@ impl WorldAssetExt for World {
}
fn res_watcher_recv(&self, path: &str) -> Option<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>> {
let man = self.get_resource::<ResourceManager>();
let man = self.get_resource::<ResourceManager>()
.expect("world is missing ResourceManager resource");
man.watcher_event_recv(path)
}