diff --git a/.gitignore b/.gitignore index ce1b959..83927d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/target +target .data .direnv .envrc diff --git a/Cargo.lock b/Cargo.lock index 7fa248d..2966fc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.72", - "toml_edit 0.22.16", + "toml_edit 0.22.17", ] [[package]] @@ -1602,6 +1602,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.72", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.72", +] + [[package]] name = "dasp_sample" version = "0.11.0" @@ -1912,6 +1947,9 @@ dependencies = [ "include_dir", "leafwing-input-manager", "log", + "macros", + "serde", + "toml", ] [[package]] @@ -2183,6 +2221,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.25.2" @@ -2525,6 +2569,16 @@ dependencies = [ "libc", ] +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3518,6 +3572,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3621,6 +3684,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "svg_fmt" version = "0.4.3" @@ -3755,11 +3824,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.17", +] + [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3774,11 +3858,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.22.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow 0.6.15", ] diff --git a/Cargo.toml b/Cargo.toml index 97eb9f2..891166e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,12 +28,17 @@ embedded = ["include_dir"] bevy = { version = "0.14", features = ["serialize", "wayland"] } leafwing-input-manager = { git = "https://github.com/Leafwing-Studios/leafwing-input-manager" } +# Local crates +macros = { path = "macros" } + # Other dependencies -include_dir = { version = "0.7.4", optional = true } +include_dir = { version = "0.7", optional = true } log = { version = "*", features = [ "max_level_debug", "release_max_level_warn", ] } +serde = { version = "1.0", features = ["derive"] } +toml = { version = "0.8" } [profile.dev] opt-level = 1 diff --git a/flake.nix b/flake.nix index 48daa2c..eba0b5b 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ general-deps = with pkgs; [ # Rust cargo-watch + cargo-expand rust-analyzer-unwrapped # Toml taplo diff --git a/macros/Cargo.lock b/macros/Cargo.lock new file mode 100644 index 0000000..63956bf --- /dev/null +++ b/macros/Cargo.lock @@ -0,0 +1,101 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..3ca0192 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "macros" +description = "procedural macros for the game" +repository = "https://github.com/eerii/hello-bevy" +authors = ["Eri "] +license = "Apache-2.0 OR MIT" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +darling = { version = "0.20" } +quote = { version = "1.0" } +syn = { version = "2.0" } +proc-macro2 = { version = "1.0" } diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..8218c77 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,72 @@ +// For a more complete solution, look at https://github.com/umut-sahin/bevy-persistent + +use darling::{ast::NestedMeta, FromMeta}; +use proc_macro as pm; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse2, DeriveInput}; + +const DATA_PATH: &str = ".data"; + +#[derive(Debug, FromMeta)] +struct PersistentArgs { + name: String, +} + +#[proc_macro_attribute] +pub fn persistent(args: pm::TokenStream, input: pm::TokenStream) -> pm::TokenStream { + let input: TokenStream = input.into(); + let DeriveInput { ident, .. } = parse2(input.clone()).unwrap(); + + let attr_args = match NestedMeta::parse_meta_list(args.into()) { + Ok(v) => v, + Err(e) => { + return pm::TokenStream::from(darling::Error::from(e).write_errors()); + }, + }; + let args = match PersistentArgs::from_list(&attr_args) { + Ok(v) => v, + Err(e) => { + return pm::TokenStream::from(e.write_errors()); + }, + }; + let path = format!("{}/{}.toml", DATA_PATH, args.name); + let name = ident.to_string(); + + let output = quote! { + #[derive(Resource, Serialize, Deserialize)] + #input + + impl Persistent for #ident { + fn load() -> Self { + let mut data = Self::default(); + data.reload(); + data + } + + fn reload(&mut self) { + *self = match std::fs::read_to_string(#path) { + Ok(data) => toml::from_str(&data).unwrap_or_default(), + Err(_) => Self::default(), + }; + } + + fn persist(&self) { + let data = toml::to_string(self).unwrap(); + let _ = std::fs::write(#path, data); + debug!("{} updated, saved in {}", #name, #path); + } + + fn update(&mut self, f: impl Fn(&mut #ident)) { + f(self); + self.persist() + } + + fn reset(&mut self) { + *self = Self::default(); + self.persist() + } + } + }; + output.into() +} diff --git a/src/base.rs b/src/base.rs index b17ae40..875959a 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,10 +1,11 @@ +mod data; mod sets; mod states; use bevy::prelude::*; pub(super) fn plugin(app: &mut App) { - app.add_plugins((sets::plugin, states::plugin)); + app.add_plugins((data::plugin, sets::plugin, states::plugin)); } pub mod prelude { diff --git a/src/base/data.rs b/src/base/data.rs new file mode 100644 index 0000000..03d3ea0 --- /dev/null +++ b/src/base/data.rs @@ -0,0 +1,25 @@ +use bevy::prelude::*; +use macros::persistent; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +const DATA_PATH: &str = ".data"; // If changed update in `macros/lib.rs` + +pub(super) fn plugin(app: &mut App) { + let _ = std::fs::create_dir_all(DATA_PATH); + app.insert_resource(SaveData::load()); +} + +#[allow(dead_code)] +trait Persistent: Resource + Serialize + DeserializeOwned + Default { + fn load() -> Self; + fn reload(&mut self); + fn persist(&self); + fn update(&mut self, f: impl Fn(&mut Self)); + fn reset(&mut self); +} + +#[derive(Default)] +#[persistent(name = "save")] +pub struct SaveData { + pub test: bool, +} diff --git a/src/lib.rs b/src/lib.rs index 65a25e5..b236f76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,7 @@ // TODO: Documentation and code examples // Readme -// TODO: Embedded assets -// TODO: Data persistence (custom implementation) -// If using derive macros, also use them for assets +// TODO: Also use derive macros for assets // TODO: UI Widgets // Main menu // UI Navigation with input (custom implementation)