`Save` is a drawer of labelled values. A **player object** is a whole GameObject that VRChat
gives to every player and remembers for them.

It suits different things. An inventory, a tool somebody carries, a settings panel: anything with
structure, anything large, anything that changes often. It also has its own separate allowance,
so it does not compete with your saved values for room.

## Setting one up

Put the object in your scene and add a `VRCPlayerObject` component. Add `VRCEnablePersistence`
as well and its synced variables are remembered between visits.

VRChat then does something that catches people out: **your object becomes a template.** It is
switched off and left where it is, and every player gets their own copy of it. The copy is what
runs. Nothing in the scene can point at a copy, because the copies do not exist until people
arrive.

## Finding the right copy

So a script cannot hold a reference to the instance it wants. It holds the template — which is
the only thing you can drag into the inspector — and asks which copy belongs to whom:

```csharp
public Inventory template;   // drag the template in

public override void OnSaveReady()
{
    Inventory mine = PlayerObjects.Mine(template);
    mine.Add("hat");
}
```

For somebody else's:

```csharp
Inventory theirs = PlayerObjects.Of(player, template);
```

`PlayerObjects.Mine()` with no argument hands back every object belonging to the player at this
client, if you would rather look through them yourself.

## Ownership

Players own their own copies and that cannot be transferred. It is the rule that stops somebody
walking off with another person's things, and it means you never have to request ownership of a
player object before changing it.

## The same timing rule

Player objects load their saved values at the same moment saved values arrive, so
`OnSaveReady` is where to read them too. Reading in `Start` gives you whatever the object
looked like before its data landed.

## Room

Roughly 100 KB per player for saved values, and another 100 KB for player objects. Going over
does not stop the world — VRChat logs an error and stops saving, so it keeps running and quietly
stops remembering.

```csharp
public override void OnPersistenceUsageUpdated()
{
    int used = Storage.SaveUsed();
    int limit = Storage.SaveLimit;
}
```

**Read those numbers here and nowhere else.** They are recalculated occasionally rather than
maintained, and before the first calculation they read zero — which means "not counted yet",
not "no room". Overriding `OnPlayerDataStorageWarning` is how you hear about a player getting
close while there is still time to do something.
