Implement init_resource for Commands
and World
(#3079)
# Objective - Fixes #3078 - Fixes #1397 ## Solution - Implement Commands::init_resource. - Also implement for World, for consistency and to simplify internal structure. - While we're here, clean up some of the docs for Command and World resource modification.
This commit is contained in:
parent
38f6da5a85
commit
bdbf626341
@ -647,18 +647,15 @@ impl App {
|
|||||||
/// App::new()
|
/// App::new()
|
||||||
/// .insert_resource(MyCounter { counter: 0 });
|
/// .insert_resource(MyCounter { counter: 0 });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
|
pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
|
||||||
where
|
|
||||||
T: Resource,
|
|
||||||
{
|
|
||||||
self.world.insert_resource(resource);
|
self.world.insert_resource(resource);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a non-send resource to the app
|
/// Inserts a non-send resource to the app
|
||||||
///
|
///
|
||||||
/// You usually want to use `insert_resource`, but there are some special cases when a resource must
|
/// You usually want to use `insert_resource`,
|
||||||
/// be non-send.
|
/// but there are some special cases when a resource cannot be sent across threads.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
@ -671,19 +668,18 @@ impl App {
|
|||||||
/// App::new()
|
/// App::new()
|
||||||
/// .insert_non_send_resource(MyCounter { counter: 0 });
|
/// .insert_non_send_resource(MyCounter { counter: 0 });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_non_send_resource<T>(&mut self, resource: T) -> &mut Self
|
pub fn insert_non_send_resource<R: 'static>(&mut self, resource: R) -> &mut Self {
|
||||||
where
|
self.world.insert_non_send_resource(resource);
|
||||||
T: 'static,
|
|
||||||
{
|
|
||||||
self.world.insert_non_send(resource);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a resource in the current [`App`], if it does not exist yet
|
/// Initialize a resource with standard starting values by adding it to the [`World`]
|
||||||
///
|
///
|
||||||
/// If the resource already exists, nothing happens.
|
/// If the resource already exists, nothing happens.
|
||||||
///
|
///
|
||||||
/// Adds a resource that implements `Default` or [`FromWorld`] trait.
|
/// The resource must implement the [`FromWorld`] trait.
|
||||||
|
/// If the `Default` trait is implemented, the `FromWorld` trait will use
|
||||||
|
/// the `Default::default` method to initialize the resource.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
@ -704,32 +700,18 @@ impl App {
|
|||||||
/// App::new()
|
/// App::new()
|
||||||
/// .init_resource::<MyCounter>();
|
/// .init_resource::<MyCounter>();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn init_resource<R>(&mut self) -> &mut Self
|
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
|
||||||
where
|
self.world.init_resource::<R>();
|
||||||
R: FromWorld + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
// PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed
|
|
||||||
// not to modify the map. However, we would need to be borrowing resources both
|
|
||||||
// mutably and immutably, so we would need to be extremely certain this is correct
|
|
||||||
if !self.world.contains_resource::<R>() {
|
|
||||||
let resource = R::from_world(&mut self.world);
|
|
||||||
self.insert_resource(resource);
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a non-send resource in the current [`App`], if it does not exist yet.
|
/// Initialize a non-send resource with standard starting values by adding it to the [`World`]
|
||||||
///
|
///
|
||||||
/// Adds a resource that implements `Default` or [`FromWorld`] trait.
|
/// The resource must implement the [`FromWorld`] trait.
|
||||||
pub fn init_non_send_resource<R>(&mut self) -> &mut Self
|
/// If the `Default` trait is implemented, the `FromWorld` trait will use
|
||||||
where
|
/// the `Default::default` method to initialize the resource.
|
||||||
R: FromWorld + 'static,
|
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) -> &mut Self {
|
||||||
{
|
self.world.init_non_send_resource::<R>();
|
||||||
// See perf comment in init_resource
|
|
||||||
if self.world.get_non_send_resource::<R>().is_none() {
|
|
||||||
let resource = R::from_world(&mut self.world);
|
|
||||||
self.world.insert_non_send(resource);
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1181,8 +1181,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn non_send_resource() {
|
fn non_send_resource() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world.insert_non_send(123i32);
|
world.insert_non_send_resource(123i32);
|
||||||
world.insert_non_send(456i64);
|
world.insert_non_send_resource(456i64);
|
||||||
assert_eq!(*world.get_non_send_resource::<i32>().unwrap(), 123);
|
assert_eq!(*world.get_non_send_resource::<i32>().unwrap(), 123);
|
||||||
assert_eq!(*world.get_non_send_resource_mut::<i64>().unwrap(), 456);
|
assert_eq!(*world.get_non_send_resource_mut::<i64>().unwrap(), 456);
|
||||||
}
|
}
|
||||||
@ -1191,7 +1191,7 @@ mod tests {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn non_send_resource_panic() {
|
fn non_send_resource_panic() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world.insert_non_send(0i32);
|
world.insert_non_send_resource(0i32);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let _ = world.get_non_send_resource_mut::<i32>();
|
let _ = world.get_non_send_resource_mut::<i32>();
|
||||||
})
|
})
|
||||||
|
@ -476,7 +476,7 @@ mod tests {
|
|||||||
fn non_send_resource() {
|
fn non_send_resource() {
|
||||||
use std::thread;
|
use std::thread;
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.insert_non_send(thread::current().id());
|
world.insert_non_send_resource(thread::current().id());
|
||||||
fn non_send(thread_id: NonSend<thread::ThreadId>) {
|
fn non_send(thread_id: NonSend<thread::ThreadId>) {
|
||||||
assert_eq!(thread::current().id(), *thread_id);
|
assert_eq!(thread::current().id(), *thread_id);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::{Entities, Entity},
|
entity::{Entities, Entity},
|
||||||
world::World,
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
use bevy_utils::tracing::{error, warn};
|
use bevy_utils::tracing::{error, warn};
|
||||||
pub use command_queue::CommandQueue;
|
pub use command_queue::CommandQueue;
|
||||||
@ -261,9 +261,45 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
self.queue.push(InsertOrSpawnBatch { bundles_iter });
|
self.queue.push(InsertOrSpawnBatch { bundles_iter });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts a resource with standard starting values to the [`World`].
|
||||||
|
///
|
||||||
|
/// If the resource already exists, nothing happens.
|
||||||
|
///
|
||||||
|
/// The value given by the [`FromWorld::from_world`] method will be used.
|
||||||
|
/// Note that any resource with the `Default` trait automatically implements `FromWorld`,
|
||||||
|
/// and those default values will be here instead.
|
||||||
|
///
|
||||||
|
/// See [`World::init_resource`] for more details.
|
||||||
|
/// Note that commands do not take effect immediately.
|
||||||
|
/// When possible, prefer the equivalent methods on `App` or `World`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_ecs::prelude::*;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Default)]
|
||||||
|
/// # struct Scoreboard {
|
||||||
|
/// # current_score: u32,
|
||||||
|
/// # high_score: u32,
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// # fn system(mut commands: Commands) {
|
||||||
|
/// commands.init_resource::<Scoreboard>();
|
||||||
|
/// # }
|
||||||
|
/// # system.system();
|
||||||
|
/// ```
|
||||||
|
pub fn init_resource<R: Resource + FromWorld>(&mut self) {
|
||||||
|
self.queue.push(InitResource::<R> {
|
||||||
|
_phantom: PhantomData::<R>::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts a resource to the [`World`], overwriting any previous value of the same type.
|
/// Inserts a resource to the [`World`], overwriting any previous value of the same type.
|
||||||
///
|
///
|
||||||
/// See [`World::insert_resource`] for more details.
|
/// See [`World::insert_resource`] for more details.
|
||||||
|
/// Note that commands do not take effect immediately.
|
||||||
|
/// When possible, prefer the equivalent methods on `App` or `World`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -283,7 +319,7 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// # bevy_ecs::system::assert_is_system(system);
|
/// # bevy_ecs::system::assert_is_system(system);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_resource<T: Resource>(&mut self, resource: T) {
|
pub fn insert_resource<R: Resource>(&mut self, resource: R) {
|
||||||
self.queue.push(InsertResource { resource })
|
self.queue.push(InsertResource { resource })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,8 +342,8 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// # bevy_ecs::system::assert_is_system(system);
|
/// # bevy_ecs::system::assert_is_system(system);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn remove_resource<T: Resource>(&mut self) {
|
pub fn remove_resource<R: Resource>(&mut self) {
|
||||||
self.queue.push(RemoveResource::<T> {
|
self.queue.push(RemoveResource::<R> {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -713,23 +749,33 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InsertResource<T: Resource> {
|
pub struct InitResource<R: Resource + FromWorld> {
|
||||||
pub resource: T,
|
_phantom: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Resource> Command for InsertResource<T> {
|
impl<R: Resource + FromWorld> Command for InitResource<R> {
|
||||||
|
fn write(self, world: &mut World) {
|
||||||
|
world.init_resource::<R>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InsertResource<R: Resource> {
|
||||||
|
pub resource: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Resource> Command for InsertResource<R> {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
world.insert_resource(self.resource);
|
world.insert_resource(self.resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RemoveResource<T: Resource> {
|
pub struct RemoveResource<R: Resource> {
|
||||||
pub phantom: PhantomData<T>,
|
pub phantom: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Resource> Command for RemoveResource<T> {
|
impl<R: Resource> Command for RemoveResource<R> {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
world.remove_resource::<T>();
|
world.remove_resource::<R>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ mod tests {
|
|||||||
world.insert_resource(false);
|
world.insert_resource(false);
|
||||||
struct NotSend1(std::rc::Rc<i32>);
|
struct NotSend1(std::rc::Rc<i32>);
|
||||||
struct NotSend2(std::rc::Rc<i32>);
|
struct NotSend2(std::rc::Rc<i32>);
|
||||||
world.insert_non_send(NotSend1(std::rc::Rc::new(0)));
|
world.insert_non_send_resource(NotSend1(std::rc::Rc::new(0)));
|
||||||
|
|
||||||
fn sys(
|
fn sys(
|
||||||
op: Option<NonSend<NotSend1>>,
|
op: Option<NonSend<NotSend1>>,
|
||||||
@ -446,8 +446,8 @@ mod tests {
|
|||||||
struct NotSend1(std::rc::Rc<i32>);
|
struct NotSend1(std::rc::Rc<i32>);
|
||||||
struct NotSend2(std::rc::Rc<i32>);
|
struct NotSend2(std::rc::Rc<i32>);
|
||||||
|
|
||||||
world.insert_non_send(NotSend1(std::rc::Rc::new(1)));
|
world.insert_non_send_resource(NotSend1(std::rc::Rc::new(1)));
|
||||||
world.insert_non_send(NotSend2(std::rc::Rc::new(2)));
|
world.insert_non_send_resource(NotSend2(std::rc::Rc::new(2)));
|
||||||
|
|
||||||
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
|
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
|
||||||
*run = true;
|
*run = true;
|
||||||
|
@ -592,46 +592,88 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new resource with the given `value`.
|
/// Inserts a new resource with standard starting values.
|
||||||
/// Resources are "unique" data of a given type.
|
///
|
||||||
|
/// If the resource already exists, nothing happens.
|
||||||
|
///
|
||||||
|
/// The value given by the [`FromWorld::from_world`] method will be used.
|
||||||
|
/// Note that any resource with the `Default` trait automatically implements `FromWorld`,
|
||||||
|
/// and those default values will be here instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_resource<T: Resource>(&mut self, value: T) {
|
pub fn init_resource<R: Resource + FromWorld>(&mut self) {
|
||||||
let component_id = self.components.init_resource::<T>();
|
// PERF: We could avoid double hashing here, since the `from_world` call is guaranteed
|
||||||
|
// not to modify the map. However, we would need to be borrowing resources both
|
||||||
|
// mutably and immutably, so we would need to be extremely certain this is correct
|
||||||
|
if !self.contains_resource::<R>() {
|
||||||
|
let resource = R::from_world(self);
|
||||||
|
self.insert_resource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new resource with the given `value`.
|
||||||
|
///
|
||||||
|
/// Resources are "unique" data of a given type.
|
||||||
|
/// If you insert a resource of a type that already exists,
|
||||||
|
/// you will overwrite any existing data.
|
||||||
|
#[inline]
|
||||||
|
pub fn insert_resource<R: Resource>(&mut self, value: R) {
|
||||||
|
let component_id = self.components.init_resource::<R>();
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type T
|
// SAFE: component_id just initialized and corresponds to resource of type T
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new non-send resource with the given `value`.
|
/// Inserts a new non-send resource with standard starting values.
|
||||||
/// Resources are "unique" data of a given type.
|
///
|
||||||
|
/// If the resource already exists, nothing happens.
|
||||||
|
///
|
||||||
|
/// The value given by the [`FromWorld::from_world`] method will be used.
|
||||||
|
/// Note that any resource with the `Default` trait automatically implements `FromWorld`,
|
||||||
|
/// and those default values will be here instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_non_send<T: 'static>(&mut self, value: T) {
|
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) {
|
||||||
self.validate_non_send_access::<T>();
|
// PERF: We could avoid double hashing here, since the `from_world` call is guaranteed
|
||||||
let component_id = self.components.init_non_send::<T>();
|
// not to modify the map. However, we would need to be borrowing resources both
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type T
|
// mutably and immutably, so we would need to be extremely certain this is correct
|
||||||
|
if !self.contains_resource::<R>() {
|
||||||
|
let resource = R::from_world(self);
|
||||||
|
self.insert_non_send_resource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new non-send resource with the given `value`.
|
||||||
|
///
|
||||||
|
/// `NonSend` resources cannot be sent across threads,
|
||||||
|
/// and do not need the `Send + Sync` bounds.
|
||||||
|
/// Systems with `NonSend` resources are always scheduled on the main thread.
|
||||||
|
#[inline]
|
||||||
|
pub fn insert_non_send_resource<R: 'static>(&mut self, value: R) {
|
||||||
|
self.validate_non_send_access::<R>();
|
||||||
|
let component_id = self.components.init_non_send::<R>();
|
||||||
|
// SAFE: component_id just initialized and corresponds to resource of type R
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
||||||
/// Resources are "unique" data of a given type.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_resource<T: Resource>(&mut self) -> Option<T> {
|
pub fn remove_resource<R: Resource>(&mut self) -> Option<R> {
|
||||||
// SAFE: T is Send + Sync
|
// SAFE: R is Send + Sync
|
||||||
unsafe { self.remove_resource_unchecked() }
|
unsafe { self.remove_resource_unchecked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_non_send<T: 'static>(&mut self) -> Option<T> {
|
pub fn remove_non_send_resource<R: 'static>(&mut self) -> Option<R> {
|
||||||
self.validate_non_send_access::<T>();
|
self.validate_non_send_access::<R>();
|
||||||
// SAFE: we are on main thread
|
// SAFE: we are on main thread
|
||||||
unsafe { self.remove_resource_unchecked() }
|
unsafe { self.remove_resource_unchecked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// make sure you're on main thread if T isn't Send + Sync
|
/// Only remove `NonSend` resources from the main thread
|
||||||
|
/// as they cannot be sent across theads
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
pub unsafe fn remove_resource_unchecked<T: 'static>(&mut self) -> Option<T> {
|
pub unsafe fn remove_resource_unchecked<R: 'static>(&mut self) -> Option<R> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
let resource_archetype = self.archetypes.resource_mut();
|
let resource_archetype = self.archetypes.resource_mut();
|
||||||
let unique_components = resource_archetype.unique_components_mut();
|
let unique_components = resource_archetype.unique_components_mut();
|
||||||
let column = unique_components.get_mut(component_id)?;
|
let column = unique_components.get_mut(component_id)?;
|
||||||
@ -639,17 +681,17 @@ impl World {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the
|
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the
|
||||||
// ptr value / drop is called when T is dropped
|
// ptr value / drop is called when R is dropped
|
||||||
let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
|
let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
|
||||||
// SAFE: column is of type T
|
// SAFE: column is of type R
|
||||||
Some(unsafe { ptr.cast::<T>().read() })
|
Some(unsafe { ptr.cast::<R>().read() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
|
/// Returns `true` if a resource of type `R` exists. Otherwise returns `false`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_resource<T: Resource>(&self) -> bool {
|
pub fn contains_resource<R: 'static>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<R>()) {
|
||||||
component_id
|
component_id
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -658,16 +700,15 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
|
/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
|
||||||
/// Resources are "unique" data of a given type.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource<T: Resource>(&self) -> Option<&T> {
|
pub fn get_resource<R: Resource>(&self) -> Option<&R> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
unsafe { self.get_resource_with_id(component_id) }
|
unsafe { self.get_resource_with_id(component_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_resource_added<T: Resource>(&self) -> bool {
|
pub fn is_resource_added<R: Resource>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<R>()) {
|
||||||
component_id
|
component_id
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -682,9 +723,9 @@ impl World {
|
|||||||
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_resource_changed<T: Resource>(&self) -> bool {
|
pub fn is_resource_changed<R: Resource>(&self) -> bool {
|
||||||
let component_id =
|
let component_id =
|
||||||
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
|
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<R>()) {
|
||||||
component_id
|
component_id
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -700,65 +741,64 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
|
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
|
||||||
/// [None] Resources are "unique" data of a given type.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource_mut<T: Resource>(&mut self) -> Option<Mut<'_, T>> {
|
pub fn get_resource_mut<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
|
||||||
// SAFE: unique world access
|
// SAFE: unique world access
|
||||||
unsafe { self.get_resource_unchecked_mut() }
|
unsafe { self.get_resource_unchecked_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// PERF: optimize this to avoid redundant lookups
|
// PERF: optimize this to avoid redundant lookups
|
||||||
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
|
/// Gets a resource of type `T` if it exists,
|
||||||
/// calling `func`.
|
/// otherwise inserts the resource using the result of calling `func`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource_or_insert_with<T: Resource>(
|
pub fn get_resource_or_insert_with<R: Resource>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl FnOnce() -> T,
|
func: impl FnOnce() -> R,
|
||||||
) -> Mut<'_, T> {
|
) -> Mut<'_, R> {
|
||||||
if !self.contains_resource::<T>() {
|
if !self.contains_resource::<R>() {
|
||||||
self.insert_resource(func());
|
self.insert_resource(func());
|
||||||
}
|
}
|
||||||
self.get_resource_mut().unwrap()
|
self.get_resource_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the resource of the given type, if it exists. Otherwise returns
|
/// Gets a mutable reference to the resource of the given type, if it exists
|
||||||
/// [None] Resources are "unique" data of a given type.
|
/// Otherwise returns [None]
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
||||||
/// that only one mutable access exists at a time.
|
/// that only one mutable access exists at a time.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_resource_unchecked_mut<T: Resource>(&self) -> Option<Mut<'_, T>> {
|
pub unsafe fn get_resource_unchecked_mut<R: Resource>(&self) -> Option<Mut<'_, R>> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
self.get_resource_unchecked_mut_with_id(component_id)
|
self.get_resource_unchecked_mut_with_id(component_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the non-send resource of the given type, if it exists. Otherwise returns
|
/// Gets a reference to the non-send resource of the given type, if it exists.
|
||||||
/// [None] Resources are "unique" data of a given type.
|
/// Otherwise returns [None]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_non_send_resource<T: 'static>(&self) -> Option<&T> {
|
pub fn get_non_send_resource<R: 'static>(&self) -> Option<&R> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
// SAFE: component id matches type T
|
// SAFE: component id matches type T
|
||||||
unsafe { self.get_non_send_with_id(component_id) }
|
unsafe { self.get_non_send_with_id(component_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise
|
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
|
||||||
/// returns [None] Resources are "unique" data of a given type.
|
/// Otherwise returns [None]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_non_send_resource_mut<T: 'static>(&mut self) -> Option<Mut<'_, T>> {
|
pub fn get_non_send_resource_mut<R: 'static>(&mut self) -> Option<Mut<'_, R>> {
|
||||||
// SAFE: unique world access
|
// SAFE: unique world access
|
||||||
unsafe { self.get_non_send_resource_unchecked_mut() }
|
unsafe { self.get_non_send_resource_unchecked_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the non-send resource of the given type, if it exists. Otherwise
|
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
|
||||||
/// returns [None] Resources are "unique" data of a given type.
|
/// Otherwise returns [None]
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// This will allow aliased mutable access to the given non-send resource type. The caller must
|
/// This will allow aliased mutable access to the given non-send resource type. The caller must
|
||||||
/// ensure that only one mutable access exists at a time.
|
/// ensure that only one mutable access exists at a time.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_non_send_resource_unchecked_mut<T: 'static>(&self) -> Option<Mut<'_, T>> {
|
pub unsafe fn get_non_send_resource_unchecked_mut<R: 'static>(&self) -> Option<Mut<'_, R>> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
self.get_non_send_unchecked_mut_with_id(component_id)
|
self.get_non_send_unchecked_mut_with_id(component_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,27 +948,27 @@ impl World {
|
|||||||
/// });
|
/// });
|
||||||
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
|
/// assert_eq!(world.get_resource::<A>().unwrap().0, 2);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn resource_scope<T: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<T>) -> U) -> U {
|
pub fn resource_scope<R: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<R>) -> U) -> U {
|
||||||
let component_id = self
|
let component_id = self
|
||||||
.components
|
.components
|
||||||
.get_resource_id(TypeId::of::<T>())
|
.get_resource_id(TypeId::of::<R>())
|
||||||
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<T>()));
|
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<R>()));
|
||||||
let (ptr, mut ticks) = {
|
let (ptr, mut ticks) = {
|
||||||
let resource_archetype = self.archetypes.resource_mut();
|
let resource_archetype = self.archetypes.resource_mut();
|
||||||
let unique_components = resource_archetype.unique_components_mut();
|
let unique_components = resource_archetype.unique_components_mut();
|
||||||
let column = unique_components.get_mut(component_id).unwrap_or_else(|| {
|
let column = unique_components.get_mut(component_id).unwrap_or_else(|| {
|
||||||
panic!("resource does not exist: {}", std::any::type_name::<T>())
|
panic!("resource does not exist: {}", std::any::type_name::<R>())
|
||||||
});
|
});
|
||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
panic!("resource does not exist: {}", std::any::type_name::<T>());
|
panic!("resource does not exist: {}", std::any::type_name::<R>());
|
||||||
}
|
}
|
||||||
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of
|
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of
|
||||||
// the ptr value / drop is called when T is dropped
|
// the ptr value / drop is called when R is dropped
|
||||||
unsafe { column.swap_remove_and_forget_unchecked(0) }
|
unsafe { column.swap_remove_and_forget_unchecked(0) }
|
||||||
};
|
};
|
||||||
// SAFE: pointer is of type T and valid to move out of
|
// SAFE: pointer is of type T and valid to move out of
|
||||||
// Read the value onto the stack to avoid potential mut aliasing.
|
// Read the value onto the stack to avoid potential mut aliasing.
|
||||||
let mut value = unsafe { std::ptr::read(ptr.cast::<T>()) };
|
let mut value = unsafe { std::ptr::read(ptr.cast::<R>()) };
|
||||||
let value_mut = Mut {
|
let value_mut = Mut {
|
||||||
value: &mut value,
|
value: &mut value,
|
||||||
ticks: Ticks {
|
ticks: Ticks {
|
||||||
@ -938,12 +978,12 @@ impl World {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let result = f(self, value_mut);
|
let result = f(self, value_mut);
|
||||||
assert!(!self.contains_resource::<T>());
|
assert!(!self.contains_resource::<R>());
|
||||||
let resource_archetype = self.archetypes.resource_mut();
|
let resource_archetype = self.archetypes.resource_mut();
|
||||||
let unique_components = resource_archetype.unique_components_mut();
|
let unique_components = resource_archetype.unique_components_mut();
|
||||||
let column = unique_components
|
let column = unique_components
|
||||||
.get_mut(component_id)
|
.get_mut(component_id)
|
||||||
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<T>()));
|
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<R>()));
|
||||||
|
|
||||||
// Wrap the value in MaybeUninit to prepare for passing the value back into the ECS
|
// Wrap the value in MaybeUninit to prepare for passing the value back into the ECS
|
||||||
let mut nodrop_wrapped_value = std::mem::MaybeUninit::new(value);
|
let mut nodrop_wrapped_value = std::mem::MaybeUninit::new(value);
|
||||||
@ -955,27 +995,27 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be assigned to a component of type T
|
/// `component_id` must be assigned to a component of type `R`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn get_resource_with_id<T: 'static>(
|
pub(crate) unsafe fn get_resource_with_id<R: 'static>(
|
||||||
&self,
|
&self,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<&T> {
|
) -> Option<&R> {
|
||||||
let column = self.get_populated_resource_column(component_id)?;
|
let column = self.get_populated_resource_column(component_id)?;
|
||||||
Some(&*column.get_data_ptr().as_ptr().cast::<T>())
|
Some(&*column.get_data_ptr().as_ptr().cast::<R>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be assigned to a component of type T.
|
/// `component_id` must be assigned to a component of type `R`
|
||||||
/// Caller must ensure this doesn't violate Rust mutability rules for the given resource.
|
/// Caller must ensure this doesn't violate Rust mutability rules for the given resource.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn get_resource_unchecked_mut_with_id<T>(
|
pub(crate) unsafe fn get_resource_unchecked_mut_with_id<R>(
|
||||||
&self,
|
&self,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<Mut<'_, T>> {
|
) -> Option<Mut<'_, R>> {
|
||||||
let column = self.get_populated_resource_column(component_id)?;
|
let column = self.get_populated_resource_column(component_id)?;
|
||||||
Some(Mut {
|
Some(Mut {
|
||||||
value: &mut *column.get_data_ptr().cast::<T>().as_ptr(),
|
value: &mut *column.get_data_ptr().cast::<R>().as_ptr(),
|
||||||
ticks: Ticks {
|
ticks: Ticks {
|
||||||
component_ticks: &mut *column.get_ticks_mut_ptr_unchecked(0),
|
component_ticks: &mut *column.get_ticks_mut_ptr_unchecked(0),
|
||||||
last_change_tick: self.last_change_tick(),
|
last_change_tick: self.last_change_tick(),
|
||||||
@ -985,48 +1025,48 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be assigned to a component of type T
|
/// `component_id` must be assigned to a component of type `R`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn get_non_send_with_id<T: 'static>(
|
pub(crate) unsafe fn get_non_send_with_id<R: 'static>(
|
||||||
&self,
|
&self,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<&T> {
|
) -> Option<&R> {
|
||||||
self.validate_non_send_access::<T>();
|
self.validate_non_send_access::<R>();
|
||||||
self.get_resource_with_id(component_id)
|
self.get_resource_with_id(component_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be assigned to a component of type T.
|
/// `component_id` must be assigned to a component of type `R`.
|
||||||
/// Caller must ensure this doesn't violate Rust mutability rules for the given resource.
|
/// Caller must ensure this doesn't violate Rust mutability rules for the given resource.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn get_non_send_unchecked_mut_with_id<T: 'static>(
|
pub(crate) unsafe fn get_non_send_unchecked_mut_with_id<R: 'static>(
|
||||||
&self,
|
&self,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<Mut<'_, T>> {
|
) -> Option<Mut<'_, R>> {
|
||||||
self.validate_non_send_access::<T>();
|
self.validate_non_send_access::<R>();
|
||||||
self.get_resource_unchecked_mut_with_id(component_id)
|
self.get_resource_unchecked_mut_with_id(component_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be valid and correspond to a resource component of type T
|
/// `component_id` must be valid and correspond to a resource component of type `R`
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn insert_resource_with_id<T>(&mut self, component_id: ComponentId, value: T) {
|
unsafe fn insert_resource_with_id<R>(&mut self, component_id: ComponentId, value: R) {
|
||||||
let change_tick = self.change_tick();
|
let change_tick = self.change_tick();
|
||||||
let column = self.initialize_resource_internal(component_id);
|
let column = self.initialize_resource_internal(component_id);
|
||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
let mut value = ManuallyDrop::new(value);
|
let mut value = ManuallyDrop::new(value);
|
||||||
// SAFE: column is of type T and has been allocated above
|
// SAFE: column is of type R and has been allocated above
|
||||||
let data = (&mut *value as *mut T).cast::<u8>();
|
let data = (&mut *value as *mut R).cast::<u8>();
|
||||||
column.push(data, ComponentTicks::new(change_tick));
|
column.push(data, ComponentTicks::new(change_tick));
|
||||||
} else {
|
} else {
|
||||||
// SAFE: column is of type T and has already been allocated
|
// SAFE: column is of type R and has already been allocated
|
||||||
*column.get_data_unchecked(0).cast::<T>() = value;
|
*column.get_data_unchecked(0).cast::<R>() = value;
|
||||||
column.get_ticks_unchecked_mut(0).set_changed(change_tick);
|
column.get_ticks_unchecked_mut(0).set_changed(change_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `component_id` must be valid and correspond to a resource component of type T
|
/// `component_id` must be valid and correspond to a resource component of type `R`
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
|
unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
|
||||||
// SAFE: resource archetype always exists
|
// SAFE: resource archetype always exists
|
||||||
@ -1055,15 +1095,15 @@ impl World {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn initialize_resource<T: Resource>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_resource<R: Resource>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.init_resource::<T>();
|
let component_id = self.components.init_resource::<R>();
|
||||||
// SAFE: resource initialized above
|
// SAFE: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_non_send_resource<R: 'static>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.init_non_send::<T>();
|
let component_id = self.components.init_non_send::<R>();
|
||||||
// SAFE: resource initialized above
|
// SAFE: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
@ -1171,7 +1211,10 @@ impl fmt::Debug for World {
|
|||||||
unsafe impl Send for World {}
|
unsafe impl Send for World {}
|
||||||
unsafe impl Sync for World {}
|
unsafe impl Sync for World {}
|
||||||
|
|
||||||
/// Creates `Self` using data from the given [World]
|
/// Creates an instance of the type this trait is implemented for
|
||||||
|
/// using data from the supplied [World].
|
||||||
|
///
|
||||||
|
/// This can be helpful for complex initialization or context-aware defaults.
|
||||||
pub trait FromWorld {
|
pub trait FromWorld {
|
||||||
/// Creates `Self` using data from the given [World]
|
/// Creates `Self` using data from the given [World]
|
||||||
fn from_world(world: &mut World) -> Self;
|
fn from_world(world: &mut World) -> Self;
|
||||||
|
@ -133,7 +133,7 @@ impl Plugin for LogPlugin {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.build();
|
.build();
|
||||||
app.world.insert_non_send(guard);
|
app.world.insert_non_send_resource(guard);
|
||||||
chrome_layer
|
chrome_layer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -223,10 +223,14 @@ pub fn winit_runner(app: App) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn winit_runner_with(mut app: App) {
|
pub fn winit_runner_with(mut app: App) {
|
||||||
let mut event_loop = app.world.remove_non_send::<EventLoop<()>>().unwrap();
|
let mut event_loop = app
|
||||||
|
.world
|
||||||
|
.remove_non_send_resource::<EventLoop<()>>()
|
||||||
|
.unwrap();
|
||||||
let mut create_window_event_reader = ManualEventReader::<CreateWindow>::default();
|
let mut create_window_event_reader = ManualEventReader::<CreateWindow>::default();
|
||||||
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
|
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
|
||||||
app.world.insert_non_send(event_loop.create_proxy());
|
app.world
|
||||||
|
.insert_non_send_resource(event_loop.create_proxy());
|
||||||
|
|
||||||
trace!("Entering winit event loop");
|
trace!("Entering winit event loop");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user