bevy_simple_random 1.0.1

A crate for easily adding random number generation to Bevy.
Documentation
#[cfg(test)]
mod tests;

use bevy::prelude::*;
use getrandom::rand_core::{SeedableRng, TryRng};
#[cfg(feature = "chacha")]
pub use rand_chacha;
pub use getrandom::rand_core;

/// A Bevy resource for generating random numbers globally.
/// - `R` the underlying random number generator.
///
/// Example:
/// ```rust
/// # use bevy::prelude::*;
/// # use bevy_simple_random::{GlobalRng, rand_core::{Rng, SeedableRng}};
/// # use rand_chacha::ChaChaRng;
/// #
/// # fn main() {
/// #    let mut app = App::new();
/// #    app.insert_resource(GlobalRng::<ChaChaRng>::seed_from_u64(0));
/// #    app.add_systems(Update, update);
/// #    app.run();
/// # }
/// #
/// fn update(mut rng: ResMut<GlobalRng<ChaChaRng>>) {
///     let random_number = rng.next_u32();
///     println!("Random Number: {random_number}");
/// }
/// ```
#[derive(Resource, Deref, DerefMut)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GlobalRng<R: TryRng + Sync + Send>(R);

impl<R: TryRng + SeedableRng + Sync + Send> SeedableRng for GlobalRng<R> {
    type Seed = R::Seed;

    fn from_seed(seed: Self::Seed) -> Self {
        R::from_seed(seed).into()
    }
}

impl<R: TryRng + SeedableRng + Sync + Send> From<R> for GlobalRng<R> {
    fn from(value: R) -> Self {
        Self(value)
    }
}

/// A Bevy component for generating random numbers locally for a component
/// - `R` the underlying random number generator
///
/// ```rust
/// # use bevy::prelude::*;
/// # use bevy_simple_random::{EntityRng, rand_core::{Rng, SeedableRng}};
/// # use rand_chacha::ChaChaRng;
/// #
/// # fn main() {
/// #     let mut app = App::new();
/// #     app.add_systems(Startup, init);
/// #     app.add_systems(Update, update);
/// # }
/// #
/// fn init(mut commands: Commands) {
///     commands.spawn(EntityRng::<ChaChaRng>::seed_from_u64(0));
/// }
///
/// fn update(rng_entities: Query<&mut EntityRng<ChaChaRng>>) {
///     for mut rng in rng_entities {
///         let random_number = rng.next_u32();
///         println!("Random Number: {random_number}");
///     }
/// }
/// ```
#[derive(Component, Deref, DerefMut)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EntityRng<R: TryRng + Sync + Send> (R);

impl<R: TryRng + SeedableRng + Sync + Send> SeedableRng for EntityRng<R> {
    type Seed = R::Seed;

    fn from_seed(seed: Self::Seed) -> Self {
        R::from_seed(seed).into()
    }
}

impl<R: TryRng + SeedableRng + Sync + Send> From<R> for EntityRng<R> {
    fn from(value: R) -> Self {
        Self(value)
    }
}

#[cfg(feature = "from_time")]
pub mod from_time {
    use getrandom::rand_core::{SeedableRng, TryRng};

    /// Generates a seed based on the current time
    /// **Note**: Panics if current time is before epoch
    pub fn time_seed() -> u64 {
        web_time::SystemTime::now()
            .duration_since(web_time::UNIX_EPOCH)
            .expect("Current time should be after epoch")
            .as_secs()
    }

    /// Generates a rng based on the current time
    /// **Note**: Panics if current time is before epoch
    pub fn random_from_time<R: SeedableRng>() -> R {
        R::seed_from_u64(time_seed())
    }

    impl<R: SeedableRng + TryRng + Sync + Send> super::EntityRng<R> {
        /// Generates an Entity Rng based on the current time
        /// **Note**: Panics if current time is before epoch
        pub fn from_time() -> Self {
            random_from_time::<R>().into()
        }
    }

    impl<R: SeedableRng + TryRng + Sync + Send> Default for super::EntityRng<R> {
        /// Generates an Entity Rng based on the current time
        /// **Note**: Panics if current time is before epoch
        fn default() -> Self {
            Self::from_time()
        }
    }

    impl<R: SeedableRng + TryRng + Sync + Send> super::GlobalRng<R> {
        /// Generates a Global Rng based on the current time
        /// **Note**: Panics if current time is before epoch
        pub fn from_time() -> Self {
            random_from_time::<R>().into()
        }
    }

    impl<R: SeedableRng + TryRng + Sync + Send> Default for super::GlobalRng<R> {
        /// Generates a Global Rng based on the current time
        /// **Note**: Panics if current time is before epoch
        fn default() -> Self {
            Self::from_time()
        }
    }
}

/// Alias for `GlobalRng` that uses `ChaChaRng` for it's underlying random number generator.
///
/// Example:
/// ```rust
/// # use bevy::prelude::*;
/// # use bevy_simple_random::{ChaChaGlobalRng, rand_core::{Rng, SeedableRng}};
/// #
/// # fn main() {
/// #    let mut app = App::new();
/// #    app.insert_resource(ChaChaGlobalRng::seed_from_u64(0));
/// #    app.add_systems(Update, update);
/// #    app.run();
/// # }
/// #
/// fn update(mut rng: ResMut<ChaChaGlobalRng>) {
///     let random_number = rng.next_u32();
///     println!("Random Number: {random_number}");
/// }
#[cfg(feature = "chacha")]
pub type ChaChaGlobalRng = GlobalRng<rand_chacha::ChaChaRng>;

/// Alias for `EntityRng` that uses `ChaChaRng` for it's underlying random number generator.
///
/// ```rust
/// # use bevy::prelude::*;
/// # use bevy_simple_random::{ChaChaEntityRng, rand_core::{Rng, SeedableRng}};
/// #
/// # fn main() {
/// #     let mut app = App::new();
/// #     app.add_systems(Startup, init);
/// #     app.add_systems(Update, update);
/// # }
/// #
/// fn init(mut commands: Commands) {
///     commands.spawn(ChaChaEntityRng::seed_from_u64(0));
/// }
///
/// fn update(rng_entities: Query<&mut ChaChaEntityRng>) {
///     for mut rng in rng_entities {
///         let random_number = rng.next_u32();
///         println!("Random Number: {random_number}");
///     }
/// }
/// ```
#[cfg(feature = "chacha")]
pub type ChaChaEntityRng = EntityRng<rand_chacha::ChaChaRng>;