Player objects
A whole object that belongs to each player and is saved with them, and how a script finds the right copy.
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:
public Inventory template; // drag the template in
public override void OnSaveReady()
{
Inventory mine = PlayerObjects.Mine(template);
mine.Add("hat");
}
For somebody else's:
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.
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.