beasty-save-system / guides / settings.md

Settings

BeastySaveSettings holds every option a save or load call uses: which storage backend it goes to, where the file goes, whether it is encrypted, whether a backup is kept, how strictly it loads, and which data version it belongs to. This page lists every field, its default, and the reason you would change it.

Settings are per call, not per project

There is no global settings asset. Every BeastySave method takes a BeastySaveSettings as an argument, and BeastySaveManager holds one in its inspector for SaveAll and LoadAll.

That means two saves in the same project can behave completely differently. An autosave can be tolerant, unencrypted and backup-free, writing to an Autosaves folder; a manual save can be strict, encrypted and backed up, writing to Saves. Nothing in the system forces them to agree.

static readonly BeastySaveSettings Manual = new BeastySaveSettings
{
    Folder = "Saves",
    Encrypted = true,
    EncryptionKey = "your own key",
};

static readonly BeastySaveSettings Auto = new BeastySaveSettings
{
    Folder = "Autosaves",
    Backup = false,
    Strict = false,
};

If you never write C#, you get one settings block: the Settings field on the BeastySaveManager component, edited in the inspector or in the Save Manager window. That is enough for most games.

The fields

The settings fields on the BeastySaveManager inspector

FieldTypeDefaultWhat it does
Folderstring"Saves"Subfolder under DataPath that holds the save files.
Extensionstring"save"The file extension, without the dot.
DataPathstringemptyAbsolute base path. Empty means Application.persistentDataPath.
EncryptedboolfalseEncrypt the data payload with AES-256.
EncryptionKeystringemptyThe key. Empty means the shared default key that ships with the asset.
BackupbooltrueKeep the previous file as <slot>.<ext>.bak when overwriting a slot.
StrictbooltrueAll-or-nothing loading. false skips bad fields and warns.
DataVersionint1The schema version stamped into every save. Drives migrations.
StorageIdstringemptyWhich storage backend the calls use. Empty means local files. Drawn as the Storage dropdown in the editor.
ScopeByUserboolfalseKeep local files in a per-user subfolder when a user provider is registered.
StorageIBeastySaveStoragenullA backend instance set from code. When assigned it wins over StorageId. Not serialized.

The final path of a local save is:

<DataPath or persistentDataPath>/<Folder>/<slot>.<Extension>

— with one extra level, <Folder>/<userId>/, when the save is scoped by user.

BeastySave.GetFolderPath(settings) and BeastySave.GetSlotPath("slot1", settings) give you those paths without you having to assemble them. Note their limits: they describe the local-file layout only — they are not scoped per user and they say nothing about where a cloud backend stores a save.

Folder

Change it to separate kinds of save from one another — Saves, Autosaves, Profiles. Different folders are independent: ListSlots on one never sees the other’s files, and a slot named slot1 can exist in both at once.

Extension

Cosmetic. save by default; sav, dat, json all work. No leading dot. Changing it on a shipped game means your players’ existing files are invisible to the new build, so choose it before you ship.

DataPath

Leave it empty. Application.persistentDataPath is the per-user writable location Unity gives you on every platform, and it is where saves belong.

Set it when you have a specific reason — an editor tool that writes into the project folder, a test that writes into a temp directory. Setting it to a path the player’s OS will not let you write to is how you get an IoError.

Encrypted

Off by default. Turn it on and the data section of the file becomes a Base64 blob instead of readable JSON. The envelope and the metadata stay in plain text, so a save-slot screen still works.

Two things you must know before turning it on:

  • The flag has to match how the file was written. With Encrypted = true the system refuses to load a plain-text save, and with Encrypted = false it cannot read an encrypted one. Flipping this setting on a shipped game strands every existing save.
  • This is obfuscation, not security. Read encryption.md before you rely on it.

EncryptionKey

Any non-empty string works — a 32-byte AES key is derived from it with SHA-256. You do not need to produce a key of a particular length.

Leave it empty and the system uses BeastySaveSettings.SharedDefaultEncryptionKey, which ships inside every copy of the asset. Anyone who owns Beasty Save System has that string. It exists so encryption works out of the box, not so you can ship with it. If encryption is on and you have not set your own key, the system warns you once in the editor and in development builds.

Set your own key before you ship. Then read encryption.md, which is honest about the fact that your key ships inside your game too.

Backup

On by default. When a save overwrites an existing slot, the old file is rotated to <slot>.<ext>.bak first. BeastySave.RestoreBackup puts it back; the Save Manager window has a Restore Backup button that does the same.

Two behaviours worth knowing:

  • The first save of a slot creates no backup. There was nothing to rotate.
  • A slot whose checksum does not verify is never rotated into the backup. A corrupt file cannot destroy your last good copy.

Turn it off only where you save so often that the extra file is a real cost — a frequent autosave, say. The default is on for a good reason. See backups-and-corruption.md.

Strict

On by default. A strict load is all-or-nothing: if one field cannot be read back, the load fails and nothing is applied. Your game state stays exactly as it was, and you get an error result to show the player.

Tolerant (Strict = false) skips the field it could not read, records it in LoadResult.Warnings, and loads the rest.

Ship strict. Use tolerant when you renamed a field mid-production and would rather lose that one value than lose the save. The full comparison, including the rollback behaviour and one important caveat about struct roots, is in strict-vs-tolerant.md.

DataVersion

The schema version of your data. It starts at 1 and is written into every file. When you load a file whose version is lower than the current one, the registered migrations run in order to bring it up to date.

You bump it when you change the shape of your save data in a way old files cannot survive, and you register a migration for the step. A file with a version higher than your setting fails with VersionTooNew — an old build refuses to guess at a save written by a newer one, rather than corrupting it.

See versioning-and-migrations.md.

StorageId

Empty by default, which means local files — the behaviour every other section of this page describes. Set it to a registered backend id (firestore, realtime-db, or an id of your own) and the same calls write to that backend instead. In the editor the field is drawn as the Storage dropdown, listing every backend whose module compiled.

When the id names a backend that is not available in the project — its module did not compile because the SDK is missing — every call fails with BackendUnavailable until the module is restored or the id is changed. On a cloud backend, Folder, Extension and DataPath do not apply: the backend stores saves per user in the cloud.

See Storage backends for choosing a backend and Firebase for the cloud setup.

ScopeByUser

Off by default. Turn it on and local saves are kept in a per-user subfolder (<Folder>/<userId>/…) whenever a user provider is registered — useful when several people share one machine, or when you want local saves to line up with the per-user layout a cloud backend uses anyway. Cloud backends always scope by user; this flag only affects local files.

Whose id is used comes from BeastySaveUsers — see Storage backends.

Slot names

The slot is the bare file name, and the system validates it. A rejected slot makes the call fail with InvalidArgument and a message saying exactly why.

A slot name is rejected when:

RuleExample that is rejectedWhy
It is empty or whitespace""There is no file name.
It contains a path separatorsaves/slot1, saves\slot1It could write outside the save folder.
It contains ..../slot1Same reason.
It is a rooted pathC:\slot1, /slot1Same reason.
It contains characters that are not valid in a file nameslot:1, slot*The OS cannot create the file.
It is a Windows reserved device nameCON, PRN, AUX, NUL, COM1COM9, LPT1LPT9The name addresses a device, not a file — CON.save included.

The Windows device names are rejected on every platform, not only Windows. A save folder written on macOS or Linux stays usable if the player later copies it to a Windows machine.

If your game lets players name their own saves, run the name past BeastySave.Save and show the InvalidArgument message, or sanitise it first. Do not assume a name is fine because it looked fine on your machine.

See also