From b00d8e33f68c373d78dabe1c2c023c14260d974e Mon Sep 17 00:00:00 2001 From: demenik Date: Wed, 26 Nov 2025 18:39:02 +0100 Subject: [PATCH] feat: Add savegame dumping (#36) --- Cargo.lock | 66 ++++++++++++++++++++++++-- Cargo.toml | 1 + src/components/mod.rs | 1 + src/components/phase.rs | 9 ++-- src/components/pom.rs | 3 +- src/components/savegame.rs | 33 +++++++++++++ src/components/tile.rs | 3 +- src/components/ui.rs | 5 ++ src/main.rs | 1 + src/messages/mod.rs | 1 + src/messages/savegame.rs | 4 ++ src/plugins/mod.rs | 2 + src/plugins/phase.rs | 8 +++- src/plugins/savegame.rs | 95 +++++++++++++++++++++++++++++++++++++ src/plugins/start_screen.rs | 8 +++- src/plugins/status.rs | 34 +++++++++++++ 16 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 src/components/savegame.rs create mode 100644 src/messages/savegame.rs create mode 100644 src/plugins/savegame.rs diff --git a/Cargo.lock b/Cargo.lock index e6579aa..21c0c78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -1092,7 +1092,7 @@ dependencies = [ "critical-section", "foldhash 0.2.0", "futures-channel", - "getrandom", + "getrandom 0.3.4", "hashbrown 0.16.0", "js-sys", "portable-atomic", @@ -2079,6 +2079,27 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -2380,6 +2401,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -2783,7 +2815,7 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] @@ -3464,6 +3496,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.48" @@ -3622,6 +3660,7 @@ dependencies = [ "bevy", "bevy_aseprite_ultra", "bevy_dev_tools", + "directories", "serde", "serde_json", ] @@ -3754,7 +3793,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.4", ] [[package]] @@ -3819,6 +3858,17 @@ dependencies = [ "bitflags 2.9.4", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.17", +] + [[package]] name = "regex" version = "1.12.2" @@ -4575,7 +4625,7 @@ version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom", + "getrandom 0.3.4", "js-sys", "serde", "wasm-bindgen", @@ -4620,6 +4670,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" diff --git a/Cargo.toml b/Cargo.toml index 529d8e9..c345354 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ bevy_aseprite_ultra = "0.7.0" bevy_dev_tools = "0.17.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +directories = "6.0" diff --git a/src/components/mod.rs b/src/components/mod.rs index 1f63dcf..03bc99f 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,4 +1,5 @@ pub mod phase; pub mod pom; +pub mod savegame; pub mod tile; pub mod ui; diff --git a/src/components/phase.rs b/src/components/phase.rs index 1b15849..1fee59a 100644 --- a/src/components/phase.rs +++ b/src/components/phase.rs @@ -1,6 +1,7 @@ use bevy::prelude::*; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Phase { Break { duration: f32 }, Focus { duration: f32 }, @@ -51,10 +52,10 @@ impl Phase { } } -#[derive(Resource, Debug)] +#[derive(Resource, Debug, Serialize, Deserialize, Clone)] pub struct CurrentPhase(pub Phase); -#[derive(Resource, Debug)] +#[derive(Resource, Debug, Serialize, Deserialize, Clone)] pub struct TimerSettings { pub focus_duration: f32, pub short_break_duration: f32, @@ -73,7 +74,7 @@ impl Default for TimerSettings { } } -#[derive(Resource, Debug, Default)] +#[derive(Resource, Debug, Default, Serialize, Deserialize, Clone)] pub struct SessionTracker { pub completed_focus_phases: u32, } diff --git a/src/components/pom.rs b/src/components/pom.rs index 469e036..5b6c5a8 100644 --- a/src/components/pom.rs +++ b/src/components/pom.rs @@ -1,11 +1,12 @@ use std::collections::VecDeque; use bevy::prelude::*; +use serde::{Deserialize, Serialize}; #[derive(Component)] pub struct Pom; -#[derive(Component)] +#[derive(Component, Serialize, Deserialize, Clone, Copy)] pub struct GridPosition { pub x: u32, pub y: u32, diff --git a/src/components/savegame.rs b/src/components/savegame.rs new file mode 100644 index 0000000..88c6015 --- /dev/null +++ b/src/components/savegame.rs @@ -0,0 +1,33 @@ +use bevy::prelude::*; +use directories::ProjectDirs; +use std::path::PathBuf; + +#[derive(Resource)] +pub struct SavegamePath(pub PathBuf); + +impl SavegamePath { + fn get_project_path() -> Option { + let project_dirs = ProjectDirs::from("de", "demenik", "pomomon-garden"); + + if let Some(dirs) = project_dirs { + Some(dirs.data_local_dir().to_path_buf()) + } else { + None + } + } + + pub fn new(name: &str) -> Self { + let base_path = Self::get_project_path().unwrap_or_else(|| { + println!( + "Could not determine platform-specific save directory. Falling back to `./saves/" + ); + PathBuf::from("./saves/") + }); + + if let Err(e) = std::fs::create_dir_all(&base_path) { + panic!("Failed to create save directory at {:?}: {}", base_path, e); + } + + Self(base_path.join(name)) + } +} diff --git a/src/components/tile.rs b/src/components/tile.rs index 12d7376..9bb994c 100644 --- a/src/components/tile.rs +++ b/src/components/tile.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use serde::{Deserialize, Serialize}; use crate::errors::GridError; @@ -8,7 +9,7 @@ pub struct Tile { pub y: u32, } -#[derive(Component, Default)] +#[derive(Component, Default, Serialize, Deserialize, Clone, Copy, Debug)] pub enum TileState { #[default] Unclaimed, diff --git a/src/components/ui.rs b/src/components/ui.rs index 4ef0a3f..33e0dd2 100644 --- a/src/components/ui.rs +++ b/src/components/ui.rs @@ -8,3 +8,8 @@ pub struct UiPhaseText; #[derive(Component)] pub struct UiTimerText; + +#[derive(Component)] +pub enum UiStatusButton { + SavegameDump, +} diff --git a/src/main.rs b/src/main.rs index e7b159d..c143b41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ fn main() { plugins::InputPlugin, plugins::PhasePlugin, plugins::StatusPlugin, + plugins::SavegamePlugin, )) .insert_resource(config) .run(); diff --git a/src/messages/mod.rs b/src/messages/mod.rs index 0e44e6d..de05e56 100644 --- a/src/messages/mod.rs +++ b/src/messages/mod.rs @@ -1,3 +1,4 @@ pub mod interact; pub mod r#move; pub mod phase; +pub mod savegame; diff --git a/src/messages/savegame.rs b/src/messages/savegame.rs new file mode 100644 index 0000000..b084cb2 --- /dev/null +++ b/src/messages/savegame.rs @@ -0,0 +1,4 @@ +use bevy::prelude::*; + +#[derive(Message)] +pub struct SavegameDumpMessage; diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 7eab8fd..a387290 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -4,6 +4,7 @@ pub mod grid; pub mod input; pub mod phase; pub mod pom; +pub mod savegame; pub mod start_screen; pub mod status; @@ -13,5 +14,6 @@ pub use grid::GridPlugin; pub use input::InputPlugin; pub use phase::PhasePlugin; pub use pom::PomPlugin; +pub use savegame::SavegamePlugin; pub use start_screen::StartScreenPlugin; pub use status::StatusPlugin; diff --git a/src/plugins/phase.rs b/src/plugins/phase.rs index 2747b20..66c3662 100644 --- a/src/plugins/phase.rs +++ b/src/plugins/phase.rs @@ -1,4 +1,8 @@ -use crate::{components::phase::*, messages::phase::*, states::AppState}; +use crate::{ + components::phase::*, + messages::{phase::*, savegame::SavegameDumpMessage}, + states::AppState, +}; use bevy::prelude::*; pub struct PhasePlugin; @@ -63,6 +67,7 @@ fn tick_timer( time: Res