Performance Tips
Server-side patterns that keep saves fast, replication predictable, and memory stable.
Batch writes
Prefer one batch over many single writes when updating several fields at once.
- Use
for multiple top-level keys.SetValues() - Use
/BatchSetValues()for larger or numeric-heavy updates.BatchUpdateValues() - Call
when you need changes visible immediately (before a manualFlushBatch()SaveData, cutscene gate, etc.).
lua
-- Prefer this
PlayerState.SetValues(player, {
Coins = newCoins,
Level = newLevel,
Experience = newExp,
})
-- Over many separate Set calls in the same frameSee Batch operations.
Paths and cache
- Reuse path strings in hot loops instead of building
"Stats." .. nameevery time. - Call
only when debugging or after large schema changes—not every frame.ClearPathCache()
See Cache.
Return values
Write APIs return boolean. Treat false as a real outcome, not noise.
- Log or warn with context (player, path, reason if known).
- Do not assume the Replica updated when the call failed.
- Common causes: invalid player, leaderstats path blocked, ViewedUser read-only in Studio.
Leaderstats
- Change source data (e.g.
Coins,Stats.Score) and let PlayerState sync leaderstats. - Do not write directly to leaderstats instances or paths that map to them.
See Leaderboard & leaderstats.
Change listeners
- Listen on the narrowest path (
"Coins","Plot.Likes"), not".", unless debugging. - Disconnect
OnChangedwhen the player leaves or the feature is destroyed.
See Change listeners and Best practices — memory.
Lifecycle events
| Goal | Use |
|---|---|
| Leave-only world → profile writes | BeforeRelease |
Logic on and leave | BeforeSave |
| After session ended (read-only) | ProfileUnloaded |
Do not use Players.PlayerRemoving + GetPath for final saves—the profile may already be released.
See Events.