Securing saves and preferences
Protecting local storage is crucial when your game relies on client-side data for progression, in-app purchases, or cooldown timers. ACTk ships with three complementary systems—ObscuredFile, ObscuredFilePrefs, and ObscuredPrefs—plus editor tooling to inspect data during development.
Choosing the right storage solution
| Feature | ObscuredPrefs | ObscuredFilePrefs | ObscuredFile |
|---|---|---|---|
| Backend | Unity PlayerPrefs | File system | File system |
| API Style | PlayerPrefs-like | PlayerPrefs-like | Raw byte arrays |
| Performance | PlayerPrefs + Binary Serialization + Encryption | File I/O + Binary Serialization + Encryption | File I/O + Encryption |
| Data Size | Small values only | Moderate size (caches data in memory) | Any size (no cache, direct I/O) |
| Thread Safety | Main thread only | Background thread safe | Background thread safe |
| Auto-save | Automatic | Automatic (configurable) | Manual |
| Use Cases | Settings, small progress data | Moderate save files, complex data | Large save files and raw data |
ObscuredFile
ObscuredFile provides an encrypted file wrapper that detects tampering and optionally locks data to a specific device or custom identifier.
Basic Usage
var file = new ObscuredFile();
var result = file.WriteAllBytes(data);
if (result.Success) Debug.Log("Saved!");
Advanced Features
- Device lock: Use
DeviceLockSettingsto bind data to specific devices - Custom encryption: Configure
EncryptionSettingswith custom passwords - Async operations: Call
UnityApiResultsHolder.InitForAsyncUsage(true)for background operations - Event handling: Subscribe to
DataFromAnotherDeviceDetectedandNotGenuineDataDetected
For detailed API reference and examples, see ObscuredFile API documentation.
Key Capabilities
- Encrypts payloads (optional) to hide sensitive information from plain-text inspection.
- Validates integrity even when you disable encryption—cheaters cannot silently edit stored values.
- Locks the file to a device ID or your own identifier. See Device lock for configuration tips.
- Works with raw byte arrays so you can integrate any serialization stack.
Warning
Always call UnityApiResultsHolder.InitForAsyncUsage(true); from the main thread before interacting with ObscuredFile from background threads.
ObscuredFilePrefs
ObscuredFilePrefs builds on top of ObscuredFile and emulates the familiar PlayerPrefs API while automatically encrypting values:
Basic Usage
ObscuredFilePrefs.Init();
ObscuredFilePrefs.Set("coins", 100);
var coins = ObscuredFilePrefs.Get("coins", 0);
Advanced Features
- Custom settings: Initialize with custom file name, encryption, and device lock settings
- Data types: Supports all Unity serializable types including Vector3, Color, DateTime, byte arrays
- Async operations: Use
UnityApiResultsHolder.InitForAsyncUsage(true)for background operations - Key management:
HasKey(),GetKeys(),DeleteKey(),DeleteAll() - Memory management:
LoadPrefs(),Save(),UnloadPrefs()for memory control
For detailed API reference and examples, see ObscuredFilePrefs API documentation.
Example implementations
- Basic usage: See
ObscuredFilePrefsExamples.csinExamples/API Examples/Scripts/Runtime/UsageExamples/ - DOTS integration: See
UIActionSystem.csinExamples/DOTS ECS Examples/Scripts/UI/Systems/
Advantages
- Supports all basic C# types,
BigInteger, byte arrays,DateTime, and common Unity structs. - Emits events when data is tampered with or loaded from another device.
- AutoSave feature enabled by default to prevent data loss on app quit (desktop) and app losing focus (mobile).
Tip
Mix regular PlayerPrefs and ObscuredFilePrefs judiciously. Encrypt only the values that matter to control performance overhead.
ObscuredPrefs
ObscuredPrefs provides a direct replacement for Unity's PlayerPrefs with built-in encryption and tamper detection:
using CodeStage.AntiCheat.Storage;
ObscuredPrefs.Set("currentLifeBarObscured", 88.4f);
var currentLifeBar = ObscuredPrefs.Get("currentLifeBarObscured", 0f);
// same as
ObscuredPrefs.Set<float>("currentLifeBarObscured", 88.4f);
var currentLifeBar = ObscuredPrefs.Get<float>("currentLifeBarObscured");
Key features:
- Drop-in replacement for PlayerPrefs with automatic encryption
- Extended type support including all basic C# types,
BigInteger,byte[],DateTime, and Unity types - Device Lock support to prevent save sharing between devices
- Migration tools to convert existing PlayerPrefs data automatically
- Event system with
NotGenuineDataDetectedfor tamper detection
For detailed API reference and examples, see ObscuredPrefs API documentation.
Example implementations
- Basic usage: See
ObscuredPrefsExamples.csinExamples/API Examples/Scripts/Runtime/UsageExamples/ - DOTS integration: See
UIActionSystem.csinExamples/DOTS ECS Examples/Scripts/UI/Systems/
Migration tips:
- Replace
PlayerPrefscalls withObscuredPrefsthroughout your project - Use
preservePlayerPrefsflag to keep original PlayerPrefs keys during migration - Mix regular PlayerPrefs and ObscuredPrefs for different data types (use different key names)
- Use
ObscuredPrefs.DeleteAll()instead ofPlayerPrefs.DeleteAll()to properly clear internals
Warning
ObscuredPrefs is slower than regular PlayerPrefs due to encryption overhead. Use it for sensitive data only, not for large datasets like maps or databases.
Prefs Editor
Use Tools > Code Stage > Anti-Cheat Toolkit > Prefs Editor to inspect and edit both PlayerPrefs and ObscuredPrefs directly in the editor.

Highlights:
- Add, edit, encrypt, and delete preferences without writing code
- Filter and sort large datasets (50 records per page) with pagination support
- Copy values to the clipboard and monitor progress when parsing large preference sets
- Works on Windows, macOS, and Linux editors
- Supports both PlayerPrefs and ObscuredPrefs in a unified interface
- Overwrite confirmation and progress bars for large datasets (1000+ records)
- Ignores Unity's internal prefs (UnitySelectMonitor, UnityGraphicsQuality, etc.)
Note
On Windows, prefs stored in Editor and standalone player are in different locations, so the Prefs Editor only works with Editor-saved preferences. Windows PlayerPrefs are stored in the registry at HKEY_CURRENT_USER\Software\[Your Company]\[Your Product].
Device lock
ObscuredFile, ObscuredFilePrefs, and ObscuredPrefs can restrict data to a device or custom ID via DeviceLockSettings. This prevents save games from being shared between devices.
Lock levels
None– data remains unlocked but can read both locked and unlocked dataSoft– existing data stays readable; all new saves lock to the current deviceStrict– only accepts data locked to the current device; all new saves lock to the current device
Event handling
Subscribe to DataFromAnotherDeviceDetected to intercept foreign payloads:
- In
ObscuredPrefs: fires once per session - In
ObscuredFile/ObscuredFilePrefs: fires every time a file is read
Platform considerations
Warning
iOS does not provide a reliable persistent device identifier. If you rely on Device Lock, set a stable, app-defined identifier via DeviceIdHolder.DeviceId (for example, an authenticated account/user ID). Plan recovery flows using DeviceLockTamperingSensitivity when an identifier changes (SIM swap, device restore, reinstall). See related notes in Troubleshooting.
Android: Device Lock may require additional permissions depending on your setup. See Permissions and compliance for symbols to avoid unnecessary permissions in builds where Device Lock or Time Cheating Detector are not used.
Performance optimization
- Call
DeviceIdHolder.ForceLockToDeviceInit()during loading screens to avoid CPU spikes on first device ID access - Use
DeviceIdHolder.DeviceIdto set custom identifiers (useful for server-side account systems)
Sensitivity control
When using Strict mode, you can adjust DeviceLockTamperingSensitivity:
Normal– default strict behavior, rejects foreign data and fires detection eventLow– detection event still fires but data remains readableDisabled– no event fired, useful for data recovery when Device ID changes unexpectedly