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:
Alice Cecile 2022-02-08 23:04:19 +00:00
parent 38f6da5a85
commit bdbf626341
8 changed files with 222 additions and 147 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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