use bevy_ecs::{
query::{QuerySingleError, With},
world::World,
};
use bevy_prng::EntropySource;
use rand_core::SeedableRng;
use crate::{global::GlobalRng, seed::RngSeed};
pub trait ForkableRng: EntropySource {
type Output: EntropySource;
#[inline]
fn fork_rng(&mut self) -> Self::Output {
Self::Output::from_rng(self)
}
}
pub trait ForkableAsRng: EntropySource {
type Output<R>: EntropySource
where
R: EntropySource;
#[inline]
fn fork_as<T: EntropySource>(&mut self) -> Self::Output<T> {
Self::Output::<T>::from_rng(self)
}
}
pub trait ForkableSeed<S: EntropySource>: EntropySource {
type Output: SeedSource<S>;
#[inline]
fn fork_seed(&mut self) -> Self::Output {
let mut seed = S::Seed::default();
self.fill_bytes(seed.as_mut());
Self::Output::from_seed(seed)
}
}
pub trait ForkableAsSeed<S: EntropySource>: EntropySource {
type Output<T>: SeedSource<T>
where
T: EntropySource;
#[inline]
fn fork_as_seed<T: EntropySource>(&mut self) -> Self::Output<T> {
let mut seed = T::Seed::default();
self.fill_bytes(seed.as_mut());
Self::Output::<T>::from_seed(seed)
}
}
pub trait ForkableInnerSeed<S: EntropySource>: EntropySource {
type Output: Send + Sync + Clone + AsMut<[u8]> + Default;
#[inline]
fn fork_inner_seed(&mut self) -> Self::Output {
let mut seed = Self::Output::default();
self.fill_bytes(seed.as_mut());
seed
}
}
pub trait SeedSource<R: EntropySource>: private::SealedSeed<R> {
fn from_seed(seed: R::Seed) -> Self;
fn get_seed(&self) -> &R::Seed;
fn clone_seed(&self) -> R::Seed;
#[deprecated = "use either `SeedSource::from_local_entropy` or `SeedSource::from_os_rng` instead"]
fn from_entropy() -> Self
where
Self: Sized,
{
#[cfg(feature = "thread_local_entropy")]
{
Self::from_local_entropy()
}
#[cfg(not(feature = "thread_local_entropy"))]
{
Self::from_os_rng()
}
}
#[cfg(feature = "thread_local_entropy")]
fn try_from_local_entropy() -> Result<Self, std::thread::AccessError>
where
Self: Sized,
{
use rand_core::Rng;
let mut dest = R::Seed::default();
bevy_prng::ThreadLocalEntropy::get()?.fill_bytes(dest.as_mut());
Ok(Self::from_seed(dest))
}
#[cfg(feature = "thread_local_entropy")]
#[inline]
fn from_local_entropy() -> Self
where
Self: Sized,
{
Self::try_from_local_entropy().expect("Unable to source user-space entropy for seeding")
}
fn try_from_os_rng() -> Result<Self, getrandom::Error>
where
Self: Sized,
{
let mut dest = R::Seed::default();
getrandom::fill(dest.as_mut())?;
Ok(Self::from_seed(dest))
}
#[inline]
fn from_os_rng() -> Self
where
Self: Sized,
{
Self::try_from_os_rng().expect("Unable to source os/hardware entropy for seeding")
}
}
pub trait ForkRngExt {
type Error: core::error::Error;
type Output<Rng>;
fn fork_rng<Target: EntropySource>(&mut self) -> Self::Output<Target>;
fn fork_as<Source: EntropySource, Target: EntropySource>(&mut self) -> Self::Output<Target>;
}
pub trait ForkSeedExt {
type Error: core::error::Error;
type Output<Rng>;
fn fork_seed<Target: EntropySource>(&mut self) -> Self::Output<RngSeed<Target>>;
fn fork_as_seed<Source: EntropySource, Target: EntropySource>(
&mut self,
) -> Self::Output<RngSeed<Target>>;
fn fork_inner_seed<Target: EntropySource>(&mut self) -> Self::Output<Target::Seed>;
}
impl ForkRngExt for &mut World {
type Error = QuerySingleError;
type Output<Rng> = Result<Rng, Self::Error>;
#[inline]
fn fork_rng<Target: EntropySource>(&mut self) -> Self::Output<Target> {
self.fork_as::<Target, Target>()
}
fn fork_as<Source: EntropySource, Target: EntropySource>(&mut self) -> Self::Output<Target> {
self.query_filtered::<&mut Source, With<GlobalRng>>()
.single_mut(self)
.map(|mut global| global.fork_as::<Target>())
}
}
impl ForkSeedExt for &mut World {
type Error = QuerySingleError;
type Output<Rng> = Result<Rng, Self::Error>;
#[inline]
fn fork_seed<Target: EntropySource>(&mut self) -> Self::Output<RngSeed<Target>> {
self.fork_as_seed::<Target, Target>()
}
fn fork_as_seed<Source: EntropySource, Target: EntropySource>(
&mut self,
) -> Self::Output<RngSeed<Target>> {
self.query_filtered::<&mut Source, With<GlobalRng>>()
.single_mut(self)
.map(|mut global| global.fork_as_seed::<Target>())
}
fn fork_inner_seed<Target: EntropySource>(&mut self) -> Self::Output<Target::Seed> {
self.query_filtered::<&mut Target, With<GlobalRng>>()
.single_mut(self)
.map(|mut global| global.fork_inner_seed())
}
}
impl<R> ForkableRng for R
where
R: EntropySource,
{
type Output = R;
}
impl<R> ForkableAsRng for R
where
R: EntropySource,
{
type Output<T>
= T
where
T: EntropySource;
}
impl<R> ForkableSeed<R> for R
where
R: EntropySource,
R::Seed: Send + Sync + Clone,
{
type Output = RngSeed<R>;
}
impl<R> ForkableAsSeed<R> for R
where
R: EntropySource,
{
type Output<T>
= RngSeed<T>
where
T: EntropySource,
T::Seed: Send + Sync + Clone;
}
impl<R> ForkableInnerSeed<R> for R
where
R: EntropySource,
R::Seed: Send + Sync + Clone + AsMut<[u8]> + Default,
{
type Output = R::Seed;
}
mod private {
use super::{EntropySource, SeedSource};
pub trait SealedSeed<R>
where
R: EntropySource,
{
}
impl<R, T> SealedSeed<R> for T
where
T: SeedSource<R>,
R: EntropySource,
R::Seed: Send + Sync + Clone,
{
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use bevy_prng::{ChaCha8Rng, ChaCha12Rng};
use super::*;
#[test]
fn forking() {
let mut rng1 = ChaCha8Rng::default();
let rng2 = rng1.fork_rng();
assert_ne!(rng1, rng2, "forked Entropys should not match each other");
}
#[test]
fn forking_as() {
let mut rng1 = ChaCha12Rng::default();
let rng2 = rng1.fork_as::<ChaCha8Rng>();
let rng1 = format!("{rng1:?}");
let rng2 = format!("{rng2:?}");
assert_ne!(&rng1, &rng2, "forked Entropys should not match each other");
}
#[cfg(feature = "bevy_reflect")]
#[test]
fn type_paths() {
use bevy_reflect::TypePath;
assert_eq!("bevy_prng::ChaCha8Rng", ChaCha8Rng::type_path());
assert_eq!("ChaCha8Rng", ChaCha8Rng::short_type_path());
}
}