Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! A collection of complimentary utility macros for building games
//!
//! Use the [asset_system] macro to create a set of types that manage loading assets, and then
//! derive [JsonLoader] for any asset type to create a flexible resource loader that links in
//! to the generated asset system
//!
//! ```rust
//! use bevy::prelude::{App, DefaultPlugins, Image, Plugin, Res, ResMut, Resource, Assets, TextureAtlas};
//! use bevy::reflect::{TypePath, TypeUuid};
//! use micro_games_macros::{asset_system, JsonLoader};
//! use serde::{Deserialize, Serialize};
//!
//! // We can customise the properties on the "asset loader" type that the
//! // macro generates by adding "loader_property" annotations. The parameter
//! // to this macro is simple any property declaration that could be added to
//! // a regular type that derives SystemParam (including lifetimes)
//!
//! #[asset_system]
//! #[loader_property(pub sheets: ResMut<'w, Assets<TextureAtlas>>)]
//! pub struct AssetHandles {
//! // For non-JsonLoader assets, you just need to specify a name -> AssetType property.
//! // The generated asset handles type will contain a hashmap of name -> Handle<AssetType>
//! // under the property name specified. Loading functions such as load_asset_name will
//! // be created on the loading type, where the first parameter is the path and the second
//! // parameter is the asset name that will be used in the hashmap for later retrieval of
//! // the asset
//! images: Image,
//! // To use an asset_system with a JsonLoader, you will need to add entries for both the
//! // asset itself and its index type (generated by JsonLoader). Specific instances of
//! // an asset are stored under the Item type (no suffix), keyed in the handle map by their
//! // ID property. The index generated from each file (a map of ID -> Item) is stored under
//! // the Index type (type name: AssetName + Index), keyed by the name provided when loading
//! // the asset
//! some_resource: MyCoolResource,
//! // The property names generate load functions, but don't have to match the name of the
//! // resource being loaded. Index & Item types for a JsonLoader resource can have their
//! // names specified as part of the JsonLoader derive
//! some_index_of_resources: MyCoolResourceIndex,
//! }
//!
//! #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)]
//! #[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"]
//! pub struct MyCoolResource {
//! // You must include exactly one of either a property named "id", or another property annotated
//! // with the "asset_id" attribute.
//! #[asset_id]
//! some_identifier: String,
//! foo: usize,
//! bar: bool,
//! }
//!
//! // The asset system generates a system param for scheduling the load of an asset. It uses bevy's
//! // asset server, which makes the system compatible with plugins such as bevy_embedded_assets
//! pub fn loading_system(mut loader: AssetHandlesLoader) {
//! // The loader contains a number of functions for loading either individual assets
//! // (two parameters, path and id), or a series of assets (a single parameter, a vector
//! // of tuples each denoting path and id)
//! loader.load_images("path/to/my_image.png", "my_image");
//!
//! // JsonLoader assets require the use of the Index type to correctly load them, even for
//! // files that only contain one object instance. The identifier (either id or asset_id)
//! // will be used to add each instance of an asset to the Item type map
//! loader.load_some_index_of_resources("path/to/my_asset.mcr", "my_asset");
//! }
//!
//! pub fn use_asset_system(assets: Res<AssetHandles>) {
//! // JsonLoader assets can be accessed through the item type functions. These functions
//! // provide a weak handle to the asset, meaning that the assets will usually unload
//! // if removed from the AssetHandles type unless another strong handle was created externally
//! let some_resource_handle = assets.some_resource("the_asset_id");
//!
//! // The handle functions exactly match the name of the asset property in the originally
//! // defined struct
//! let image_handle = assets.images("my_image");
//! }
//!
//! pub struct AssetSystemPlugin;
//! impl Plugin for AssetSystemPlugin {
//! fn build(&self, app: &mut App) {
//! app.init_resource::<AssetHandles>()
//! // JsonLoader will create a plugin that wraps all of the functionality for a single
//! // asset type, including loaders and hot reload support
//! .add_plugins(MyCoolResourcePlugin);
//! }
//! }
//!
//! fn main() {
//! let app = App::new()
//! .add_plugins((DefaultPlugins, AssetSystemPlugin));
//! }
//! ```
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
pub(crate) mod fqpath;
pub(crate) mod utils;
pub(crate) mod asset_system;
pub(crate) mod json_loader;
#[cfg(feature = "kayak")]
pub(crate) mod kayak;
pub(crate) mod std_traits;
/// Generate loader and handler implementations for keyed JSON resources. The asset must implement `bevy::asset::Asset`, as well as
/// `serde::Deserialize` and `serde::Serialize`
///
/// Container Attributes:
///
/// - `loader(extension = "")`: *Required* - The file extension associated with this asset
/// - `loader(uuid = "")`: *Required* - The uuid to use for the generated index asset. This _must_ be different to the UUID provided for the asset itself
/// - `loader(storage = qualified::path::to::AssetStorage)`: The qualified path to an asset storage instance. Defaults to `AssetHandles`
/// - `loader(asset_name = my_asset)`: The property of the asset storage used to store the asset. Defaults to the snake case version of the struct name & must be a valid identifier
/// - `loader(index_name = my_asset_index)`: The property of the asset storage used to store an index of the asset types. Defaults to `asset_name`, suffixed with `_index` & must be a valid identifier
///
/// Property Attributes:
///
/// - `loader(asset_id)`: Label a property of the asset as the identifier, to be used as the key on which it will be matched. Must implement [std::fmt::Display].
/// If not present, will be assumed to be a property named "id". If no such property is present, and this attribute is not specified, that is considered an error
///
/// ## Examples
///
/// By default, it is assumed that each resource is keyed by a property named "id", that the
/// storage
///
/// ```rust
/// # use std::collections::HashMap;
/// # use bevy::asset::Handle;
/// # use bevy::prelude::Resource;
/// # use bevy::reflect::{TypePath, TypeUuid};
/// # use serde::{Deserialize, Serialize};
/// # use micro_games_macros::JsonLoader;
///
/// #[derive(Resource)]
/// pub struct AssetHandles {
/// simple_asset: HashMap<String, Handle<SimpleAsset>>,
/// simple_asset_index: HashMap<String, Handle<SimpleAssetIndex>>,
/// }
///
/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)]
/// #[loader(extension = "satt", uuid = "00000000-0000-0000-0000-000000000000")]
/// #[uuid = "00000000-0000-0000-0000-000000000001"]
/// pub struct SimpleAsset {
/// bevy::app::App::new()
/// .add_plugins(bevy::prelude::DefaultPlugins)
/// .add_plugins(SimpleAssetPlugin);
/// }
/// ```
///
/// Attributes can also be used to customise most of the default assumptions made by this derive:
///
/// ```rust
/// # use std::collections::HashMap;
/// # use bevy::asset::Handle;
/// # use bevy::prelude::Resource;
/// # use bevy::reflect::{TypePath, TypeUuid};
/// # use serde::{Deserialize, Serialize};
/// # use micro_games_macros::JsonLoader;
///
/// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)]
/// #[loader(
/// extension = "asset.json", uuid = "00000000-0000-0000-0000-000000000000",
/// storage = inner_module::SimpleAssetLocator,
/// asset_name = some_asset_prop, index_name = set_of_assets
/// )]
/// #[uuid = "00000000-0000-0000-0000-000000000001"]
/// pub struct MyAsset {
/// /// The asset identifier needs to implement [std::fmt::Display]
/// #[asset_id]
/// uniq_ident: usize,
/// }
///
/// pub mod inner_module {
/// # use bevy::prelude::{Handle, Resource};
/// # use std::collections::HashMap;
///
/// #[derive(Resource)]
/// pub struct SimpleAssetLocator {
/// pub some_asset_prop: HashMap<String, Handle<super::MyAsset>>,
/// pub set_of_assets: HashMap<String, Handle<super::MyAssetIndex>>,
/// }
/// bevy::app::App::new()
/// .add_plugins(bevy::prelude::DefaultPlugins)
/// .add_plugins(MyAssetPlugin);
/// }
/// ```
#[proc_macro_derive(JsonLoader, attributes(loader, asset_id))]
pub fn json_loader(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
json_loader::json_loader(input).into()
}
/// Convert a mapping of name -> Asset type into an asset loading system
///
/// ## Examples
///
/// Convert a struct that represents mappings between names and asset types
/// into an asset system to handle loading, and storing asset handles
///
/// ```rust
/// use bevy::prelude::{Image, Res, Resource};
/// use micro_games_macros::asset_system;
///
/// #[asset_system]
/// pub struct AssetHandles {
/// }
///
/// pub fn loading_system(mut loader: AssetHandlesLoader) {
/// loader.load_my_asset("path/to/asset.png", "Asset");
/// }
/// pub fn use_asset_system(assets: Res<AssetHandles>) {
/// }
/// ```
///
/// Annotate any property with a `skip` attribute to omit it from the resulting loader
///
/// ```rust
/// use bevy::prelude::{Image, TextureAtlas};
/// use micro_games_macros::asset_system;
///
/// #[asset_system]
/// pub struct AssetHandles {
/// image: Image,
/// #[skip]
/// spritesheet: TextureAtlas,
/// impl<'w> AssetHandlesLoader<'w> {
/// pub fn load_spritesheet(&mut self, path: String, name: String) {
/// let image_handle = self.load_image(path, name.clone());
/// // let sheet_handle = .. more code ..
/// // self.spritesheet.insert(name, sheet_handle);
/// }
/// }
/// ```
///
/// Include extra properties in the generated loader with one or more `loader_property` attributes.
/// The included property must be a `SystemParam`, and has access to the `'w` lifetime
///
/// ```rust
/// use bevy::prelude::{Assets, Event, EventWriter, Handle, Image, ResMut, TextureAtlas, Vec2};
/// use micro_games_macros::{asset_system, loader_property};
///
/// #[derive(Event)]
/// pub struct LoadingEvent {
/// }
///
/// #[asset_system]
/// #[loader_property(pub load_events: EventWriter<'w, LoadingEvent>)]
/// #[loader_property(pub sheets: ResMut<'w, Assets<TextureAtlas>>)]
/// pub struct AssetHandles {
/// image: Image,
/// #[skip]
/// spritesheet: TextureAtlas,
/// impl<'w> AssetHandlesLoader<'w> {
/// pub fn load_spritesheet(&mut self, path: String, name: String) -> Handle<TextureAtlas> {
/// let image_handle = self.load_image(path, name.clone());
/// let sheet = TextureAtlas::new_empty(image_handle, Vec2::ZERO);
/// let sheet_handle = self.sheets.add(sheet);
///
/// self.storage
/// .spritesheet
/// .insert(name.clone(), sheet_handle.clone());
/// self.load_events.send(LoadingEvent { event_id: 123 });
///
/// sheet_handle
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn asset_system(_: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
asset_system::asset_system(input).into()
/// Marker attribute, used exclusively by other proc macros
#[proc_macro_attribute]
#[doc(hidden)]
pub fn skip(_: TokenStream, input: TokenStream) -> TokenStream {
input
}
/// Marker attribute, used exclusively by other proc macros
#[proc_macro_attribute]
#[doc(hidden)]
pub fn loader_property(_: TokenStream, input: TokenStream) -> TokenStream {
input
}
#[proc_macro_derive(Widget)]
#[cfg(feature = "kayak")]
pub fn derive_kayak_wigdet(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
kayak::derive_widget(input).into()
}
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/// Derive [std::convert::From] for single property tuple or named struct types
///
/// ## Examples
///
/// `FromInner` can derive `From` for single-property tuple structs
///
/// ```rust
/// use micro_games_macros::FromInner;
///
/// #[derive(PartialEq, Eq, Debug, FromInner)]
/// struct MyValue(i32);
///
/// let value = 123i32.into();
///
/// assert_eq!(MyValue(123), value);
/// assert_eq!(MyValue::from(123), value);
/// ```
///
/// As well as single-property named structs
///
///
/// ```rust
/// use micro_games_macros::FromInner;
///
/// #[derive(PartialEq, Eq, Debug, FromInner)]
/// struct MyValue {
/// }
///
/// let value = MyValue::from(2000);
///
/// assert_eq!(value.foo_bar, 2000);
/// assert_eq!(MyValue { foo_bar: 2000 }, value);
/// assert_eq!(MyValue::from(2000), value);
/// ```
#[proc_macro_derive(FromInner)]
pub fn derive_from_inner(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
std_traits::from_inner::derive(input).into()