From c9ad39496444356f022ebefa256a1584ebc8234b Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Sun, 19 Nov 2023 04:00:47 +0000 Subject: [PATCH] Update JSON Loader to assets v2 --- Cargo.toml | 4 +- README.md | 2 + src/fqpath.rs | 13 ++++- src/json_loader/components.rs | 97 ++++++++++++++++++++++++++--------- src/json_loader/context.rs | 2 + src/lib.rs | 12 ++--- 6 files changed, 98 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe6bde3..4f5979e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "micro_games_macros" -version = "0.1.1" +version = "0.2.0" edition = "2021" authors = ["Louis Capitanchik <contact@louiscap.co>"] description = "Utility macros to make it easier to build complex systems with Bevy" @@ -28,7 +28,7 @@ anyhow = "1.0.72" [dev-dependencies.bevy] -version = "0.11.0" +version = "0.12.0" default-features = false features = [ "bevy_asset", diff --git a/README.md b/README.md index 1dc3028..04dfdd0 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ A collection of utility macros for building games +**Current Version Support: 0.2.x -> Bevy 0.12** + ## Macros For executable examples, visit the rustdoc and/or read the doctests in `src/lib.rs` diff --git a/src/fqpath.rs b/src/fqpath.rs index 1fa9809..7d9b5c7 100644 --- a/src/fqpath.rs +++ b/src/fqpath.rs @@ -24,7 +24,15 @@ fq!(FQDefault => ::core::default::Default); fq!(FQSend => ::core::marker::Send); fq!(FQSync => ::core::marker::Sync); fq!(FQFrom => ::std::convert::From); +fq!(FQIOError => ::std::io::Error); +fq!(FQError => ::std::error::Error); +fq!(FQFormatter => ::std::fmt::Formatter); +fq!(FQFormatResult => ::std::fmt::Result); +fq!(FQOption => ::std::option::Option); +fq!(FQResult => ::std::result::Result); +fq!(FQBox => ::std::boxed::Box); +fq!(SerdeJsonError => ::serde_json::Error); fq!(ImportBevyPrelude => use ::bevy::prelude::*); fq!(BevyApp => ::bevy::app::App); @@ -40,6 +48,7 @@ fq!(BevyTypeUuid => ::bevy::reflect::TypeUuid); fq!(BevyDeref => ::bevy::prelude::Deref); fq!(BevyDerefMut => ::bevy::prelude::DerefMut); fq!(BevyHandle => ::bevy::asset::Handle); +fq!(BevyAsset => ::bevy::asset::Asset); fq!(BevyAssets => ::bevy::asset::Assets); fq!(BevyAssetEvent => ::bevy::asset::AssetEvent); fq!(BevyAssetLoader => ::bevy::asset::AssetLoader); @@ -47,7 +56,9 @@ fq!(BevyBoxedFuture => ::bevy::asset::BoxedFuture); fq!(BevyLoadContext => ::bevy::asset::LoadContext); fq!(BevyLoadedAsset => ::bevy::asset::LoadedAsset); fq!(BevyAssetServer => ::bevy::asset::AssetServer); -fq!(BevyAddAsset => ::bevy::asset::AddAsset); +fq!(BevyAddAsset => ::bevy::asset::AssetApp); +fq!(BevyAsyncRead => ::bevy::asset::AsyncReadExt); +fq!(BevyAssetReader => ::bevy::asset::io::Reader); #[cfg(feature = "kayak")] fq!(KayakWidget => ::kayak_ui::prelude::Widget); diff --git a/src/json_loader/components.rs b/src/json_loader/components.rs index 2c8cff1..7b331b3 100644 --- a/src/json_loader/components.rs +++ b/src/json_loader/components.rs @@ -10,6 +10,7 @@ pub fn json_loader(input: DeriveInput) -> TokenStream { Err(stream) => return stream, }; + let error_type = define_error_type(&context); let asset_set = define_loading_type(&context); let index_type = define_index_type(&context); let plugin = define_plugin(&context); @@ -17,6 +18,7 @@ pub fn json_loader(input: DeriveInput) -> TokenStream { let load_handler = define_load_handler(&context); quote! { + #error_type #asset_set #index_type #loader @@ -56,57 +58,105 @@ pub fn define_index_type( }: &IdentContext, ) -> TokenStream { quote! { - #[derive(#FQDebug, #BevyTypePath, #BevyTypeUuid, #BevyDeref, #BevyDerefMut)] + #[derive(#FQDebug, #BevyTypePath, #BevyTypeUuid, #BevyDeref, #BevyDerefMut, #BevyAsset)] #[uuid = #uuid] #vis struct #index_name(pub #FQHashMap<String, #BevyHandle<#asset_name>>); } } +pub fn define_error_type(IdentContext { error_name, .. }: &IdentContext) -> TokenStream { + quote! { + #[derive(#FQDebug)] + pub enum #error_name { + Io(#FQIOError), + Json(#SerdeJsonError) + } + + impl #FQDisplay for #error_name { + fn fmt(&self, f: &mut #FQFormatter<'_>) -> #FQFormatResult { + match self { + #error_name::Io(err) => err.fmt(f), + #error_name::Json(err) => err.fmt(f), + } + } + } + + impl #FQError for #error_name { + fn source(&self) -> #FQOption<&(dyn #FQError + 'static)> { + match self { + #error_name::Io(err) => Some(err), + #error_name::Json(err) => Some(err), + } + } + } + + impl #FQFrom<#FQIOError> for #error_name { + fn from(value: #FQIOError) -> #error_name { + #error_name::Io(value) + } + } + + impl #FQFrom<#SerdeJsonError> for #error_name { + fn from(value: #SerdeJsonError) -> #error_name { + #error_name::Json(value) + } + } + } +} + pub fn define_loader( IdentContext { vis, loader_name, index_name, set_name, + error_name, id_field, extension, .. }: &IdentContext, ) -> TokenStream { quote! { + #[derive(#FQDefault)] #vis struct #loader_name; impl #BevyAssetLoader for #loader_name { + type Asset = #index_name; + type Settings = (); + type Error = #error_name; + fn load<'a>( &'a self, - bytes: &'a [u8], + mut reader: &'a mut #BevyAssetReader, + _settings: &'a Self::Settings, load_context: &'a mut #BevyLoadContext, - ) -> #BevyBoxedFuture<'a, ::anyhow::Result<()>> { - Box::pin(async { - let data: #set_name = ::serde_json::from_slice(bytes)?; + ) -> #BevyBoxedFuture<'a, #FQResult<Self::Asset, Self::Error>> { + #FQBox::pin(async move { + let mut bytes = #FQVec::new(); + #BevyAsyncRead::read_to_end(&mut reader, &mut bytes).await?; + let data: #set_name = ::serde_json::from_slice(bytes.as_slice())?; let mut asset_map = #FQHashMap::new(); match data { #set_name::One(single_asset) => { let asset_id = format!("{}", &single_asset.#id_field); - let handle = load_context.set_labeled_asset( - asset_id.as_str(), - #BevyLoadedAsset::new(single_asset), + let handle = load_context.add_labeled_asset( + asset_id.clone(), + single_asset, ); asset_map.insert(asset_id, handle); } #set_name::Many(asset_list) => { for single_asset in asset_list.into_iter() { let asset_id = format!("{}", &single_asset.#id_field); - let handle = load_context.set_labeled_asset( - asset_id.as_str(), - #BevyLoadedAsset::new(single_asset), + let handle = load_context.add_labeled_asset( + asset_id.clone(), + single_asset, ); asset_map.insert(asset_id, handle); } } } - load_context.set_default_asset(#BevyLoadedAsset::new(#index_name(asset_map))); - Ok(()) + Ok(#index_name(asset_map)) }) } @@ -132,9 +182,9 @@ pub fn define_plugin( #vis struct #plugin_name; impl #BevyPlugin for #plugin_name { fn build(&self, app: &mut #BevyApp) { - #BevyAddAsset::add_asset::<#asset_name>(app); - #BevyAddAsset::add_asset::<#index_name>(app); - #BevyAddAsset::add_asset_loader(app, #loader_name); + #BevyAddAsset::init_asset::<#asset_name>(app); + #BevyAddAsset::init_asset::<#index_name>(app); + #BevyAddAsset::init_asset_loader::<#loader_name>(app); app.add_systems(#BevyUpdate, #handler_name); } } @@ -163,21 +213,22 @@ pub fn define_load_handler( for event in events.iter() { match event { - #BevyAssetEvent::Created { handle } | #BevyAssetEvent::Modified { handle } => { + #BevyAssetEvent::LoadedWithDependencies { id } | #BevyAssetEvent::Added { id } | #BevyAssetEvent::Modified { id } => { + let handle = #BevyHandle::Weak(*id); if let Some(asset_container) = asset_data.get(handle) { for (id, handle) in asset_container.iter() { asset_storage.#storage_asset_name.insert(id.clone(), handle.clone()); } } } - #BevyAssetEvent::Removed { handle } => { - asset_storage.#storage_index_name.iter().for_each(|(id, stored)| { - if stored == handle { + #BevyAssetEvent::Removed { id } => { + asset_storage.#storage_index_name.iter().for_each(|(key, stored)| { + if stored.id() == *id { if let Some(asset_container) = asset_data.get(stored) { - for (id, _) in asset_container.iter() { - removed_assets.push(id.clone()); + for (key, _) in asset_container.iter() { + removed_assets.push(key.clone()); } - removed_containers.push(id.clone()); + removed_containers.push(key.clone()); } } }); diff --git a/src/json_loader/context.rs b/src/json_loader/context.rs index a6930d6..9ec8c38 100644 --- a/src/json_loader/context.rs +++ b/src/json_loader/context.rs @@ -13,6 +13,7 @@ pub struct IdentContext { pub plugin_name: Ident, pub loader_name: Ident, pub handler_name: Ident, + pub error_name: Ident, // -- Property names pub id_field: Ident, pub storage_type: TokenStream, @@ -105,6 +106,7 @@ impl IdentContext { loader_name: ident_suffix(&ident, "Loader"), set_name: ident_suffix(&ident, "Set"), plugin_name: ident_suffix(&ident, "Plugin"), + error_name: ident_suffix(&ident, "Error"), handler_name: ident_suffix(&snake_case_ident(&ident), "_load_handler"), asset_name: ident.clone(), diff --git a/src/lib.rs b/src/lib.rs index e58555f..4a5d491 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! to the generated asset system //! //! ```rust -//! use bevy::prelude::{App, DefaultPlugins, Image, Plugin, Res, ResMut, Resource, Assets, TextureAtlas}; +//! use bevy::prelude::{App, DefaultPlugins, Image, Plugin, Res, ResMut, Resource, Assets, Asset, TextureAtlas}; //! use bevy::reflect::{TypePath, TypeUuid}; //! use micro_games_macros::{asset_system, JsonLoader}; //! use serde::{Deserialize, Serialize}; @@ -38,7 +38,7 @@ //! some_index_of_resources: MyCoolResourceIndex, //! } //! -//! #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)] +//! #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize, Asset)] //! #[loader(extension = "mcr", uuid = "00000000-0000-0000-0000-000000000000", //! asset_name = some_resource, index_name = some_index_of_resources)] //! #[uuid = "10000000-0000-0000-0000-000000000001"] @@ -128,7 +128,7 @@ pub(crate) mod std_traits; /// /// ```rust /// # use std::collections::HashMap; -/// # use bevy::asset::Handle; +/// # use bevy::asset::{Handle, Asset}; /// # use bevy::prelude::Resource; /// # use bevy::reflect::{TypePath, TypeUuid}; /// # use serde::{Deserialize, Serialize}; @@ -140,7 +140,7 @@ pub(crate) mod std_traits; /// simple_asset_index: HashMap<String, Handle<SimpleAssetIndex>>, /// } /// -/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)] +/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize, Asset)] /// #[loader(extension = "satt", uuid = "00000000-0000-0000-0000-000000000000")] /// #[uuid = "00000000-0000-0000-0000-000000000001"] /// pub struct SimpleAsset { @@ -159,13 +159,13 @@ pub(crate) mod std_traits; /// /// ```rust /// # use std::collections::HashMap; -/// # use bevy::asset::Handle; +/// # use bevy::asset::{Handle, Asset}; /// # use bevy::prelude::Resource; /// # use bevy::reflect::{TypePath, TypeUuid}; /// # use serde::{Deserialize, Serialize}; /// # use micro_games_macros::JsonLoader; /// -/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)] +/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize, Asset)] /// #[loader( /// extension = "asset.json", uuid = "00000000-0000-0000-0000-000000000000", /// storage = inner_module::SimpleAssetLocator, -- GitLab