Adding Reloadables to your Plugins

You can add reloadable portions to your application within any plugin, but they have to be explicitly segmented and defined. Within a plugin, you would add a reloadable section like so:

#![allow(unused)]

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

#[dexterous_developer_setup]
fn reloadable(app: &mut ReloadableAppContents) {
    app
        .add_systems(Update, this_system_will_reload);
}

}

Here, we are defining a setup function reloadable, and adding it to our app in the plugin builder. This function will be called any time the library is reloaded.

In the background, the plugin runs cargo watch on the source directory of the project, and whenever it re-builds we will load a new version of the library.

Types of Reloadables

Systems

The simplest, and most versatile, type of reloadable is a simple system. This can be any system - we just register it in a special way allowing us to remove them when thge library is replaced.

These are added using .add_systems, exactly like you would add a system to a normal bevy app!

Setup Systems

These are special systems, that are supposed to set things up - which might need to be re-done upon a reload. The classic example is a game UI, which you might want to re-build after a reload. There are a few helpers for these types of systems

First, we can clear all entities that have a marker component and then run our setup function using .reset_setup::<Component>(systems_go_here). And if we want it to only happen on entering a specific state (or re-loading while within that state), we can use .reset_setup_in_state::<Component>(state, systems). Alternatively, if we just want to clear stuff out on a reload, we can use a marker component and call .clear_marked_on_reload::<Component>().

Resources

Reloading resources is a little more complex - so we have a few variations

Reset on Reload

If you want to reset a resource when the library reloads, you can use either .reset_resource::<Resource>() which uses its default value, or .reset_resource_to_value(value) which uses a value you provide.

Replaceable Resources

If you want to be able to iterate on the structure of a resource, but maintain it's existing data via serialization, you can use a ReplacableResource. To do so you need to implement the ReplacableResource trait on your type:

#![allow(unused)]
fn main() {
#[derive(Resource, Serialize, Deserialize, Default)]
struct MyResource;

impl ReplacableResource {
    fn get_type_name() -> &'static str {
        "my_resource"
    }
}
}

Then, you can register it using .init_replacable_resource::<ReplacableResource>(). This will cause the resource to be serialized before the library is reloaded, and replaced with a new version after reload. Since serialization is done using msgpack, it should be able to cope with adding new fields or removing old ones - but keep in mind the way serde handles that kind of stuff.

Replacable Components

You can also set up replacable components. These function like replacable resources, but involve replacing components on various entities. Here you implement the ReplacableComponent trait:

#![allow(unused)]
fn main() {
#[derive(Component, Serialize, Deserialize, Default)]
struct MyComponent;

impl ReplacableComponent {
    fn get_type_name() -> &'static str {
        "my_component"
    }
}

}

And then register it using .register_replacable_component::<ReplacableComponent>().

Replacable State

States are also possible to set up as replacable. Here you implement the ReplacableState trait:

#![allow(unused)]
fn main() {
#[derive(States, PartialEq, Eq, Clone, Copy, Debug, Hash, Default, Serialize, Deserialize)]
pub enum AppState {
    #[default]
    A,
    B,
}


impl ReplacableState for AppState {
    fn get_type_name() -> &'static str {
        "app-state"
    }

    fn get_next_type_name() -> &'static str {
        "next-app-state"
    }
}

}

Note that unlike ReplacableResource or ReplacableComponent, with ReplacableState you need to give it a name as well as giving a name for the NextState<S> resource it'll create.

You can then add the state using .init_state::<ReplacableComponent>().

Replacable Event

You can also create replacable Events. Here you implement the ReplacableEvent trait:

#![allow(unused)]
fn main() {
#[derive(Event, Clone, Debug, Serialize, Deserialize)]
pub enum AppEvent {
    Text(String),
    Shout(String)
}


impl ReplacableEvent for AppEvent {
    fn get_type_name() -> &'static str {
        "app-event"
    }
}


}

Note that when events get replaced it resets the event queue - so all existing events will be cleared! Since as a rule events only persist to the next frame generally, this shouldn't be too much of an issue - depending on when you trigger the reload.

You can then add the state using .add_event::<ReplacableEvent>().