Skip to content

Commit

Permalink
Implement init_resource for Commands and World (#3079)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
alice-i-cecile committed Feb 8, 2022
1 parent 38f6da5 commit bdbf626
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 144 deletions.
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`]
///
/// 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

0 comments on commit bdbf626

Please sign in to comment.