Quick answer: Every save includes a monotonic game-time timestamp, a content hash, and a progress metric. On sync, the higher progress metric wins when both saves contain real advancement. Write a .bak before every overwrite. Prompt only when the rule is genuinely ambiguous, and log every conflict decision for post-mortem.

A player finishes a six-hour session on their desktop. They fire up Steam Deck the next morning, and the last three hours are gone. The thread on your support forum already has forty-two replies, each angrier than the last. Cloud save conflicts are one of the most damaging bugs you can ship — trust, once broken, doesn’t come back. The defense is a save system that treats conflict as a first-class state, not an edge case.

What Cloud Sync Actually Does

Every platform’s cloud save is essentially the same primitive: on launch, the client checks for a newer version of the save on the server. If present, it downloads and replaces the local file. On exit (or on demand), it uploads the local file. Each platform has a few wrinkles:

Each platform’s default conflict-resolution policy is wrong for your game. They decide based on file timestamps, which are unreliable when players change timezones, travel across DST boundaries, or have a dead CMOS battery. Your own logic must sit on top.

Save Header Fields You Need

Every save file starts with a header containing the data your sync logic needs. Minimum:

struct SaveHeader {
    uint32 version;              // schema version
    uint64 gameTimeSeconds;      // monotonic in-game time played
    uint64 wallClockUnix;        // for human display only
    uint8  contentHash[32];      // SHA-256 of save body
    float  progressPercent;      // 0..1 of total completable content
    char   deviceId[32];         // which device wrote this
    uint32 saveCount;            // monotonic counter per user
};

Monotonic game time and save count are the reliable signals. Wall clock is useful only for showing the player “last played 2 days ago” and must never be the sole basis for conflict resolution.

The Resolution Algorithm

On launch, load both the cloud save header and the local save header. Run this decision tree:

  1. If hashes are equal, the saves are identical. Use either.
  2. If one save is missing, use the other.
  3. If the save counts differ by more than 1, you have a multi-device branch. Prompt the user.
  4. Otherwise, use the save with the higher gameTimeSeconds — the one that represents more play.

The “branch” case catches the scenario where both devices played from the same starting save, each accumulated progress, and neither knew about the other. That’s the only case where the player must decide, because either choice loses data.

The Prompt UI

When you do prompt, don’t just say “Cloud save or local save?” Show useful metadata:

The “keep both” option is crucial. Players who pick wrong should have a path to recovery. Persist the discarded save under a versioned name (savegame.conflict.2026-04-10.dat) so they can recover from the in-game menu.

Backups Are the Safety Net

Before every overwrite, write the previous file to savegame.bak. Keep the last three backups. A rotation scheme like .bak.1, .bak.2, .bak.3 is fine. Cloud sync goes wrong occasionally — driver crashes mid-upload, corruption, user intervention — and the backup is the difference between a restorable mistake and a Reddit thread.

Surface the backups in the UI. A “Restore previous save” option under settings is a trust-building feature. Don’t hide it; players who need it are already in a bad state.

Detecting Silent Failures

The worst bugs are syncs that never happen. Upload errors that the platform swallows. Files that aren’t marked dirty. Save directories that aren’t in the cloud sync path. Instrument every step:

In your dashboard, alert when the ratio of saves-written to uploads-succeeded drops below 0.95. That ratio catches cloud sync outages, bad quota behavior, and users who have disabled cloud sync without realizing it (then complain when the data’s gone).

Testing the Unhappy Paths

Write integration tests for each of these scenarios:

Each scenario must either resolve silently with a correct result or prompt with clear information. No scenario should silently lose data. That’s the bar.

“Cloud sync is distributed systems in miniature. Assume clocks lie, networks partition, and players do every combination of actions you didn’t plan for.”

Related Issues

For Steam-specific save integrations, see how to integrate Steamworks for indie games. For diagnosing save-related crashes, see how to reproduce a bug from a save file.

Players forgive bugs. They don’t forgive lost progress — so the save system is the last system you should ship without a full test matrix.