Skip to content

Add random number generator resource (optionally specified with a seed) #2355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from

Conversation

LegNeato
Copy link
Contributor

@LegNeato LegNeato commented Jun 18, 2021

Objective

  • Make bevy "random aware" and allow seeds to be controlled by bevy

Solution

  • Move random number generators into bevy as a resource.

I'm new to bevy and would love guidance if this even makes sense to do. If so, I'll clean it up and add tests.

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Jun 18, 2021
@james7132
Copy link
Member

james7132 commented Jun 18, 2021

IMO, unless these PRNGs are required for a mission-critical official crate, I think this should be done by the end-developer instead of having it globally configured provided by the engine itself. There is a sizable number of open questions regarding how the PRNG is used that is entirely dependent on the developer's requirements:

  • What performance characteristics is desired?
  • How random does the outputs need to be?
  • How is the PRNG seeded?
  • Does the output need to be secure?
  • Is random state shared between threads? Between isolated systems?
  • How does the PRNG's use impact determinism-sensitive systems?

These questions all have use case specific answers and there is no one-size fits all solution. Using it as a bevy_core provided resource makes the choice here for several of these questions and that might not be what's needed for every use case. The thread_rng cases in the examples seem to be sane enough for it's use and is a cost that only the developers who need it will pay.

@NathanSWard
Copy link
Contributor

I think this should be done by the end-developer instead of having it globally configured provided by the engine itself.

I agree.
There are to many variables when it comes to using some sort of RNG that providing it in the engine doesn't seem to be very beneficial imo.
Also since adding the rand crate to your own project and setting up an RNG is quite trivial, this is probably best left to the end-developer.

@mockersf
Copy link
Member

While easy to setup, it's still a nice feature to have a random number generator available by default (Godot offers one, Unity also).

Could you put it behind a feature flag so that it can be removed if needed?

@LegNeato
Copy link
Contributor Author

Yep, I'll clean this up and put behind a feature flag by the end of the weekend

@vgel
Copy link
Contributor

vgel commented Jul 6, 2021

Instead of being in core, it might make more sense for this to be a separate bevy-random crate / plugin that gets included as part of the DefaultPlugins group:

impl PluginGroup for DefaultPlugins {
fn build(&mut self, group: &mut PluginGroupBuilder) {
group.add(LogPlugin::default());
group.add(CorePlugin::default());
group.add(TransformPlugin::default());
group.add(DiagnosticsPlugin::default());
group.add(InputPlugin::default());
group.add(WindowPlugin::default());
group.add(AssetPlugin::default());
group.add(ScenePlugin::default());
#[cfg(feature = "bevy_render")]
group.add(RenderPlugin::default());
#[cfg(feature = "bevy_sprite")]
group.add(SpritePlugin::default());
#[cfg(feature = "bevy_pbr")]
group.add(PbrPlugin::default());
#[cfg(feature = "bevy_ui")]
group.add(UiPlugin::default());
#[cfg(feature = "bevy_text")]
group.add(TextPlugin::default());
#[cfg(feature = "bevy_audio")]
group.add(AudioPlugin::default());
#[cfg(feature = "bevy_gilrs")]
group.add(GilrsPlugin::default());
#[cfg(feature = "bevy_gltf")]
group.add(GltfPlugin::default());
#[cfg(feature = "bevy_winit")]
group.add(WinitPlugin::default());
#[cfg(feature = "bevy_wgpu")]
group.add(WgpuPlugin::default());
}
}

I also agree it would be nice to have this by default -- little things like this add up, and while it does add a little bit of overhead for people who don't use it, I think that overhead is fairly minimal and if it's behind a feature flag it's not too difficult to remove. Having it as a plugin will also make the feature flag a bit easier to implement.

@james7132
Copy link
Member

I also agree it would be nice to have this by default -- little things like this add up, and while it does add a little bit of overhead for people who don't use it, I think that overhead is fairly minimal and if it's behind a feature flag it's not too difficult to remove. Having it as a plugin will also make the feature flag a bit easier to implement.

My main concern is that this is a source of non-determinism that is fundamental and accessible enough that it may alter game state in difficult to detect ways. For games that require it, like deterministic networked games, this is can be a hard non-starter. Other game engines like Unity and Unreal that have it built in require explicit management of these global PRNGs to avoid the side effects of one system altering another.

Re: having it behind a feature flag: this is marginally better, though I'm concerned that other first-party crates will become dependent on it as a source of randomness, which will make it transitively required for certain core engine features.

IMO, any first party crate requiring a PRNG should use local PRNGs embedded in resources/components from the crate itself. This both keeps the dependency tree smaller and isolates the effects of one crate/system on another.

@LegNeato
Copy link
Contributor Author

LegNeato commented Jul 7, 2021

Apologies, I may not understand as I haven't worked in games professionally. The reason I put up this PR is precisely because I need bevy to be deterministic and I want anything that can change the world logic and state be driven via bevy.

IMHO a source of randomness should be a feature of the world...multiple examples have already naturally included it. Happy to make the change elsewhere / in a subcrate, but I couldn't see how to get at the very early bevy stages outside core...any pointers would be swell!

I can remove the default os randomness if that is desired. The idea was for people who care about determinism, they pass in their own seed (as in the example I added). For people who don't, they get an out of the box experience as with other engines 🤷‍♂️. For example, this is how Godot works AFAICT...default is osrandomness but you can specify a seed.

@james7132
Copy link
Member

Apologies, I may not understand as I haven't worked in games professionally. The reason I put up this PR is precisely because I need bevy to be deterministic and I want anything that can change the world logic and state be driven via bevy.

A global PRNG means the execution order and presence of systems that depend on it will affect the current state: i.e. the particle system emitter will affect the PRNG outputs will affect the power-ups generated. It's for this reason why I think it's better to have system local PRNGs instead. Those that require control over the PRNG of multiple systems need to put in more work, but there's no spooky action at a distance by default.

@LegNeato
Copy link
Contributor Author

LegNeato commented Jul 7, 2021

Ah, I now get what you are saying!

I assumed that people would indeed add their own additional rng resources when they needed
specifc guarantees or segmentation...but I think in the most general the distinction one would care about is insecure vs secure so those were baked in.

Isn't it also preferable to have a single source of randomness all others descend from? It helps determinism as there is one place to set the seed and all callsites respect it. As it is there is nothing preventing a system in a dependency using rand directly and causing non-determinism.

Perhaps instead of an rng implementation what we need as a resource is a "genesis" random seed / source of entropy that all rngs use to seed, something similar to https://docs.rs/rand/0.8.4/rand/trait.SeedableRng.html#method.from_rng.

@Nilirad
Copy link
Contributor

Nilirad commented Jul 7, 2021

I like the idea of a seed generator. However we need to make sure that the local PRNGs are seeded deterministically.

@alice-i-cecile alice-i-cecile added core C-Feature A new feature, making something new possible and removed S-Needs-Triage This issue needs to be labelled labels Jul 9, 2021
@LegNeato
Copy link
Contributor Author

Looking for a little more guidance on where to take this

@mockersf mockersf added the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Jul 16, 2021
@LegNeato
Copy link
Contributor Author

LegNeato commented Jul 20, 2021

I put up a PR with a new plugin called bevy_entropy in #2504. If that lands I'll rebase this on top of that and continue the discussion. I still think it is useful to have built-in rngs, as many of our examples use them...which likely means that many games would too. But we'll see what happens with the lower-level building block PR first.

@mockersf mockersf removed the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Jul 21, 2021
@cart cart added the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Jul 23, 2021
@mockersf mockersf removed the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Jul 24, 2021
@LegNeato
Copy link
Contributor Author

LegNeato commented Aug 8, 2021

Closing in favor of the entropy PR. Will revisit this in the future.

@LegNeato LegNeato closed this Aug 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Feature A new feature, making something new possible
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants