Data Storage¶
How player data is persisted, reconciled, and safely released.
ProfileService¶
All player data goes through ProfileService. The store is created once in PlayerDataService:
The key for each player's profile is "Player_" .. player.UserId.
Profile Lifecycle¶
PlayerAdded
└── LoadProfileAsync("Player_<userId>")
├── success → Profiles[player] = profile
│ OnProfileLoaded(player, profile)
└── failure → player:Kick(...)
PlayerRemoving
└── profile:Release() -- saves and unlocks the data
profile:Reconcile() is called after load so that any new fields added to the Template automatically appear in existing profiles without overwriting existing values.
If the profile is released externally (e.g. the same player's data is loaded in another server), the ListenToRelease callback kicks the player and removes the cached entry.
Observers¶
PlayerDataService has a small observer registry. When update() is called with a key that has a registered observer, the observer fires synchronously. Currently the only observer is Money, which keeps the leaderstats IntValue in sync.
Adding a new observer is as simple as defining a function in the observers table with the matching key name.