## Save something

```csharp
Save.Set("coins", 25);
Save.Set("name", "ada");
Save.Set("spawn", transform.position);
```

One verb for every type. The value picks which one is used, so there is no `SetInt` to get wrong.
Writing always applies to the player at this client, because that is the only data a client is
allowed to change.

## Read it back

```csharp
int coins = Save.Get("coins", 100);
string name = Save.Get("name", "friend");
Vector3 spawn = Save.Get("spawn", Vector3.zero);
```

**The second argument is what you get when nothing is saved,** and it is required. That is the
whole point: a returning player gets their 25 coins, a new one gets 100, and the code says which
is which without a `TryGet` and an `if`.

It is also what you get when the key holds a different type than you asked for, so two systems
that both picked the name `"level"` degrade to a default rather than to nonsense.

## Read at the right moment

```csharp
public class Shop : SaveBehaviour
{
    private int coins;

    public override void OnSaveReady()
    {
        coins = Save.Get("coins", 100);
    }
}
```

**Do not read in `Start`.** Saved data arrives shortly after the player joins, and anything read
before then is the fallback. This is the mistake worth spending a paragraph on, because it does
not look like one: in the Editor there is often nothing saved anyway, so the world behaves
exactly as intended right up until a real player with real progress walks in.

Deriving from `SaveBehaviour` gives you `OnSaveReady`, which runs once, for the player at this
client, at the moment their data is readable.

## Somebody else's data

```csharp
public override void OnPlayerSaveReady(VRCPlayerApi player)
{
    int theirs = Save.Get(player, "best", 0);
}
```

You can read anybody's saved values, and their data becomes readable at a different moment from
your own — when they arrive, which may be an hour later. That is why it is a separate method:
a behaviour that reloaded its own state every time a stranger joined would throw away everything
done since.

## Telling a first visit from a returning one

```csharp
if (!Save.Has("visits"))
{
    // Hand out the welcome pack.
}
```

`Save.Has` answers whether a key has ever been written. Most of the time a fallback says the same
thing more directly; reach for this when the absence itself is the information.

## Two limits worth knowing

**Nothing can delete a key.** VRChat can overwrite one but not remove it, so the set of keys a
world uses is worth deciding once rather than letting it grow.

**Keys are shared across the whole world.** Two systems that both save `"level"` are using the
same key. Give them prefixes.
