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()
/// .insert_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
where
T: Resource,
{
pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
self.world.insert_resource(resource);
self
}
/// 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
/// be non-send.
/// You usually want to use `insert_resource`,
/// but there are some special cases when a resource cannot be sent across threads.
///
/// ## Example
/// ```
@ -671,19 +668,18 @@ impl App {
/// App::new()
/// .insert_non_send_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_non_send_resource<T>(&mut self, resource: T) -> &mut Self
where
T: 'static,
{
self.world.insert_non_send(resource);
pub fn insert_non_send_resource<R: 'static>(&mut self, resource: R) -> &mut Self {
self.world.insert_non_send_resource(resource);
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.
///
/// 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
/// ```
@ -704,32 +700,18 @@ impl App {
/// App::new()
/// .init_resource::<MyCounter>();
/// ```
pub fn init_resource<R>(&mut self) -> &mut Self
where
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);
}
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
self.world.init_resource::<R>();
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.
pub fn init_non_send_resource<R>(&mut self) -> &mut Self
where
R: FromWorld + 'static,
{
// 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);
}
/// 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.
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) -> &mut Self {
self.world.init_non_send_resource::<R>();
self
}

View file

@ -1181,8 +1181,8 @@ mod tests {
#[test]
fn non_send_resource() {
let mut world = World::default();
world.insert_non_send(123i32);
world.insert_non_send(456i64);
world.insert_non_send_resource(123i32);
world.insert_non_send_resource(456i64);
assert_eq!(*world.get_non_send_resource::<i32>().unwrap(), 123);
assert_eq!(*world.get_non_send_resource_mut::<i64>().unwrap(), 456);
}
@ -1191,7 +1191,7 @@ mod tests {
#[should_panic]
fn non_send_resource_panic() {
let mut world = World::default();
world.insert_non_send(0i32);
world.insert_non_send_resource(0i32);
std::thread::spawn(move || {
let _ = world.get_non_send_resource_mut::<i32>();
})

View file

@ -476,7 +476,7 @@ mod tests {
fn non_send_resource() {
use std::thread;
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>) {
assert_eq!(thread::current().id(), *thread_id);
}

View file

@ -4,7 +4,7 @@ use crate::{
bundle::Bundle,
component::Component,
entity::{Entities, Entity},
world::World,
world::{FromWorld, World},
};
use bevy_utils::tracing::{error, warn};
pub use command_queue::CommandQueue;
@ -261,9 +261,45 @@ impl<'w, 's> Commands<'w, 's> {
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.
///
/// 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
///
@ -283,7 +319,7 @@ impl<'w, 's> Commands<'w, 's> {
/// # }
/// # 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 })
}
@ -306,8 +342,8 @@ impl<'w, 's> Commands<'w, 's> {
/// # }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn remove_resource<T: Resource>(&mut self) {
self.queue.push(RemoveResource::<T> {
pub fn remove_resource<R: Resource>(&mut self) {
self.queue.push(RemoveResource::<R> {
phantom: PhantomData,
});
}
@ -713,23 +749,33 @@ where
}
}
pub struct InsertResource<T: Resource> {
pub resource: T,
pub struct InitResource<R: Resource + FromWorld> {
_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) {
world.insert_resource(self.resource);
}
}
pub struct RemoveResource<T: Resource> {
pub phantom: PhantomData<T>,
pub struct RemoveResource<R: Resource> {
pub phantom: PhantomData<R>,
}
impl<T: Resource> Command for RemoveResource<T> {
impl<R: Resource> Command for RemoveResource<R> {
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);
struct NotSend1(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(
op: Option<NonSend<NotSend1>>,
@ -446,8 +446,8 @@ mod tests {
struct NotSend1(std::rc::Rc<i32>);
struct NotSend2(std::rc::Rc<i32>);
world.insert_non_send(NotSend1(std::rc::Rc::new(1)));
world.insert_non_send(NotSend2(std::rc::Rc::new(2)));
world.insert_non_send_resource(NotSend1(std::rc::Rc::new(1)));
world.insert_non_send_resource(NotSend2(std::rc::Rc::new(2)));
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
*run = true;

View file

@ -592,46 +592,88 @@ impl World {
}
}
/// Inserts a new resource with the given `value`.
/// Resources are "unique" data of a given type.
/// Inserts a new resource with standard starting values.
///
/// 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]
pub fn insert_resource<T: Resource>(&mut self, value: T) {
let component_id = self.components.init_resource::<T>();
pub fn init_resource<R: Resource + FromWorld>(&mut self) {
// 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
unsafe { self.insert_resource_with_id(component_id, value) };
}
/// Inserts a new non-send resource with the given `value`.
/// Resources are "unique" data of a given type.
/// Inserts a new non-send resource with standard starting values.
///
/// 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]
pub fn insert_non_send<T: 'static>(&mut self, value: T) {
self.validate_non_send_access::<T>();
let component_id = self.components.init_non_send::<T>();
// SAFE: component_id just initialized and corresponds to resource of type T
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) {
// 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_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) };
}
/// 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]
pub fn remove_resource<T: Resource>(&mut self) -> Option<T> {
// SAFE: T is Send + Sync
pub fn remove_resource<R: Resource>(&mut self) -> Option<R> {
// SAFE: R is Send + Sync
unsafe { self.remove_resource_unchecked() }
}
#[inline]
pub fn remove_non_send<T: 'static>(&mut self) -> Option<T> {
self.validate_non_send_access::<T>();
pub fn remove_non_send_resource<R: 'static>(&mut self) -> Option<R> {
self.validate_non_send_access::<R>();
// SAFE: we are on main thread
unsafe { self.remove_resource_unchecked() }
}
#[inline]
/// # 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)]
pub unsafe fn remove_resource_unchecked<T: 'static>(&mut self) -> Option<T> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
pub unsafe fn remove_resource_unchecked<R: 'static>(&mut self) -> Option<R> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
let resource_archetype = self.archetypes.resource_mut();
let unique_components = resource_archetype.unique_components_mut();
let column = unique_components.get_mut(component_id)?;
@ -639,17 +681,17 @@ impl World {
return None;
}
// 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) };
// SAFE: column is of type T
Some(unsafe { ptr.cast::<T>().read() })
// SAFE: column is of type R
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]
pub fn contains_resource<T: Resource>(&self) -> bool {
pub fn contains_resource<R: 'static>(&self) -> bool {
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
} else {
return false;
@ -658,16 +700,15 @@ impl World {
}
/// 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]
pub fn get_resource<T: Resource>(&self) -> Option<&T> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
pub fn get_resource<R: Resource>(&self) -> Option<&R> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
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 =
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
} else {
return false;
@ -682,9 +723,9 @@ impl World {
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 =
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
} else {
return false;
@ -700,65 +741,64 @@ impl World {
}
/// 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]
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
unsafe { self.get_resource_unchecked_mut() }
}
// PERF: optimize this to avoid redundant lookups
/// Gets a resource of type `T` if it exists, otherwise inserts the resource using the result of
/// calling `func`.
/// Gets a resource of type `T` if it exists,
/// otherwise inserts the resource using the result of calling `func`.
#[inline]
pub fn get_resource_or_insert_with<T: Resource>(
pub fn get_resource_or_insert_with<R: Resource>(
&mut self,
func: impl FnOnce() -> T,
) -> Mut<'_, T> {
if !self.contains_resource::<T>() {
func: impl FnOnce() -> R,
) -> Mut<'_, R> {
if !self.contains_resource::<R>() {
self.insert_resource(func());
}
self.get_resource_mut().unwrap()
}
/// 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.
/// Gets a mutable reference to the resource of the given type, if it exists
/// Otherwise returns [None]
///
/// # Safety
/// This will allow aliased mutable access to the given resource type. The caller must ensure
/// that only one mutable access exists at a time.
#[inline]
pub unsafe fn get_resource_unchecked_mut<T: Resource>(&self) -> Option<Mut<'_, T>> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
pub unsafe fn get_resource_unchecked_mut<R: Resource>(&self) -> Option<Mut<'_, R>> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
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
/// [None] Resources are "unique" data of a given type.
/// Gets a reference to the non-send resource of the given type, if it exists.
/// Otherwise returns [None]
#[inline]
pub fn get_non_send_resource<T: 'static>(&self) -> Option<&T> {
let component_id = self.components.get_resource_id(TypeId::of::<T>())?;
pub fn get_non_send_resource<R: 'static>(&self) -> Option<&R> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
// SAFE: component id matches type T
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
/// returns [None] Resources are "unique" data of a given type.
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
/// Otherwise returns [None]
#[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
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
/// returns [None] Resources are "unique" data of a given type.
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
/// Otherwise returns [None]
///
/// # Safety
/// 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.
#[inline]
pub unsafe fn get_non_send_resource_unchecked_mut<T: 'static>(&self) -> Option<Mut<'_, T>> {
let component_id = self.components.get_resource_id(TypeId::of::<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::<R>())?;
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);
/// ```
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
.components
.get_resource_id(TypeId::of::<T>())
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<T>()));
.get_resource_id(TypeId::of::<R>())
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<R>()));
let (ptr, mut ticks) = {
let resource_archetype = self.archetypes.resource_mut();
let unique_components = resource_archetype.unique_components_mut();
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() {
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
// 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) }
};
// SAFE: pointer is of type T and valid to move out of
// 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 {
value: &mut value,
ticks: Ticks {
@ -938,12 +978,12 @@ impl World {
},
};
let result = f(self, value_mut);
assert!(!self.contains_resource::<T>());
assert!(!self.contains_resource::<R>());
let resource_archetype = self.archetypes.resource_mut();
let unique_components = resource_archetype.unique_components_mut();
let column = unique_components
.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
let mut nodrop_wrapped_value = std::mem::MaybeUninit::new(value);
@ -955,27 +995,27 @@ impl World {
}
/// # Safety
/// `component_id` must be assigned to a component of type T
/// `component_id` must be assigned to a component of type `R`
#[inline]
pub(crate) unsafe fn get_resource_with_id<T: 'static>(
pub(crate) unsafe fn get_resource_with_id<R: 'static>(
&self,
component_id: ComponentId,
) -> Option<&T> {
) -> Option<&R> {
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
/// `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.
#[inline]
pub(crate) unsafe fn get_resource_unchecked_mut_with_id<T>(
pub(crate) unsafe fn get_resource_unchecked_mut_with_id<R>(
&self,
component_id: ComponentId,
) -> Option<Mut<'_, T>> {
) -> Option<Mut<'_, R>> {
let column = self.get_populated_resource_column(component_id)?;
Some(Mut {
value: &mut *column.get_data_ptr().cast::<T>().as_ptr(),
value: &mut *column.get_data_ptr().cast::<R>().as_ptr(),
ticks: Ticks {
component_ticks: &mut *column.get_ticks_mut_ptr_unchecked(0),
last_change_tick: self.last_change_tick(),
@ -985,48 +1025,48 @@ impl World {
}
/// # Safety
/// `component_id` must be assigned to a component of type T
/// `component_id` must be assigned to a component of type `R`
#[inline]
pub(crate) unsafe fn get_non_send_with_id<T: 'static>(
pub(crate) unsafe fn get_non_send_with_id<R: 'static>(
&self,
component_id: ComponentId,
) -> Option<&T> {
self.validate_non_send_access::<T>();
) -> Option<&R> {
self.validate_non_send_access::<R>();
self.get_resource_with_id(component_id)
}
/// # 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.
#[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,
component_id: ComponentId,
) -> Option<Mut<'_, T>> {
self.validate_non_send_access::<T>();
) -> Option<Mut<'_, R>> {
self.validate_non_send_access::<R>();
self.get_resource_unchecked_mut_with_id(component_id)
}
/// # 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]
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 column = self.initialize_resource_internal(component_id);
if column.is_empty() {
let mut value = ManuallyDrop::new(value);
// SAFE: column is of type T and has been allocated above
let data = (&mut *value as *mut T).cast::<u8>();
// SAFE: column is of type R and has been allocated above
let data = (&mut *value as *mut R).cast::<u8>();
column.push(data, ComponentTicks::new(change_tick));
} else {
// SAFE: column is of type T and has already been allocated
*column.get_data_unchecked(0).cast::<T>() = value;
// SAFE: column is of type R and has already been allocated
*column.get_data_unchecked(0).cast::<R>() = value;
column.get_ticks_unchecked_mut(0).set_changed(change_tick);
}
}
/// # 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]
unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
// SAFE: resource archetype always exists
@ -1055,15 +1095,15 @@ impl World {
})
}
pub(crate) fn initialize_resource<T: Resource>(&mut self) -> ComponentId {
let component_id = self.components.init_resource::<T>();
pub(crate) fn initialize_resource<R: Resource>(&mut self) -> ComponentId {
let component_id = self.components.init_resource::<R>();
// SAFE: resource initialized above
unsafe { self.initialize_resource_internal(component_id) };
component_id
}
pub(crate) fn initialize_non_send_resource<T: 'static>(&mut self) -> ComponentId {
let component_id = self.components.init_non_send::<T>();
pub(crate) fn initialize_non_send_resource<R: 'static>(&mut self) -> ComponentId {
let component_id = self.components.init_non_send::<R>();
// SAFE: resource initialized above
unsafe { self.initialize_resource_internal(component_id) };
component_id
@ -1171,7 +1211,10 @@ impl fmt::Debug for World {
unsafe impl Send 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 {
/// Creates `Self` using data from the given [World]
fn from_world(world: &mut World) -> Self;

View file

@ -133,7 +133,7 @@ impl Plugin for LogPlugin {
}
}))
.build();
app.world.insert_non_send(guard);
app.world.insert_non_send_resource(guard);
chrome_layer
};

View file

@ -223,10 +223,14 @@ pub fn winit_runner(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 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");