-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Immediate Execution of Commands on &mut World #6184
Description
What problem does this solve or what need does it fill?
Currently, to write a test for a command, we have 2 options:
- Either create a minimal app with a system that uses the command, or
- Use
CommandQueuewith aWorldto execute commands "in place"
I've noticed we already use option 2 in Bevy code:
fn remove_resources() {
let mut world = World::default();
let mut queue = CommandQueue::default();
{
let mut commands = Commands::new(&mut queue, &world);
commands.insert_resource(W(123i32));
commands.insert_resource(W(456.0f64));
}
queue.apply(&mut world);
assert!(world.contains_resource::<W<i32>>());
assert!(world.contains_resource::<W<f64>>());
{
let mut commands = Commands::new(&mut queue, &world);
// test resource removal
commands.remove_resource::<W<i32>>();
}
queue.apply(&mut world);
assert!(!world.contains_resource::<W<i32>>());
assert!(world.contains_resource::<W<f64>>());
}My proposal is that we simplify this by adding a function to execute commands directly on a world.
What solution would you like?
I have a minimal working implementation of this in my own project:
use bevy::prelude::*;
use bevy::ecs::system::CommandQueue;
///
/// Trait to execute [`Commands`] on a [`World`].
///
pub trait Execute {
fn execute<F: FnOnce(&World, Commands) -> R, R>(self, f: F) -> R;
}
///
/// Any mutable reference to a [`World`] may be used to execute [`Commands`] on it.
///
impl Execute for &mut World {
fn execute<F: FnOnce(&World, Commands) -> R, R>(self, f: F) -> R {
let mut queue = CommandQueue::default();
let commands = Commands::new(&mut queue, self);
let result = f(self, commands);
queue.apply(self);
result
}
}
///
/// Any mutable reference to an [`App`] may be used to execute [`Commands`] on its [`World`].
///
impl Execute for &mut App {
fn execute<F: FnOnce(&World, Commands) -> R, R>(self, f: F) -> R {
self.world.execute(f)
}
}This allows you to just do this anywhere you have &mut World access:
world.execute(|_world, mut commands| {
/* ... */
});What alternative(s) have you considered?
See above for current options.
Additional context
I tried making this a direct pull request. But as I was implementing it, I wasn't sure where this should go. I was thinking of putting Execute in bevy_ecs/src/system/commands/mod.rs, but I wasn't sure if it's smart to import Commands in bevy_app/src/lib.rs
If we're ok with the implementation I've proposed above, and importing Commands in Bevy app, then I can just turn this into a PR.
Also I was internally debating guarding this behind a feature flag, so that it's only available for tests/examples. Still not sure if that's wise.