A plugin to enable random number generation for the Bevy game engine, built upon turborand
. Implements ideas from Bevy's Deterministic RNG RFC.
turborand
's internal implementation uses Wyrand, a simple and fast generator but not cryptographically secure, as well as ChaCha8, a cryptographically secure generator tuned to 8 rounds of the ChaCha algorithm for increased throughput without sacrificing too much security, as per the recommendations in the Too Much Crypto paper.
use bevy::prelude::*;
use bevy_turborand::prelude::*;
#[derive(Debug, Component)]
struct Player;
fn setup_player(mut commands: Commands, mut global_rng: ResMut<GlobalRng>) {
commands.spawn((
Player,
RngComponent::from(&mut global_rng)
));
}
fn do_damage(mut q_player: Query<&mut RngComponent, With<Player>>) {
let mut rng = q_player.single_mut();
println!("Player attacked for {} damage!", rng.u32(10..=20));
}
fn main() {
App::new()
.add_plugins(RngPlugin::default())
.add_systems(Startup, setup_player)
.add_systems(Update, do_damage)
.run();
}
In order to obtain determinism for your game/app, the Rng
's must be seeded. GlobalRng
and RngPlugin
can be given a seed which then sets the internal PRNG to behave deterministically. Instead of having to seed every RngComponent
manually, as long as the GlobalRng
is seeded, then RngComponent
can be created directly from the global instance, cloning the internal Rng to itself, which gives it a random but deterministic seed. This allows for better randomised states among RngComponent
s while still having a deterministic app.
Systems also must be ordered correctly for determinism to occur. Systems however do not need to be strictly ordered against every one as if some linear path. Only related systems that access a given set of RngComponent
s need to be ordered. Ones that are unrelated can run in parallel and still yield a deterministic result. So systems selecting a Player
entity with a RngComponent
should all be ordered against each other, but systems selecting an Item
entity with an RngComponent
that never interacts with Player
don't need to be ordered with Player
systems, only between themselves.
To see an example of this, view the project's tests to see how to make use of determinism for testing random systems.
bevy_turborand |
bevy |
---|---|
v0.9.0-rc.1 | v0.14.0-rc.4 |
v0.8 | v0.13 |
v0.7 | v0.12 |
v0.6 | v0.11 |
v0.5 | v0.10 |
v0.4 | v0.9 |
v0.2, v0.3 | v0.8 |
v0.1 | v0.7 |
MSRV for bevy_turborand
is the same as in bevy
, so always the latest Rust compiler version.
With turborand
0.6, there are a lot of breaking changes due to a rework of the API. For the most part, this is mostly internal to turborand
and bevy_turborand
exposes the new traits by default, so any existing code should more or less work fine, except for the following:
from_global
on RngComponent
no longer exists. Instead, there are From
implementations on RngComponent
and ChaChaRngComponent
that cover more use-cases where a reference or resource or even another component could be used for initialising a new RngComponent
and so forth. This makes it more flexible with regards to what the source is. Just go to town with RngComponent::from
.new
functions no longer accept an Option parameter for the seed. The methods are split between new
for initialising without a seed (so obtaining a random seed), and with_seed
for initialising with a seed value (which applies to components and resources).RngPlugin
now uses a builder pattern to initialise. new
creates a default state with no seeds applied, and then with_rng_seed
and with_chacha_seed
applies seed values to the plugin to then initialise the global RNG resources with. See the docs for an example of how that might look now.bevy_turborand
now has a number of feature flags, and apart from the new traits (which are always provided when no flags are enabled), everything else is behind a feature flag. For example, wyrand
based structs (RngComponent
et al) are behind the wyrand
flag, which is enabled by default. For a higher quality entropy source (though it will be slower), chacha
flag provides RNG provided by the ChaCha8 algorithm, such as ChaChaRngComponent
. RngPlugin
is available when either wyrand
or chacha
is enabled. Otherwise, existing flags like rand
enable the rand crate compatibility layer, and serialize
for serde derives.bevy_turborand
moves to turborand
0.8, which rolls with a couple of major API breaking changes. Certain traits are no longer exposed as they are internal implementation details. The main changes are that ChaChaRng
is no longer backed by a Vec
for buffering entropy, switching to an aligned array for improving generation throughput at the slight cost of initialisation performance and struct size. It does mean no need for the single heap allocation however when the RNG generates a number for the first time. This refactor also changes how ChaChaRng
is serialised, so bevy_turborand
0.4 is not compatible with previously serialised data.
Also, the old Clone
behaviour for TurboCore
RNGs has been changed, so .clone()
now maintains the state between original and cloned instances. The old behaviour now exists as the ForkableCore
trait with the .fork()
method, which has the original instance's state be mutated in order to derive a new random state for the forked instance. As such, RngComponent
and ChaChaRngComponent
can now implement Clone
.
bevy_turborand
moves to turborand
0.10, which introduces a few more sample/sample_multiple methods for supporting iterators, partial_shuffle
and the major change regarding stable indexing, with sample/shuffle methods using the new index
method. index
allows for sampling to be stable across platforms, though it is currently optimised for 64-bit systems. All sampling/shuffling methods now make use of index
, so this means bevy_turborand
will be deterministic across different platforms, such as having the same output for wasm32
and x86-64
. The only method that doesn't benefit from this is usize
.
ChaChaRngComponent
also now has a different output due to a change of the internal ChaChaRng
source, which also changes how it gets serialised.
Licensed under either of
at your option.