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

Application should be returned when Bevy runner terminate #2937

Open
arialpew opened this issue Oct 9, 2021 · 5 comments
Open

Application should be returned when Bevy runner terminate #2937

arialpew opened this issue Oct 9, 2021 · 5 comments
Labels
A-App Bevy apps and plugins C-Examples An addition or correction to our examples C-Usability A simple quality-of-life change that makes Bevy easier to use

Comments

@arialpew
Copy link

arialpew commented Oct 9, 2021

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

Bevy can be run in headless mode, where you often want to introspect the state of the world (ressource, query, ...) and assert something for test and debugging. Some usefull data are present in Bevy, like time since startup, the whole world state, ...

When an application is constructed and App::run() is called, the application never terminate unless :

  • The runner is set to "run once".
  • The application emit an event called AppExit.
  • The user have setup a custom runner with his own rule.

When the application terminate, App::run() return nothing and you have no way to introspect the final state because the application is now empty due to std::mem::replace.

// impl App
pub fn run(&mut self) {
    #[cfg(feature = "trace")]
    let bevy_app_run_span = info_span!("bevy_app");
    #[cfg(feature = "trace")]
    let _bevy_app_run_guard = bevy_app_run_span.enter();

    let mut app = std::mem::replace(self, App::empty());
    let runner = std::mem::replace(&mut app.runner, Box::new(run_once));
    (runner)(app);
}
// App
pub struct App {
    pub world: World,
    pub runner: Box<dyn Fn(App)>, // Desugar into ---> Box<Dyn Fn(App) -> ()>
    pub schedule: Schedule,
}

What solution would you like?

Since there's a clear owner of the Application (the runner), the runner should be able to return the Application and borrow checker should not complain about that.

We can either :

  1. Replace self with the returned application (should be favored in my opinion, since we are mutating the world when we run the application).
  2. Return the application and let the empty application like that.

What alternative(s) have you considered?

Don't return the Application and let the user with an empty application. If user don't have any background with std::mem::replace, confusion can happen. The user expect the application to be in final state, they don't expect an empty application.

Additional context

/

@arialpew arialpew added C-Enhancement A new feature S-Needs-Triage This issue needs to be labelled labels Oct 9, 2021
@bjorn3
Copy link
Contributor

bjorn3 commented Oct 9, 2021

You can do this using app.add_resource(bevy::winit::WinitConfig { return_from_run: true }). This is not the default as some platforms don't allow it. Desktop OSes generally support it, but mobile OSes and wasm don'. The following OSes do:

#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]

@arialpew
Copy link
Author

arialpew commented Oct 9, 2021

If you don't run your loop with Winit, can you do that ? Especially when bevy_winit is an optionnal dependency (windowing is not needed for all case, headless you don't care about that and disable the dependency).

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 9, 2021

Sorry, got confused with what you wanted. The runner consumes the App, so it can't be returned from App::run. It isn't possible to return any value from the runner without making the entire App generic over the return type of the runner. This would be an ergonomics loss. You could do the assertions and printings inside the runner after you are done running all schedules. Or you could re-implement App::run yourself to not use the runner stored inside it and return the App afterwards.

@arialpew
Copy link
Author

arialpew commented Oct 9, 2021

I end up doing this :

  1. I wrap my Application with my own "Engine" struct with a "on_exit" fn pointer.
  2. Since I'm using a custom runner, when I handle AppExit and break the main loop, before I return I call my fn pointer with an owned access to the App.

No need to change signature, no need to add a new trait for App.

Work perfectly, but it's a bit weird to have to do all this dance. Anyway, thanks for the information.

@alice-i-cecile alice-i-cecile added A-App Bevy apps and plugins C-Usability A simple quality-of-life change that makes Bevy easier to use C-Examples An addition or correction to our examples and removed C-Enhancement A new feature S-Needs-Triage This issue needs to be labelled labels Oct 9, 2021
@alice-i-cecile
Copy link
Member

Related to #2896 and #1057.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-App Bevy apps and plugins C-Examples An addition or correction to our examples C-Usability A simple quality-of-life change that makes Bevy easier to use
Projects
None yet
Development

No branches or pull requests

3 participants