bevy_auto_plugin

Crates.iobevy_auto_plugin
lib.rsbevy_auto_plugin
version0.5.0
created_at2025-01-28 18:35:27.205201+00
updated_at2025-08-25 19:09:32.021105+00
descriptionProcedural attribute macros for Bevy apps that reduce boilerplate by automatically registering components, resources, events, states, and systems in your plugin's build function.
homepage
repositoryhttps://github.com/StrikeForceZero/bevy_auto_plugin
max_upload_size
id1533864
size163,755
Brett Striker (StrikeForceZero)

documentation

README

Bevy Auto Plugin

License Crates.io Downloads Docs CI

Bevy Auto Plugin provides attribute macros that automatically handle the repetitive setup usually required in Bevy plugins. Instead of manually wiring up components, resources, events, states, and systems - and remembering all their respective derives - you can declare them with concise annotations tied to a plugin.

If you’ve ever added several components only to hit runtime errors or discover a missing TypeRegistry entry when using tools like bevy-inspector-egui, this plugin is for you. It helps keep your code focused on game logic rather than framework plumbing.

The following examples demonstrate how common Bevy patterns can be expressed more ergonomically with #[auto_*] macros, while still generating the underlying bevy-specific code you would normally write by hand.

Examples

Basic

Component

instead of having to specify all these derives and remember to reflect:

#[derive(Component, Debug, Default, Reflect)]
#[reflect(Component, Debug, Default)]
#[require(Name::new("FooComponent"))]
struct FooComponent;

and then later having to remember to register your component in the type registry:

struct MyPlugin;

impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.register_type::<FooComponent>();
    }
}

you can do:

#[derive(AutoPlugin)]
#[auto_plugin(impl_plugin_trait)]
struct MyPlugin;

#[auto_component(
    plugin = MyPlugin,
    derive(Debug, Default),
    reflect(Debug, Default),
    register,
    auto_name,
)]
struct FooComponent;

System

instead of writing a function then scheduling in your plugin's build function:

fn my_system() {}

fn plugin(app: &mut App) {
    app.add_systems(Update, my_system.run_if(some_condition).after(some_other_system));
}

you can do it via the auto_system(..) attribute macro:

#[auto_system(
    plugin = MyPlugin,
    schedule = Update,
    config( 
        run_if = some_condition,
        after = some_other_system,
    ),
)]
fn my_system() {}

Generics

Component

if your items have generics, you can specify the types using the generics(...) meta argument for each concrete type.

#[derive(AutoPlugin)]
#[auto_plugin(impl_plugin_trait)]
struct MyPlugin;

#[auto_component(
    plugin = MyPlugin,
    generics(usize, bool),
    generics(bool, bool),
    derive(Debug, Default),
    reflect(Debug, Default),
    register,
    auto_name,
)]
struct FooComponent<A, B>(A, B);

this will generate something equivalent to:

impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.register_type::<FooComponent<usize, bool>>();
        app.register_type::<FooComponent<bool, bool>>();
    }
}

System

if your systems have generics, you can specify the types using the generics(...) meta argument for each concrete type.

#[auto_system(
    plugin = MyPlugin,
    schedule = Update, 
    generics(Name), 
    generics(Transform),
    config( 
        run_if = some_condition,
        after = some_other_system,
    ),
)]
fn my_system<A: Component>(q: Query<&A>) {
    for item in q.iter() {
        //
    }
}

this will generate something equivalent to:

impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, my_system::<Name>.run_if(some_condition).after(some_other_system));
        app.add_systems(Update, my_system::<Transform>.run_if(some_condition).after(some_other_system));
    }
}

Plugin

There are three distinct ways to make a bindable plugin:

#[derive(AutoPlugin)]
#[auto_plugin(impl_plugin_trait)]
struct MyPlugin;
#[derive(AutoPlugin)]
struct MyPlugin;

impl Plugin for MyPlugin {
    #[auto_plugin]
    fn build(&self, app: &mut App) {
        //
    }
}
#[derive(AutoPlugin)]
struct MyPlugin;

#[auto_plugin(plugin = MyPlugin)]
fn plugin(app: &mut App) {
    //
}

There is auto_plugin arguments if your plugin has generics.

See tests for other examples

Expanded

If you were looking to cherry-pick certain functionality like auto_name or auto_register_type for example you could use them individually: Only requirement when using global mode is you need tp make sure you are binding to a plugin that derives AutoPlugin

Global Mode

Features required:

  • default or mode_global or all_modes
use bevy::prelude::*;
use bevy_auto_plugin::modes::global::prelude::*;

#[derive(AutoPlugin)]
#[auto_plugin(impl_plugin_trait)]
struct MyPlugin;

#[derive(Component, Reflect)]
#[reflect(Component)]
#[auto_register_type(plugin = MyPlugin)]
#[auto_name(plugin = MyPlugin)]
struct FooComponent;

// or if you want to omit plugin for each auto_* item:
#[auto_bind_plugin(plugin = MyPlugin)]
#[derive(Component, Reflect)]
#[reflect(Component)]
#[auto_register_type]
#[auto_name]
struct FooComponent2;

#[derive(Resource, Debug, Default, Reflect)]
#[reflect(Resource)]
#[auto_register_type(plugin = MyPlugin)]
#[auto_init_resource(plugin = MyPlugin)]
struct FooDefaultResource(usize);

#[derive(Resource, Debug, Default, Reflect)]
#[reflect(Resource)]
#[auto_register_type(plugin = MyPlugin)]
#[auto_init_resource(plugin = MyPlugin)]
#[auto_insert_resource(plugin = MyPlugin, resource(FooResource(1)))]
struct FooResource(usize);

#[derive(Event, Debug, Default, Reflect)]
#[auto_register_type(plugin = MyPlugin)]
#[auto_add_event(plugin = MyPlugin)]
struct FooEvent(usize);

#[derive(States, Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Reflect)]
#[auto_init_state(plugin = MyPlugin)]
#[auto_register_state_type(plugin = MyPlugin)]
enum FooState {
    #[default]
    Start,
    End,
}

#[auto_add_system(plugin = MyPlugin, schedule = Update)]
fn foo_system(mut foo_resource: ResMut<FooResource>) {
    foo_resource.0 += 1;
}

fn main() {
    App::new()
        .add_plugins(MyPlugin)
        // ... other plugins and setup
        .run();
}

Which automatically implements the Plugin trait for MyPlugin and registers all the types, resources, events, and systems when the plugin is added to the app.

Known Limitations

  • WASM should work, CI uses the wasm-bindgen-test-runner but maybe there's a specific wasm target/environment where it fails?

below are some other modes that are deprecated awaiting feedback from users:

Module Mode (Deprecated)

Module Mode

Features required:

  • mode_module or all_modes
use bevy::prelude::*;
use bevy_auto_plugin::modes::module::prelude::*;

#[auto_plugin(init_name=init)]
mod plugin_module {
    use super::*;
    
    #[auto_register_type]
    #[derive(Component, Reflect)]
    #[reflect(Component)]
    #[auto_name]
    pub struct FooComponent;

    #[auto_register_type(generics(bool))]
    #[auto_register_type(generics(u32))]
    #[derive(Component, Reflect)]
    #[reflect(Component)]
    pub struct FooComponentWithGeneric<T>(T);

    #[auto_register_type]
    #[auto_add_event]
    #[derive(Event, Reflect)]
    pub struct FooEvent;

    #[auto_register_type(generics(bool))]
    #[auto_add_event]
    #[derive(Event, Reflect)]
    pub struct FooEventWithGeneric<T>(T);

    #[auto_register_type]
    #[auto_init_resource]
    #[derive(Resource, Default, Reflect)]
    #[reflect(Resource)]
    pub struct FooResource;

    #[auto_register_type(generics(bool))]
    #[auto_init_resource]
    #[derive(Resource, Default, Reflect)]
    #[reflect(Resource)]
    pub struct FooResourceWithGeneric<T>(T);
}

fn plugin(app: &mut App) {
    plugin_module::init(app);
}

Which generates this code

mod plugin_module {
    // ...
    fn init(app: &mut App) {
        app.register_type::<FooComponent>();
        app.register_type::<FooComponentWithGeneric<bool>>();
        app.register_type::<FooComponentWithGeneric<u32>>();
        app.register_type::<FooEvent>();
        app.register_type::<FooEventWithGeneric<bool>>();
        app.register_type::<FooResource>();
        app.register_type::<FooResourceWithGeneric<bool>>();

        app.add_event::<FooEvent>();
        app.add_event::<FooEventWithGeneric<bool>>();

        app.init_resource::<FooResource>();
        app.init_resource::<FooResourceWithGeneric<bool>>();

        app.register_required_components_with::<FooComponent, Name>(|| Name::new("FooComponent"));
    }
}

Known Limitations

  • Causes issues for ide's like RustRover
Flat File Mode (Deprecated)

Flat File Mode

Features required:

  • mode_flat_file or all_modes,
  • Optional but recommendedflat_file_lang_server_noop
use bevy::prelude::*;
use bevy_auto_plugin::modes::flat_file::prelude::*;

#[auto_register_type]
#[derive(Component, Reflect)]
#[reflect(Component)]
#[auto_name]
struct FooComponent;

#[auto_register_type(generics(bool))]
#[auto_register_type(generics(u32))]
#[derive(Component, Reflect)]
#[reflect(Component)]
struct FooComponentWithGeneric<T>(T);

#[auto_register_type]
#[auto_add_event]
#[derive(Event, Reflect)]
struct FooEvent;

#[auto_register_type(generics(bool))]
#[auto_add_event]
#[derive(Event, Reflect)]
struct FooEventWithGeneric<T>(T);

#[auto_register_type]
#[auto_init_resource]
#[derive(Resource, Default, Reflect)]
#[reflect(Resource)]
struct FooResource;

#[auto_register_type(generics(bool))]
#[auto_init_resource]
#[derive(Resource, Default, Reflect)]
#[reflect(Resource)]
struct FooResourceWithGeneric<T>(T);

#[auto_plugin(app_param=app)]
fn plugin(app: &mut App) {}

Which generates this code in your fn accepting &mut App

#[auto_plugin(app_param=app)]
fn plugin(app: &mut App) {
    app.register_type::<FooComponent>();
    app.register_type::<FooComponentWithGeneric<bool>>();
    app.register_type::<FooComponentWithGeneric<u32>>();
    app.register_type::<FooEvent>();
    app.register_type::<FooEventWithGeneric<bool>>();
    app.register_type::<FooResource>();
    app.register_type::<FooResourceWithGeneric<bool>>();
    
    app.add_event::<FooEvent>();
    app.add_event::<FooEventWithGeneric<bool>>();
    
    app.init_resource::<FooResource>();
    app.init_resource::<FooResourceWithGeneric<bool>>();

    app.register_required_components_with::<FooComponent, Name>(|| Name::new("FooComponent"));
    // ...
}

Known Limitations

  • Won't provide outputs in IDE's due to Language Server Stubbed
    • use lang_server_noop feature (enabled by default) to allow flat_file macros to no-ops when they fail to resolve Span::local_file
    • attempts to naively detect when running under rustc context to otherwise bubble up the errors to the compiler
  • All items need to be in the same module. This won't work:
use bevy::prelude::*;
use bevy_auto_plugin::modes::flat_file::prelude::*;

mod foo {
    use super::*;
    #[auto_register_type]
    #[derive(Component, Reflect)]
    #[reflect(Component)]
    struct FooComponent;
}

#[auto_plugin(app_param=app)]
fn plugin(app: &mut App) {
    // ...
}

Changelog

Migrations

License

All code in this repository is dual-licensed under either:

at your option. This means you can select the license you prefer.

Your Contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

Commit count: 325

cargo fmt