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>()
.