Skip to content

Immediate Execution of Commands on &mut World #6184

@Zeenobit

Description

@Zeenobit

What problem does this solve or what need does it fill?

Currently, to write a test for a command, we have 2 options:

  1. Either create a minimal app with a system that uses the command, or
  2. Use CommandQueue with a World to 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-TestingA change that impacts how we test Bevy or how users test their appsC-UsabilityA targeted quality-of-life change that makes Bevy easier to use

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions