-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Conversation
Fix missing semicolon
There was a problem hiding this 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.
Fix documentation
As a heads up, I just added |
This is no longer blocked! |
Not sure what's going on with the validation here. This is already moved to |
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. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
.
@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. |
There was a problem hiding this 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.
world.apply_commands(|_, mut commands| { | ||
commands.add(InsertOrAddRequest(entity, Request)); | ||
commands.add(InsertOrAddRequest(entity, Request)); | ||
}); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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...?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Closing in favor of |
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: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
toApplyCommands
because I think it matches better with existing terminology, and simplifiedhow_to_test_commands.rs
.