bevy-settings-lib
A flexible settings management library for Bevy with async saving, multiple formats, and built‑in validation.
This library provides a convenient way to save, load, and reload settings in Bevy applications. It supports text formats (TOML, JSON) and binary (postcard) with atomic write‑then‑rename to prevent file corruption, with a clean, event‑driven API.
Features
- Any number of configurations – each configuration has its own data type and file name.
- File names can be explicit or derived from the struct name (automatically converted to snake_case).
- Asynchronous saving with atomic write‑then‑rename – files are never left in a corrupted state.
- Format support: TOML (default), JSON, binary (postcard).
- Load from OS‑standard directories (via
directoriescrate) or from the game's local folder. - Events:
PersistSetting<S>,PersistAllSettings,ReloadSetting<S>,SettingsSaveError<S>. - Partial loading – if a file does not exist,
S::default()is used. - Validation – every settings type must implement
ValidatedSettingto normalize values after loading and before saving. - Thread‑safe – background threads handle file I/O without blocking the main thread.
Installation
Add to your Cargo.toml:
[]
= "0.1"
Quick Start
use *;
use ;
use ;
// Mandatory validation implementation (can be empty if no validation needed)
Guide
1. Defining a Settings Type
Your settings type must be a Bevy Resource and implement Serialize, Deserialize, Clone, Debug, Default, and
PartialEq.
It also must implement the ValidatedSetting trait, which is called after loading and before saving to normalize
values.
2. Plugin Configuration
Use SettingsPluginConfig to control where and how settings are stored.
| Field | Description | Default |
|---|---|---|
domain |
Top‑level domain for OS‑specific paths (e.g., "com", "org"). |
"com" |
company |
Company/organization name (required for SystemConfigDir). |
"" |
project |
Project name (required for SystemConfigDir). |
"" |
format |
Serialization format: FormatKind::Toml, Json, or Binary. |
Toml |
file_name |
Explicit file name (without extension). If None, derived from the struct name. |
None |
storage |
Where to store files: SettingsStorage::SystemConfigDir (OS‑standard) or GameLocalDir (next to executable). |
SystemConfigDir |
Example with custom configuration:
let config = SettingsPluginConfig ;
app.add_plugins;
3. Storage Locations
-
SystemConfigDir– uses the OS‑standard configuration directory:- Windows:
%APPDATA%\Company\Project\config\ - macOS:
~/Library/Application Support/company/project/config/ - Linux:
~/.config/company/project/config/
- Windows:
-
GameLocalDir– uses the directory containing the executable (ideal for portable installations).
4. Saving Settings
Trigger saving by sending a PersistSetting<S> event. The event can optionally carry a new value to replace the current
settings before saving.
// Save current settings
commands.trigger;
// Save with a new value
commands.trigger;
To save all settings types at once (if you have multiple plugins), use PersistAllSettings:
commands.trigger;
5. Reloading Settings
Reload settings from disk with a ReloadSetting<S> event:
commands.trigger;
Note: If the settings file does not exist when reloading, the current in‑memory settings remain unchanged.
The library does not automatically reset toS::default(). To reset, sendPersistSettingwith a new value or implement your own logic.
6. Handling Errors
If a background save fails, a SettingsSaveError<S> event is emitted. You can observe it to notify the user or log the
error.
7. Multiple Settings Types
You can have as many independent settings types as you need – just add a separate SettingsPlugin for each.
app.add_plugins
.add_plugins;
Each will be stored in its own file and can be saved/reloaded independently.
8. First Launch: Dynamic Defaults and File Creation
By default, the library does not create a settings file on disk until the first call to PersistSetting (or
PersistAllSettings).
This lazy creation is safe and efficient: your game runs with in‑memory S::default() values, and the file appears only
when the player actually changes and saves something.
However, you may need to adapt default settings to the runtime environment (screen resolution, system language, hardware
capabilities).
Here is the recommended pattern:
- Generate dynamic defaults in a system that runs after the window is created (e.g., in
PostStartupor anOnEnterstate). - Modify the
ResMut<S>resource directly – no file is written yet. - Optionally force file creation only if you really need a file to exist from the start (e.g., for external tools).
In that case, trigger
PersistSettingafter setting your dynamic defaults, but checkpath.exists()first to avoid overwriting an existing configuration.
Important Notes
- Company and project names must not contain invalid characters for
ProjectDirsand cannot be empty when usingSystemConfigDir– the library will panic. WithGameLocalDirthese fields are optional (may be empty). - No auto‑save – the developer decides when to trigger saving.
- Validation is mandatory – even if you don't need validation, you must provide an empty
validateimplementation.
Examples
#TODO
Run an example with:
API Reference
Full API documentation is available on docs.rs.
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
See CHANGELOG.md for a history of changes.
License
Licensed under the MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).