Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Implement init_resource for Commands and World #3079

Closed
52 changes: 17 additions & 35 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// ```
Expand All @@ -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`]
Copy link
Contributor

@jamesbeilby jamesbeilby Nov 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor suggestion to avoid the word "initialize" as it's often ambiguous whether it's separate to creation; perhaps instead:

Create a resource with its standard starting values and insert it into the [World]

Or:

Ensure a resource of the given type exists in the [World]. If it does not, insert a new instance of this resource with its standard starting values.

///
/// 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
/// ```
Expand All @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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>();
})
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/executor_parallel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
68 changes: 57 additions & 11 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
///
Expand All @@ -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 })
}

Expand All @@ -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,
});
}
Expand Down Expand Up @@ -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>();
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
Expand All @@ -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;
Expand Down
Loading