Skip to main content

CE Persistence

CE.Persistence provides a model-based layer for persistent player data, validation, and size estimation.

Quick Start

using UdonSharp;
using UdonSharp.CE.Persistence;
using VRC.SDK3.Data;

[PlayerData("rpg_save")]
public class PlayerSaveData
{
[PersistKey("xp")] public int experience;
[PersistKey("lvl"), Range(1, 100)] public int level = 1;
}

public class SaveManager : UdonSharpBehaviour
{
private PlayerSaveData _data = new PlayerSaveData();

void Start()
{
// No manual registration required.
}

public void SaveLocal()
{
CEPersistence.Save(_data);
}
}

Auto Registration

CE.Persistence auto-registers [PlayerData] models during Udon compilation. You only need the attributes.

Supported field types:

  • Primitives (bool, int, float, etc.)
  • string
  • DataList / DataDictionary
  • Arrays of the above

If you need custom conversion (complex types, nested models, etc.), register a converter manually with CEPersistence.Register.

API Reference

Model Attributes

AttributeDescription
PlayerDataMarks a class as persistent data with a storage key.
PersistKeyDefines the key name used for a field.
PersistIgnoreExcludes a field from persistence.
RangeNumeric range validation.
MaxLengthString or array length validation.
RequiredRequires non-empty values.

CEPersistence

MemberDescription
Register<T>(toData, fromData, key, version, validate)Register a data model and converters (optional; only for custom converters).
IsRegistered<T>()Check if a model is registered.
Save<T>(T model)Save a registered model to the local player's VRChat PlayerData as JSON.
Restore<T>(out T model)Restore a registered model from the local player's VRChat PlayerData JSON.
ToData<T>(T model) / FromData<T>(DataDictionary, out T)Convert to and from DataDictionary.
ToJson<T>(T model, bool beautify) / FromJson<T>(string, out T)Convert to and from JSON.
Validate<T>(T model, out List<ValidationError>)Validate a model against constraints.
EstimateSize<T>(T model)Estimate serialized size in bytes.
GetQuotaLimit()The PlayerData storage limit, from Networking.GetPlayerDataStorageLimit().
GetUsedQuota()Bytes currently used, from Networking.GetPlayerDataStorageUsage(localPlayer).
GetRemainingQuota()Limit minus current usage. This is a real measurement — the earlier note claiming VRChat does not expose usage was wrong; both SDK calls exist and are now used.

PersistenceLifecycle

MemberDescription
RegisterCallback(UdonSharpBehaviour)Register for save/restore lifecycle events.
UnregisterCallback(UdonSharpBehaviour)Remove a callback.
GetPendingRestoreResult() / GetPendingSaveResult()Access last result inside callbacks.
GetPendingModelKey()Access model key inside callbacks.
GetPendingCorruptionInfo()Access corruption details inside callbacks.

Callbacks are invoked by name. Implement _CE_OnDataRestored, _CE_OnDataSaved, and _CE_OnDataCorrupted on your behaviour and read the pending data via the accessor methods above.

PlayerObjectHelper

MemberDescription
Initialize()Initialize slot tracking.
AssignPlayerSlot(VRCPlayerApi)Assign a player to a slot.
ReleasePlayerSlot(VRCPlayerApi)Release a slot.
GetPlayerSlot(VRCPlayerApi) / GetLocalPlayerSlot()Retrieve slot IDs.
GetPlayerInSlot(int)Retrieve player by slot.

Current Limitations

  • Save writes model JSON with PlayerData.SetString; Restore reads it with PlayerData.TryGetString.
  • Restore returns NotReady until local PlayerData is available and NoData when the key does not exist.
  • Auto-registration runs during Udon compilation and supports primitives, strings, DataList/DataDictionary, and arrays of those types.
  • Unsupported field types require manual converters via Register<T>.
  • JSON numbers deserialize as doubles; cast carefully in custom converters.
  • Exact remaining quota cannot be queried, so GetRemainingQuota() returns the full configured quota.

Common Pitfalls

Bad

// Using unsupported field types without a custom converter.
CEPersistence.Save(_data);

Good

// Keep persistence fields to supported types, or register a custom converter.