mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 22:20:20 +00:00
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
8 changed files with 222 additions and 147 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -133,7 +133,7 @@ impl Plugin for LogPlugin {
|
|||
}
|
||||
}))
|
||||
.build();
|
||||
app.world.insert_non_send(guard);
|
||||
app.world.insert_non_send_resource(guard);
|
||||
chrome_layer
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue