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

Add ApplyCommands (#6184) #7307

Closed
wants to merge 9 commits into from
Closed

Conversation

Zeenobit
Copy link
Contributor

@Zeenobit Zeenobit commented Jan 20, 2023

Objective

Fixed #6184

Solution

This PR adds an ApplyCommands trait which is implemented for &mut World and &mut App. It allows the user to do this:

let mut world = World::default();
let _ = world.apply_commands(|_world, mut commands| {
    /* ... */
});

Initially this PR was posted as #6186. I accidentally deleted the repo, so this is the replacement.
I also took this chance to rename Execute to ApplyCommands because I think it matches better with existing terminology, and simplified how_to_test_commands.rs.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Jan 20, 2023
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

Great docs: this is much better motivated now.

I think this should live in bevy_ecs (still using a trait extension and not in the prelude), and be linked to from the Commands docs.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Jan 20, 2023
@alice-i-cecile
Copy link
Member

As a heads up, I just added Schedule::apply_system_buffers in #7267 (because pipelined rendering needs it). We should wait and see how that shakes out, and then decide whether this is still needed post-Stageless.

@alice-i-cecile alice-i-cecile added the S-Blocked This cannot move forward until something else changes label Jan 20, 2023
@alice-i-cecile alice-i-cecile removed the S-Blocked This cannot move forward until something else changes label Feb 23, 2023
@alice-i-cecile
Copy link
Member

This is no longer blocked!

@Zeenobit
Copy link
Contributor Author

Not sure what's going on with the validation here. This is already moved to bevy_ecs.

@alice-i-cecile
Copy link
Member

Yeah pushing a new commit will probably reset CI: we changed it quite a bit (to remove bors) since your last push.

/// dedicated [`CommandQueue`] per call.
/// However, this function provides a convenient tool for diagnostics and testing because it allows you to
/// invoke commands on a world immediately, without the need for a system.
/// Therefore, its use should be reserved for special cases where performance is not a concern.
Copy link
Contributor

Choose a reason for hiding this comment

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

should add something here to say what you should do for normal situations. i.e. call methods that act directly on World

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I feel like that's a bit overly verbose. It should be axiomatically known that if you have a &mut World, you can modify that world without commands. I'd also argue that methods which act directly on World aren't "normal situations". In normal situations Commands and systems are used to modify the world, which is why we added the link to Commands.

@Zeenobit Zeenobit requested review from alice-i-cecile and hymm and removed request for alice-i-cecile March 18, 2023 17:18
@Zeenobit
Copy link
Contributor Author

Zeenobit commented Jun 5, 2023

@alice-i-cecile Is there anything blocking the integration of this from my end? As far as I can tell all the review concerns have been addressed.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

This has my approval now: it's genuinely useful for tests, and I think the cleanest way to do this.

@alice-i-cecile alice-i-cecile removed the X-Controversial There is active debate or serious implications around merging this PR label Jun 5, 2023
Comment on lines +42 to +45
world.apply_commands(|_, mut commands| {
commands.add(InsertOrAddRequest(entity, Request));
commands.add(InsertOrAddRequest(entity, Request));
});
Copy link
Member

Choose a reason for hiding this comment

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

Why is this preferable to the existing:

InsertOrAddRequest(entity, Request).write(&mut world);
InsertOrAddRequest(entity, Request).write(&mut world);

It doesn't add a new abstraction, uses fewer lines of code, and is more efficient (because it doesn't use an intermediate Commands buffer).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's often desirable to write utility functions that take a &mut Commands in game logic. For example:

fn spawn(commands: &mut Commands) -> Entity {
  let bundle = todo!("Make a bundle");
  commands.spawn(bundle).id()
}

Without this changelist, I can't test such a function without manually creating a CommandQueue, or a duplicate version of the function which works on &mut World.

Copy link
Member

Choose a reason for hiding this comment

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

Fair point! I agree that Commands access might be necessary in some cases. But this should only really be used for tests as allocating and immediately dropping CommandBuffer isn't a pattern we should be encouraging.

Is the risk of making this easy really worth it? Especially when this is almost as ergonomic?

let mut schedule = Schedule::new();
schedule
    .add_systems(move |mut commands: Commands| {
        commands.add(InsertOrAddRequest(entity, Request));
        commands.add(InsertOrAddRequest(entity, Request));
    })
    .run(&mut world);

And if we reeeeeealllly want to make this ergonomic, maybe we should just do this:

world.run_system(move |mut commands: Commands| {
    commands.add(InsertOrAddRequest(entity, Request));
    commands.add(InsertOrAddRequest(entity, Request));
});

Also an anti-pattern in game code (because it will create the system state and immediately drop it). But it removes the special casing of commands and makes this more generally useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would agree that run_system is a superior alternative to apply_commands.

Let me know if you'd like me to modify this CL into run_system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On second thought, there is a downside to run_system compared to apply_commands.

apply_commands returns the return value of its function. This allows the function to return the list of entities spawned, for example. As far as I know, we wouldn't be able to get the output of a system with run_system. Right...?

Copy link
Member

@alice-i-cecile alice-i-cecile Jun 6, 2023

Choose a reason for hiding this comment

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

We could hook up run_system to system piping TBH.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If run_system could return the output of a piped system, that would be most ideal. I'm not sure how I'd approach implementing that though. I could give it a try with some guidance/examples though.

Copy link
Member

Choose a reason for hiding this comment

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

apply_commands returns the return value of its function. This allows the function to return the list of entities spawned, for example. As far as I know, we wouldn't be able to get the output of a system with run_system. Right...?

System impls can have outputs (return values) and run_system can return them. No need for any new abstractions. It would "just work".

Copy link
Member

Choose a reason for hiding this comment

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

I do think we should port this to run_system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good to me. I'll close this PR and open a new one for run_system once I have a working prototype.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Jun 6, 2023
@Zeenobit
Copy link
Contributor Author

Closing in favor of run_system based on feedback.

@Zeenobit Zeenobit closed this Jun 10, 2023
@Zeenobit Zeenobit mentioned this pull request Aug 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Immediate Execution of Commands on &mut World
4 participants