# 4. Write Locks Conflicts and Offline
Part of [[Cloud Save and Unity Services]]. Prev: [[3. Player Data Save and Load]]. Next: [[5. Worked Example High Scores Mirror]].
A write lock is how Cloud Save stops you from blindly overwriting a value that **someone else** (another device, a Cloud Code script, a second client) already changed. Official concept page: [Write locks](https://docs.unity.com/en-us/cloud-save/concepts/write-locks). Official request type: [`SaveItem`](https://docs.unity3d.com/Packages/
[email protected]/api/Unity.Services.CloudSave.Models.SaveItem.html) in 3.4.2.
This is **in addition to** your game-level comparison of `savedUtcTicks`. Locks protect the **service item**. Ticks protect the **player's progress story**. You want both.
## 4.1 How a lock works
Official:
- Load (and a successful save) return a `writeLock` string. Store it next to the item (in memory is enough; you can also persist it if you want, but the next load will give you a fresh one).
- When you save, you may include that lock. If it does **not** match what the service has, the service returns a conflict error (`CloudSaveConflictException` on the `SaveItem` overload).
- If you are **creating** a new item, **omit** the lock (official: omitting it on create avoids unnecessary errors).
- If you want to **force** an overwrite, pass a **null** write lock on `SaveItem`. Official: a null lock **skips validation** and overwrites any existing value. `SaveAsync(IDictionary<string, object>)` from part 3 also overwrites without validation - same idea, no lock parameter.
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.CloudSave;
using Unity.Services.CloudSave.Models;
/// <summary>
/// Locked write of a JSON string. NOT compiled in Unity.
/// </summary>
public static class CloudSaveLocked
{
public static async Task<string> SaveJsonAsync(string key, string json, string writeLock)
{
var data = new Dictionary<string, SaveItem>
{
{ key, new SaveItem(json, writeLock) }
};
var locks = await CloudSaveService.Instance.Data.Player.SaveAsync(data);
return locks.TryGetValue(key, out var updated) ? updated : null;
}
}
```
Official constructor: `SaveItem(object value, string writeLock)`. After a success, replace your stored lock with the returned one.
## 4.2 Conflict resolution (game policy)
Official options when a write-lock error comes back:
1. If you are **certain** the value you have is the one to keep, bypass the lock (null lock / object dictionary overload) and overwrite.
2. Otherwise **load again**, then **manually** resolve.
"Manually" in this course means **ask the player**, using the slot's `savedUtcTicks` (and any other human-readable summary: play time, location, score). Suggested prompts:
- "This machine has a later save than the cloud. Upload this one?"
- "The cloud has a later save than this machine. Download and replace the local file?"
- "They differ and we cannot tell. Keep local, keep cloud, or cancel."
Never pick "the cloud always wins" in a class project. A student who played offline on a bus will lose a session.
Implementation sketch (not a full UI):
1. Local save already succeeded (or you are at startup).
2. Load cloud `slot_0` (if any) and its `WriteLock`.
3. If missing: `SaveItem(json, null)` or the object-dictionary `SaveAsync` to **create**.
4. If present: compare ticks.
- Same ticks and same JSON: nothing to do.
- Local newer, player confirms upload: `SaveItem(localJson, cloudWriteLock)`. If you get `CloudSaveConflictException`, load again and repeat - someone else wrote in between.
- Cloud newer, player confirms download: write the cloud JSON to the **local** atomic save path (the High Scores save system's existing write, so `.bak` still works). Do not only keep it in memory.
- Equal ticks but different JSON: treat as a conflict and ask. Do not invent a merge of two `JsonUtility` blobs unless you have tests for it (you will not, in this course).
## 4.3 Offline is normal
Wrap every Cloud Save call:
```csharp
try
{
await CloudSaveLocked.SaveJsonAsync(key, json, writeLock);
}
catch (CloudSaveRateLimitedException e)
{
Debug.LogWarning(