Bevy Simple Subsecond System
Hotpatch your Bevy systems, allowing you to change their code while the app is running and directly see the results! This is an intermediate solution you can use until Bevy implements this feature upstream.
Powered by Dioxus' subsecond
Please report all hotpatch-related problems to them :)
https://github.com/user-attachments/assets/a44e446b-b2bb-4e10-81c3-3f20cccadea0
First Time Installation
First, we need to install a specific version of the Dioxus CLI.
Depending on your OS, you'll have to set up your environment a bit more:
If you're lucky, you don't need to change anything.
However, some users may experience issues with their path length.
If that happens, move your crate closer to your drive, e.g. C:\my_crate.
If that is not enough, set the following in your ~\.cargo\config.toml:
[]
= 1
Note that this may increase compile times significantly if your crate is very large.
When changing this number, always run cargo clean before rebuilding.
If you can verify that this solved your issue,
try increasing this number until you find a happy middle ground. For reference, the default number
for incremental builds is 256, and for non-incremental builds 16.
You also cannot set linker = "rust-lld.exe", as subsecond currently crashes when linker is set.
You're in luck! Everything should work out of the box if you use the default system linker.
Execute the following:
If this points to clang, you're good. Otherwise, we'll need to symlink it.
Read the path returned by the following command:
and cd into it. For example,
Assuming you have clang installed, run the following commands:
Note that the above commands may require sudo.
Now everything should work. If not, install lld on your system and add the following to your ~/.cargo/config.toml:
[]
= [
"-Clink-arg=-fuse-ld=lld",
]
If you prefer to use mold, you can set it up like this:
[]
#linker = clang
= [
"-Clink-arg=-fuse-ld=mold",
]
Note that the linker key needs to be commented out.
You will also need to replace your system ld with mold.
On NixOS you can do this in a shell by replacing:
pkgs.mkShell {
# ..
}
with:
pkgs.mkShell.override {
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
} {
# ..
}
Usage
Add the crate to your dependencies:
Then add the plugin to your app and annotate any system you want with #[hot]:
use *;
use *;
Now run your app with
Now try changing that string at runtime and then check your logs!
Note that changing the greet function's signature at runtime by e.g. adding a new parameter will still require a restart.
In general, you can only change the code inside the function at runtime. See the Advanced Usage section for more.
Examples
Run the examples with
e.g.
Language Servers
In general, rust analyzer for VS Code will play nice with the #[hot] attribute.
If you're running into issues, you can add the following to your VS Code settings:
"rust-analyzer.procMacro.ignored": ,
"rust-analyzer.diagnostics.disabled":
lspconfig..
Advanced Usage
There are some more things you can hot-patch, but they come with extra caveats right now
- Annotating a function relying on local state will clear it every frame. Notably, this means you should not use
#[hot(rerun_on_hot_patch)]or#[hot(hot_patch_signature)]on a system that uses any of the following:EventReaderLocal- Queries filtering with
Added,Changed, orSpawned
- Some signatures are not supported, see the tests. Some have
#[hot(rerun_on_hot_patch)]or#[hot(hot_patch_signature)]commented out to indicate this - All hotpatched systems run as exclusive systems, meaning they won't run in parallel
- For component migration:
- While top level component definitions can be changed and renamed (and will be migrated if using
HotPatchMigrate), changing definitions of the types used as fields of the components isn't supported. It might work in some cases but most probably will be an undefined behaviour
- While top level component definitions can be changed and renamed (and will be migrated if using
UI is often spawned in Startup or OnEnter schedules. Hot-patching such setup systems would be fairly useless, as they wouldn't run again.
For this reason, the plugin supports automatically rerunning systems that have been hot-patched. To opt-in, replace #[hot] with #[hot(rerun_on_hot_patch = true)].
See the rerun_setup example for detailed instructions.
Replace #[hot] with #[hot(hot_patch_signature = true)] to allow changing a system's signature at runtime.
This allows you to e.g. add additional Query or Res parameters or modify existing ones.
Features
- Change systems' code and see the effect live at runtime
- If your system calls other functions, you can also change those functions' code at runtime
- Extremely small API: You only need the plugin struct and the
#[hot]attribute - Automatically compiles itself out on release builds and when targetting Wasm. The
#[hot]attribute does simply nothing on such builds.
Known Limitations
- A change in the definition of structs that appear in hot-patched systems at runtime will result in your query failing to match, as that new type does not exist in
Worldyet.- Practically speaking, this means you should not change the definition of
Resources andComponents of your system at runtime
- Practically speaking, this means you should not change the definition of
- Only the topmost binary is hotpatched, meaning your app is not allowed to have a
lib.rsor a workspace setup. - Attaching a debugger is problaby not going to work. Let me know if you try!
- I did not test all possible ways in which systems can be used. Does piping work? Does
bevy_mod_debugdumpstill work? Maybe. Let me know! - Only functions that exist when the app is launched are considered while hotpatching. This means that if you have a system
Athat calls a functionB, changingBwill only work at runtime if that function existed already when the app was launched. - Does nothing on Wasm. This is not a technical limitation, just something we didn't implement yet..
Compatibility
| bevy | bevy_simple_subsecond_system |
|---|---|
| 0.16 | 0.2 |