bevy: Teardown/exit systems
What problem does this solve or what need does it fill?
We have systems that run once at the start of an App, the startup systems. What is missing is the opposite: Systems that run once at the end of the program. A specific example of this would be an app that changes terminal state to raw mode at the start, but needs to change it back at the end. Or a game that saves on exit.
What solution would you like?
I think it would be nice if there were the ability to add exit systems to the App in a similar fashion as the way we add startup systems to the App.
What alternative(s) have you considered?
You can currently get pretty much the same behavior by having a system with EventReader<AppExit>. Unfortunately, this behavior depends heavily on system scheduling, as once AppExit is written, and game loop completes, the system exits. To run this after the user exits the window, for example, I had to look up the WindowsPlugin and find out that it added the “exit_on_all_closed” system in the CoreStage::PostUpdate stage. Then I had to add the exit system to the CoreStage::Last
stage.
So, while this might not be the highest priority since this functionality isn’t /necessary/ for anything, it would be nice if there was a simpler way to do it where I wasn’t worried that I was going to miss the AppExit event being emitted.
Additional context
I had first asked about this on reddit when trying to figure out a solution to a specific problem of mine: https://www.reddit.com/r/bevy/comments/zzjex9
I’ve discovered the solution provided, but it has been pointed out this might be a good thing to create an issue for on Github, either to implement exit systems for the App or at the very least provide a little more information on the AppExit page for how to create these exit systems within the current framework.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 8
- Comments: 24 (24 by maintainers)
Commits related to this issue
- Docs: App::run() might never return; effect of WinitSettings::return_from_run. (#7228) # Objective See: - https://github.com/bevyengine/bevy/issues/7067#issuecomment-1381982285 - (This does ... — committed to bevyengine/bevy by stephenmartindale a year ago
- Docs: App::run() might never return; effect of WinitSettings::return_from_run. (#7228) # Objective See: - https://github.com/bevyengine/bevy/issues/7067#issuecomment-1381982285 - (This does ... — committed to alradish/bevy by stephenmartindale a year ago
- # Objective Help users understand how to write code that runs when the app is exiting. See: - #7067 ## Solution Added documentation to `AppExit` class that mentions using the `Drop` trait for cod... — committed to Testare/bevy by Testare a year ago
- AppExit documentation updates #7067 Help users understand how to write code that runs when the app is exiting. See: - #7067 Added documentation to `AppExit` class that mentions using the `Drop` tr... — committed to Testare/bevy by Testare a year ago
- AppExit documentation updates (#7067) Help users understand how to write code that runs when the app is exiting. See: - #7067 Added documentation to `AppExit` class that mentions using the `Drop` ... — committed to Testare/bevy by Testare a year ago
- AppExit documentation updates (#7067) Help users understand how to write code that runs when the app is exiting. See: - #7067 (Partial resolution) Added documentation to `AppExit` class that menti... — committed to Testare/bevy by Testare a year ago
- AppExit documentation updates (#7067) # Objective Help users understand how to write code that runs when the app is exiting. See: - #7067 (Partial resolution) ## Solution Added documentation to ... — committed to Testare/bevy by Testare a year ago
- AppExit documentation updates (#7067) (#7347) # Objective Help users understand how to write code that runs when the app is exiting. See: - #7067 (Partial resolution) ## Solution Added... — committed to bevyengine/bevy by Testare a year ago
- Docs: App::run() might never return; effect of WinitSettings::return_from_run. (#7228) # Objective See: - https://github.com/bevyengine/bevy/issues/7067#issuecomment-1381982285 - (This does ... — committed to ItsDoot/bevy by stephenmartindale a year ago
- AppExit documentation updates (#7067) (#7347) # Objective Help users understand how to write code that runs when the app is exiting. See: - #7067 (Partial resolution) ## Solution Added... — committed to ItsDoot/bevy by Testare a year ago
- Squashed commit of the following: commit a85b740f242cb0a239082fcfb8c1eceb23a266df Author: James Liu <contact@jamessliu.com> Date: Sun Jan 22 00:21:55 2023 +0000 Support recording multiple Comm... — committed to HackerFoo/bevy by HackerFoo a year ago
- Docs: App::run() might never return; effect of WinitSettings::return_from_run. (#7228) # Objective See: - https://github.com/bevyengine/bevy/issues/7067#issuecomment-1381982285 - (This does ... — committed to Subserial/bevy_winit_hook by stephenmartindale a year ago
Awesome summary. I would love to see a PR or 4 tackling those docs issues, although there’s clearly fundamental issues with how this is designed.
Here is some further points of input from a discussion on Discord, this afternoon.
Nothing about
App::run()
suggests it may or may not return:winit
)winit
case!
(likestd::process::exit()
, which does, and is thus self-documenting) or even something likeResult<!, AnError>
in thewinit
-powered case.The user is expected to have “arcane knowledge” – In Safe Rust! – and the compiler cannot help them.
There is
winit::WinitSettings::return_from_run
BUT…app/return_after_run.rs
which I will discuss, further, later.winit
’s documentation tells you not to use it, too.SPASTA apartment-model processes and message-loop limitations…)That’s a whole lot of nuance for a Bevy user to need to know in order to get expected behaviour from
App::run()
!The
WindowClosed
event is very easy to discover in the documentation BUT…WindowClosed
The aforementioned
app/return_after_run.rs
example is probably the next most discoverable resource BUT…winit::WinitSettings::return_from_run
.return_from_run
as the solution, which it isn’t if the documentation (andwinit
’s) are accurate.WindowClosed
because, by that time, the entities are gone from the ECSBoth is my preference, in separate PRs.
Those are some valid points! I had not considered implementing
Drop
, nor had I considered thatDrop
would still execute if the app was exited through a window closing, and I have verified that it does indeed work in the code. Perhaps if exit systems are not implemented, a note about that would be a good addition to the documentation forAppExit
?That said, I still have a couple points to defend this Idea: The biggest one is that I find that this might be a contention point between Rust idioms and ECS idioms: To my understanding, the principle idea of ECS is that behavior is not contained in the components, but in the systems. But perhaps
Drop
might be a special case.And I’ll admit that saving on exit could be a bad idea, the examples of it that I was thinking of actually probably do it through clever use of other systems. Perhaps a better example would be a multiplayer client sending a “LOGOUT” message to the server? While the server would probably eventually get the idea when the client stopped sending keep-alive messages or other input, it’s possible they would delay, which could even lead to bad things happening to the player while they weren’t connected. It could also prevent the player from logging in on another device until the timeout occurred.
This can still be solved with a
Drop
trait implementation though, so really the thing that needs to be determined is if “Drop” implementations or exit systems would be more idiomatic for the Bevy engine in particular.This is a good issue. My opinion is that this should just be another schedule (akin to the new startup schedule), which the default runner evaluates once before exiting.
Oooooh, well that’s good to know!
Well, it’s good to know we have more ways of configuring this for the app developer side, but if you’re trying to vend a plugin for additional game functionality, it’s less than ideal to say " be sure to configure your other plugins this way and add this code after the
App.run()
." We have options that can do this by having a system listen toAppExit
events that runs after exits (Though figuring out scheduling is less than ideal), and we can do it by implementingDrop
for our components (Which is idiomatic Rust, and might have issues if you have multiples of those components).I personally still think the major question is still “Should we add explicit exit systems, or should we update AppExit documentation to point out that implementing
Drop
might be more rust-idiomatic, or both?” Once we solve that question, it looks like we already have ideas on how to implement it?