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

Explore how to make APIs play nicer with Plugins #5

Open
inodentry opened this issue Mar 20, 2022 · 5 comments
Open

Explore how to make APIs play nicer with Plugins #5

inodentry opened this issue Mar 20, 2022 · 5 comments
Labels
enhancement New feature or request

Comments

@inodentry
Copy link
Contributor

Because of how our fixed timestep and states implementations rely on the user adding custom stages, that nest other stages, it is difficult to make this play nicely with Bevy's Plugin API.

If a plugin needs to add stuff to happen on state transitions, or on fixed timestep, it has no way of accessing the actual SystemStages to add systems to. Labels don't help, because Bevy's SystemLabels are a top-level Schedule thing, and cannot refer to nested stages.

The only way right now seems to be to sidestep the Plugin API altogether, and just use functions that take &mut SystemStage (for whatever they need access to) instead.

This might not be solvable while our stuff is in an external crate, and I cannot think of a better design. I'm open to ideas.

@inodentry inodentry added the enhancement New feature or request label Mar 20, 2022
@inodentry
Copy link
Contributor Author

The new App helper methods in 0.4 allow states to be used from plugins, so the most important use case is fixed.

Leaving this issue open for future improvements.

@pinkponk
Copy link

pinkponk commented Jun 1, 2022

Hi, I'm interested in this!

I make heavy use of plugins but cannot get my head around how to make use of the SystemStage for downstream plugins.

What do you mean with "and just use functions that take &mut SystemStage (for whatever they need access to) instead."?

@inodentry
Copy link
Contributor Author

@pinkponk I mean that, for the purposes of internal organization inside your project (this is very suboptimal if you are publishing a plugin crate), you could just do this:

// instead of `impl Plugin` ...

pub fn add_enter_systems(stage: &mut SystemStage) {
    stage.add_system(thing);
    stage.add_system(thing2);
}
pub fn add_exit_systems(stage: &mut SystemStage) {
    stage.add_system(cleanup);
}
pub fn add_fixed_timestep_systems(stage: &mut SystemStage) {
    stage.add_system(fixed_thing);
}
pub fn add_general_systems(app: &mut App) {
    // add other things
}

and then:

fn main() {
    // ... set up other things ...
    // add our things, using our helper functions
    // (instead of `.add_plugin`)
    module::add_general_systems(&mut app);
    module::add_fixed_timestep_systems(&mut fixed_stage);
    module::add_enter_systems(&mut state_enter_stage);
    module::add_exit_systems(&mut state_exit_stage);
}

Yes, it is super ugly. You are fragmenting all your setup, instead of having it all in one place, like with impl Plugin. But it works.

@inodentry
Copy link
Contributor Author

There is now also an extension trait for working with fixed timestep.

@pinkponk
Copy link

I ended up doing having all the plugins only add certain systems and resources and then having a method for getting the stages I needed. I make heavy use of your fixedtimestep and add a bunch of stages under it so that my game can make use of fast forwarding.

Had a problem with rapier when I did fast forwarding as rapier has its own fixedtimestep system so when I ran 10x speed what happend was that the rapier ran 5x then my systems 5x but now I only have a single fixedtimestep stage which gets its child stages from other plugins.

pub struct WorldPlugin {
    pub with_map: bool,
    pub with_creatures: bool,
    pub with_physics: bool,
}
            //Main time stage
        let mut main_stage = FixedTimestepStage::from_stage(
            Duration::from_millis((1000.0 / BASE_FPS) as u64),
            SystemStage::parallel().with_system_set(
                ConditionSet::new()
                    .run_in_state(GameState::Gaming)
                    .with_system(sync_game_speed)
                    .into(),
            ),
        );
        //Add map stage
        if self.with_map {
            app.add_plugin(MapPlugin);
            main_stage.add_stage(MapPlugin::get_time_stage());
        }

        //Add creature stages
        if self.with_creatures {
            app.add_plugin(CreaturePlugin);
            for stage in CreaturePlugin::get_stages() {
                main_stage.add_stage(stage);
            }
        }

        //Add physics stages
        if self.with_physics {
            app.add_plugin(
                RapierPhysicsPlugin::<NoUserData>::default().with_default_system_setup(false),
            );
            main_stage.add_stage(
                SystemStage::parallel().with_system_set(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsStages::SyncBackend),
                ),
            );
            main_stage.add_stage(
                SystemStage::parallel().with_system_set(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsStages::StepSimulation),
                ),
            );
            main_stage.add_stage(
                SystemStage::parallel().with_system_set(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsStages::Writeback),
                ),
            );

            // NOTE: we run sync_removals at the end of the frame, too, in order to make sure we don’t miss any `RemovedComponents`.
            main_stage.add_stage(
                //TODO run this outside in the correct stage?
                SystemStage::parallel().with_system_set(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsStages::DetectDespawn),
                ),
            );
        }

        app.add_stage_after(CoreStage::Update, GameStages::WorldTopStage, main_stage);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants