# Bevy Preferences API Games and apps need a way to store user settings such as window size, graphics options, sound and music volumes and so on. These settings are typically per-user, and are neither assets (in the traditional sense) nor saved games. Preferences may be set *explicitly* (such as adjusting a music volume slider) or *implicitly* (like the fact that the user has already seen the tutorial and doesn't need to see it again), but in all cases are the result of user interaction. All modern operating systems have designated locations for application preferences. For games that run in a browser, user preferences can either be saved in browser local storage, or stored on a server. The original thread can be found [here](https://github.com/bevyengine/bevy/pull/13312). ## General Requirements ### Cross-Platform The preferences framework should adhere to the platform conventions for preference configuration. For example: * **Linux**: `~/.config/APP_NAME` * **Windows**: `~\AppData\Roaming\APP_NAME` * **MacOS**: `~/Library/Preferences/APP_NAME` For browser-based games, the preferences framework should be able to abstract away the differences between filesystem-based configuration and browser storage APIs. ### Supporting Third-Party Crates It should be possible for libraries and plugins to define their own preference settings. These settings will usually be stored in the same configuration file as the preferences for the main application, albeit in a separate section. For example, a crate which implements a "color picker" widget might want to store the list of recent colors. This preference should automatically be included in the app's preferences configuration, without requiring the app developer to explicitly include it. This means that libraries and plugins will need a mechanism to register their own preference types with the framework. Third-party crates can control what information gets stored in the preferences configuration, but they can't generally control where and when it gets stored, something that is usually controlled by the application and the preferences framework. ### Uniqueness Between Apps / Games The app developer should be able to specify the root name of the preferences config file, so that it doesn't collide with preference files from other games. The API should also encourage (but not require) that the developer provide the name of their organization or domain name as well, to guard against collisions between two games with the same name. ### Uniqueness Between Preferences From Different Crates There should be no collision between preference keys defined by different, independent crates. To prevent this, the keys should be derived from the Rust type system, as determined by Bevy Reflect. ### Multiple Namespaces Although most preferences will be stored in a single configuration file, it may be desirable for certain groups of preferences to be stored in their own dedicated configuration file. An example might be `screenshots.json` or `characters.ron`. ### Fallback configurations Most operating systems allow for multiple levels of configuration, for example a "user" level which applies to a single user, and a "system" level which applies to all users. It should be possible to locate all relevant preference configurations and merge them in priority order. ### Flexible serialization The app developer should be free to decide what serialization format to use for storing the app configuration. Since preference objects will support reflection / serde, it should be possible to serialize preference objects in a variety of formats. That being said, we should also promote consistency by encouraging app developers to use a standard serialization format. ### Eagerness It must be possible to load preferences before plugin initialization. This is because some settings such as window size or display mode need to be initialized once, rather than being initialized to a default setting and then modified later. This means that we will need to be able to load the complete set of preferences before the main game loop starts. This represents a challenge for two reasons: * Some preferences are likely to require asynchronous loading, and we'll need to wait for loading to finish before the main loop starts. * During preference loading, parts of the game may not be fully initialized, but we'll need to initialize at least those parts needed for preference deserialization. ### Change detection It should be possible for subsystems to get notified when a preference changes. These notifications should be somewhat fine-grained, i.e. when you change the audio volume it only notifies the audio subsystem, not the graphics subsystem. However it is not anticipated that we will need notification at the level of individual properties. This change detection feature will only become available when the game actually starts running - that is, no changes will be triggered before the Bevy game loop begins. ### File Watching / Reloading During the discussion, there was strong support for the ability to watch preference files for changes, and automatically re-load the data if those files were changed by an external process. ## Implementation Design Notes This section describes the current thinking on how to implement the preference framework. ### Use of Resources Preferences will be represented in memory as Bevy resources. Each individual preference "object" will be a separate resource. This allows the use of Bevy's change detection mechanism for notification of changes. An earlier version had all preferences stored in a single resource (a map of preferences) but was deemed to be too coarse-grained. ### Use of Reflection We can use the Bevy reflection system to discover preference settings. Preference types can be statically registered by deriving the appropriate trait: ```rust= #[derive(Reflect, Resource, Preferences)] #[reflect(Preferences)] #[preferences_scope(GamePreferences)] struct MyPreferences; ``` ### Use of Asset Server Preferences will be stored as assets using a special `AssetSource` that is created by the preferences framework. This asset source will provide the `"config://"` asset namespace that automatically handles the loading, deserialization, and merging of preference configuration files. Because of the asynchronous nature of loading, and because preferences are meant to be loaded before the game initialiation is complete, we'll need to make several changes to the asset system: * We'll need to register the preferences asset sources before app startup. * We'll need to provide the means to `await` on asset loads before the main game loop runs. ### Use of browser local storage The `LocalStorage` API (which is standard in HTML5) is frequently used in browsers to store user preferences. Accessing this from WASM is complex but possible. Up to 5Mb of data can be stored (possibly more, depending on the browser). `LocalStorage` is a key/value store, and the values stored can be encoded in JSON or other serialization format. ## Open Questions * Saving Preferences API * Whether to save as user vs. system prefs * Use cases for "bevy_settings" (non-game-specific) config - what writes this?