use crate::system::AppState; use bevy::asset::{AddAsset, AssetLoader, AssetServer, BoxedFuture, LoadContext, LoadedAsset}; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy::reflect::{TypePath, TypeUuid}; use paste::paste; use serde::{Deserialize, Serialize}; macro_rules! impl_config_lifecycle { ($(($struct: ident, $ext: literal)),*) => { paste! { #[allow(dead_code)] mod _inner { use super::*; $( struct [<$struct Loader>]; impl AssetLoader for [<$struct Loader>] { fn load<'a>( &'a self, bytes: &'a [u8], load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, anyhow::Result<()>> { Box::pin(async move { let value: $struct = serde_json::from_slice(bytes)?; load_context.set_default_asset(LoadedAsset::new(value)); Ok(()) }) } fn extensions(&self) -> &[&str] { &[$ext] } } #[derive(Resource)] struct [<$struct Wrapper>] { handle: Handle<$struct>, } #[derive(SystemParam)] pub struct [<Fetch $struct>]<'w> { wrapper: Res<'w, [<$struct Wrapper>]>, asset: Res<'w, Assets<$struct>>, } impl<'w> [<Fetch $struct>]<'w> { pub fn get(&self) -> Option<&$struct> { self.asset.get(&self.wrapper.handle) } } fn [<init_ $struct:snake>](mut commands: Commands, assets: Res<AssetServer>) { let handle = assets.load(format!("config/config.{}", $ext)); commands.insert_resource([<$struct Wrapper>] { handle }); } )* pub struct ConfigsPlugin; impl Plugin for ConfigsPlugin { fn build(&self, app: &mut App) {$( app.add_asset::<$struct>() .add_asset_loader([<$struct Loader>]) .add_systems(OnEnter(AppState::Preload), [<init_ $struct:snake>]); )*} } } pub use _inner::ConfigsPlugin; pub use _inner::{$([<Fetch $struct>]),*}; } }; } #[derive(Copy, Clone, TypeUuid, TypePath, Debug, Default, Serialize, Deserialize)] #[uuid = "f7d88160-28e4-11ee-bcad-b34d9ff949be"] pub struct PhysicsConfig { pub gravity: f32, pub jump: f32, pub speed: f32, } impl_config_lifecycle!((PhysicsConfig, "phys"));