KorvayneGuides
Save integrity

SaveGame protection

Korvayne Runtime can wrap serialized save bytes with an integrity envelope before they are written to disk, then verify that envelope before the game loads the save again.

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 partExampleWhy it helps
Player/accountplayer=studio-123Discourages copying saves between accounts.
Save slotslot=campaign-1Separates autosaves, manual saves, and profiles.
Schema versionschema=2Lets you intentionally invalidate older formats after migrations.
Mode or build familymode=rankedKeeps 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();
}
ReturnMeaningTypical response
AC_SAVE_OKProtected or verified successfully.Continue save/load.
AC_SAVE_BUFFER_TOO_SMALLThe output buffer cannot hold the verified payload.Allocate a larger buffer and retry.
AC_SAVE_TAMPEREDEnvelope, context, or payload verification failed.Reject load and optionally restore backup.
AC_SAVE_UNLICENSEDThe 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
KeyMeaning
enabledAllows the SaveGame API to protect and verify payloads.
obfuscate_payloadStores the protected payload in a non-plain form. Integrity still comes from signing, not from obfuscation alone.
tamper_severitySeverity used for telemetry when verification fails.
max_save_bytesMaximum accepted plain save payload size.
on_tamperGuidance 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.