Mental model
The SDK does not know your save format. The game still serializes and deserializes its own data. Korvayne only protects the byte array between those steps.
Save:
game state -> serialize -> AC_ProtectSaveFile -> write .ksave
Load:
.ksave -> AC_VerifySaveFile -> deserialize only if verification succeeds
If verification fails, reject the load, restore a backup, or start a clean profile. Do not deserialize tampered bytes.
Context string
Pass the same stable context string when saving and loading. The context binds a save to the parts of your game that should not change silently.
| Context part | Example | Why it helps |
|---|---|---|
| Player/account | player=studio-123 | Discourages copying saves between accounts. |
| Save slot | slot=campaign-1 | Separates autosaves, manual saves, and profiles. |
| Schema version | schema=2 | Lets you intentionally invalidate older formats after migrations. |
| Mode or build family | mode=ranked | Keeps high-value modes stricter than local sandbox modes. |
const char* ctx = "player=studio-123;slot=campaign-1;schema=2";
C API
Use file helpers when your game already has a save path. Use buffer helpers when your engine wants to handle file I/O itself.
int rc = AC_ProtectSaveFile("profile.ksave", saveBytes, saveLen, ctx);
if (rc != AC_SAVE_OK) {
Log(AC_SaveResultName(rc));
}
unsigned outLen = 0;
unsigned char out[1024 * 1024];
int rc = AC_VerifySaveFile("profile.ksave", ctx, out, sizeof(out), &outLen);
if (rc == AC_SAVE_OK) {
Deserialize(out, outLen);
} else if (rc == AC_SAVE_TAMPERED) {
RejectLoad();
}
| Return | Meaning | Typical response |
|---|---|---|
AC_SAVE_OK | Protected or verified successfully. | Continue save/load. |
AC_SAVE_BUFFER_TOO_SMALL | The output buffer cannot hold the verified payload. | Allocate a larger buffer and retry. |
AC_SAVE_TAMPERED | Envelope, context, or payload verification failed. | Reject load and optionally restore backup. |
AC_SAVE_UNLICENSED | The runtime is not armed under a valid license. | Log a clear integration error. |
AC_SAVE_DISABLED | [SaveGameProtection] enabled = 0. | Do not treat as player tamper. |
Engine wrappers
Unity and Unreal integrations should expose simple save helpers so gameplay teams do not call raw native exports.
// Unity wrapper
var bytes = Encoding.UTF8.GetBytes(json);
int rc = KorvayneSdk.ProtectSaveFile(path, bytes, context);
if (KorvayneSdk.TryVerifySaveFile(path, context, out var verified, out rc)) {
var json = Encoding.UTF8.GetString(verified);
}
// Unreal wrapper
TArray<uint8> Plain = SerializeMySave();
ACTGuard::ProtectSaveFile(TCHAR_TO_UTF8(*Path), Plain.GetData(), Plain.Num(), TCHAR_TO_UTF8(*Context));
TArray<uint8> Verified;
int32 Rc = ACTGuard::VerifySaveFile(TCHAR_TO_UTF8(*Path), TCHAR_TO_UTF8(*Context), Verified);
Config
The Configurator writes a dedicated section for the API. These settings do not serialize anything automatically; they control how the API behaves when the game calls it.
[TelemetryEvents]
savegame_integrity = 1
[SaveGameProtection]
enabled = 1
mode = sign_and_obfuscate
obfuscate_payload = 1
tamper_severity = high
max_save_bytes = 1048576
on_tamper = reject_load
context_binding = game_id,player_id,slot,schema
telemetry_event = 1
| Key | Meaning |
|---|---|
enabled | Allows the SaveGame API to protect and verify payloads. |
obfuscate_payload | Stores the protected payload in a non-plain form. Integrity still comes from signing, not from obfuscation alone. |
tamper_severity | Severity used for telemetry when verification fails. |
max_save_bytes | Maximum accepted plain save payload size. |
on_tamper | Guidance for the game wrapper. The runtime returns an error; the game decides the UX. |
Telemetry
When telemetry is enabled and savegame_integrity = 1, failed verification emits a savegame_integrity event with sensor SaveGame. Treat it as high-confidence local file tamper evidence, but still avoid automatic account-level punishment from the client alone.
Good backend handling is simple: store the event, show it in support/debug views, and let the game reject that specific load locally.
Limits
This is integrity friction, not DRM. It catches casual save editors, copied files, and accidental corruption. A determined attacker who can patch an offline user-mode client can still bypass the caller, so high-value economy, inventory, unlocks, and ranked rewards should stay server-authoritative.
- Use it for local profiles, campaign saves, settings-like progression, and tamper evidence.
- Do not use it as the only authority for multiplayer currency, paid items, ranked progress, or bans.
- Keep one backup of the last known-good save so players can recover from corruption.
- Use a schema version in the context whenever you change the serialized format.
