Newer
Older
//! 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, Asset, TextureAtlasLayout};
//! use bevy::reflect::{TypePath};
//! 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<TextureAtlasLayout>>)]
//! 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, Serialize, Deserialize, Asset)]
//! #[loader(extension = "mcr", uuid = "00000000-0000-0000-0000-000000000000",
//! asset_name = some_resource, index_name = some_index_of_resources)]
//! 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)
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
//!
//! // 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));
//! }
//! ```
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
pub(crate) mod fqpath;
pub(crate) mod utils;
pub(crate) mod asset_system;

Louis
committed
pub(crate) mod event_system;
pub(crate) mod json_loader;
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 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, Serialize, Deserialize, Asset)]
/// #[loader(extension = "satt", uuid = "00000000-0000-0000-0000-000000000000")]
/// 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 serde::{Deserialize, Serialize};
/// # use micro_games_macros::JsonLoader;
///
/// #[derive(JsonLoader, TypePath, Serialize, Deserialize, Asset)]
/// #[loader(
/// extension = "asset.json", uuid = "00000000-0000-0000-0000-000000000000",
/// storage = inner_module::SimpleAssetLocator,
/// asset_name = some_asset_prop, index_name = set_of_assets
/// )]
/// 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, TextureAtlasLayout};
/// use micro_games_macros::asset_system;
///
/// #[asset_system]
/// pub struct AssetHandles {
/// 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, TextureAtlasLayout, UVec2};
/// 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<TextureAtlasLayout>>)]
/// pub fn load_spritesheet(&mut self, path: String, name: String) -> Handle<TextureAtlasLayout> {
/// let image_handle = self.load_image(path, name.clone());
/// 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()

Louis
committed
///
/// ```rust
/// use bevy::prelude::{Entity, EventWriter, IVec2};
/// use micro_games_macros::event_system;
///
/// #[event_system]
/// enum ActionEvent {

Louis
committed
/// }
///
/// pub fn emit_wait_event(mut event_writer: EventWriter<WaitEvent>) {

Louis
committed
/// }
/// ```
#[proc_macro_attribute]
pub fn event_system(_: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
event_system::event_system(input).into()
}
310
311
312
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
339
340
341
342
343
344
345
346
347
348
349
/// 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
}
/// 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()
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/// Create a TagFinder type, used for checking what tag components might contain
///
/// ## Examples
///
/// Spawns a number of entities, and then perform different behaviour based on tags
///
/// ```
/// # use bevy::prelude::*;
/// # use micro_games_macros::tag_finder;
///
/// #[tag_finder]
/// struct TagFinder {
/// ally: Ally,
/// enemy: Enemy,
/// }
///
/// pub fn spawn_entities(mut commands: Commands) {
/// commands.spawn((Ally, SpatialBundle::default()));
/// commands.spawn((Enemy, SpatialBundle::default()));
/// commands.spawn((Enemy, SpatialBundle::default()));
/// }
///
/// pub fn my_checking_system(query: Query<(Entity, &Transform)>, tags: TagFinder) {
/// for (e, t) in &query {
/// if tags.is_ally(e) {
/// println!("Celebrate");
/// } else if tags.is_enemy(e) {
/// println!("Run away")
/// }
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn tag_finder(_: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
tag_finder::tag_finder(input).into()
}
/// Marker attribute, used exclusively by other proc macros
#[proc_macro_attribute]
#[doc(hidden)]
pub fn sparse(_: TokenStream, input: TokenStream) -> TokenStream {
input
}