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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Save/Load plugin for persistent game state
//!
//! This plugin provides:
//! - Declarative persistence via `#[require(Save)]` component marker
//! - Type-safe serialization using Bevy's Reflect system
//! - Automatic entity reference mapping (Entity IDs preserved across save/load)
//! - Model/View separation (save game logic, not visuals)
//! - Multiple save slots with metadata
//! - Auto-save functionality
//! - Error handling and notifications
//!
//! # Architecture
//!
//! The save/load plugin wraps [moonshine_save](https://github.com/Zeenobit/moonshine_save):
//! - **moonshine_save**: Core save/load functionality (serialization, entity mapping)
//! - **ISSUN Wrapper**: Ergonomic commands, slot management, validation, error handling
//!
//! # Usage Example
//!
//! ```ignore
//! use bevy::prelude::*;
//! use issun_bevy::plugins::save_load::{
//! SaveLoadPlugin, SaveLoadConfig,
//! Save, Unload,
//! SaveRequested, LoadRequested,
//! SaveMetadata,
//! };
//!
//! // Register the plugin
//! App::new()
//! .add_plugins(SaveLoadPlugin::default())
//! .add_systems(Startup, setup)
//! .add_systems(Update, save_game)
//! .run();
//!
//! // Mark components for saving
//! #[derive(Component, Reflect)]
//! #[reflect(Component)]
//! #[require(Save)] // ← Makes this component saveable!
//! struct Player {
//! name: String,
//! level: u32,
//! }
//!
//! // Mark visual components for despawn before load
//! #[derive(Component, Reflect)]
//! #[reflect(Component)]
//! #[require(Unload)] // ← Will be despawned before load
//! struct Sprite {
//! texture: Handle<Image>,
//! }
//!
//! // Save game
//! fn save_game(mut commands: Commands, keyboard: Res<ButtonInput<KeyCode>>) {
//! if keyboard.just_pressed(KeyCode::F5) {
//! commands.write_message(SaveRequested {
//! slot_name: "quicksave".into(),
//! metadata: None, // Auto-generated
//! });
//! }
//! }
//!
//! // Load game
//! fn load_game(mut commands: Commands, keyboard: Res<ButtonInput<KeyCode>>) {
//! if keyboard.just_pressed(KeyCode::F9) {
//! commands.write_message(LoadRequested {
//! slot_name: "quicksave".into(),
//! });
//! }
//! }
//!
//! // Listen to save/load events
//! fn on_save_completed(mut messages: MessageReader<SaveCompleted>) {
//! for msg in messages.read() {
//! info!("Game saved to slot: {}", msg.slot_name);
//! }
//! }
//!
//! fn on_load_completed(mut messages: MessageReader<LoadCompleted>) {
//! for msg in messages.read() {
//! info!("Game loaded from slot: {}", msg.slot_name);
//! }
//! }
//! ```
//!
//! # Model/View Separation
//!
//! **Save only game logic, not visuals:**
//! - Components with `#[require(Save)]` are persisted
//! - Components with `#[require(Unload)]` are despawned before load
//! - Visual entities should be respawned after load based on saved data
//!
//! # File Format
//!
//! Saves are stored as RON (Rusty Object Notation) files in the save directory:
//! ```text
//! ./saves/
//! ├── slot_1.ron (manual save)
//! ├── slot_2.ron (manual save)
//! ├── auto_save.ron (auto-save)
//! └── quicksave.ron (quicksave)
//! ```
// Re-export public API
pub use ;
pub use *;
pub use SaveLoadPlugin;
pub use ;
pub use *;