PlayerState: Player Data That Just Works
A production-ready wrapper for ProfileStore + Replica. Stop reimplementing the same data management boilerplate and start building your game.
📦 Get the Model · 📖 Documentation · 📚 GitHub
đź“‹ Changelog & Version History
V1.18.6 (Latest - March 21, Saturday, 2026)
- Server global leaderboards: OrderedDataStore writes now go through a paced queue with deduplication, reducing burst
SetAsynctraffic and lowering the chance of throttle / queue drops. Scores still flush on tracked stat changes, player join, player leave, and server shutdown. Added an optional slower full-sweep setting for periodic reconciliation.
Performance:
- Server: Dict updates replicate per key when the dict already exists (benchmark “Dict Operations”: ~91% less time).
- Server: Increment / BatchUpdateValues do less redundant work (~12% / ~26% faster in benchmarks).
- Server: Tracked leaderboard stats use O(1) lookup (~6–10% faster on leaderboard hot paths in benchmarks).
- Client: GetFromDict avoids heavy path work when
AutoCloneTablesis on (~40% faster in benchmarks). - Client: OnChanged invalidates only paths touched by a change (global listener still clears all); setup bench ~39% faster.
- Client: Faster readiness wake-up (signal + short poll); full client benchmark suite ~14% less wall time (~16% higher throughput).
- Suites (same op counts): server wall time ~28% faster (~39% higher overall ops/sec).
V1.18.5 (March 12, Wednesday, 2026)
- Added the long requested
OnChangedon the server with very minimal performance changes.
V1.18 (March 10, Tuesday, 2026)
- Added Legacy Migration so you can move existing Datastore data into PlayerState. Supports strategies like Auto, Overwrite, KeepNew, Manual, and ContinuousOverwrite.
- Added
PlayerState.MigrationResultto report when legacy migration finishes. It provides applied status, legacy data found, strategy used, and other metadata. - Added support for session-only (non-persistent) data via runtime roots (e.g.
session). Values likesession.isChoppingorsession.isSprintinglive in memory only and are not saved to the Datastore. - Added
RuntimeNonPersistentRootsin the config for paths that must not be persisted. These are stripped before save and restored from defaults after load. - Updated migration config normalization for both simple and advanced shapes, keeping backward compatibility with minimal configs.
V1.17.1 (Monday, March 9, 2026)
- Fixed a bug where leaderboard data (top 100 and ranks) were being saved to player data, instead of only being stored in memory. The script now deletes any data to do with the leaderboards on join to free up datastore space it should not have. This does not effect leaderboard functionality at all, and all current leaderboard data is safe and will not be deleted
V1.17 (Wednesday, February, 2026)
- Added a
ViewedUserconfiguration in the config. Allows you to load in with the data of another user, with optional read only.
V1.16.5 (Sunday, February 15, 2026)
- Added
GetLeaderboard(statName, limit)to the client
V1.16 (Saturday, February 7, 2026)
- Fixed up the type checking errors that were in the
PlayerStateServer - Fixed an issue with decimal based numbers not being able to push to global leaderboards.
Decimal based numbers now round for global leaderboards
V1.15 (Sunday, February 1, 2026)
- Fixed a couple issues with the Changed event listener not firing for certain calls that were made via batch function calls
- Modified Replica to handle custom batching correctly and efficiently
V1.14 (Thursday, January 29, 2026 )
- Internal refactoring. Makes it a lot easier for me to add to and modify PlayerState.
- Added BatchUpdateValues for batch incrementing/decrementing (Thanks to xtheodxre on discord for this idea!)
- Fixed a cyclic table issue that could occur (Thanks to lopjoss on discord for finding this, and fixing it!)
V1.13 (Tuesday, January 2, 2026)
Thank you to risebit on discord for the leaderboard idea and some of the code!- Added a new client function
GetLeaderboardInfo(statName)to easily get your current leaderboard rank and score for a stat. - Fixed a small cache cleanup issue on the client.
- Improved leaderboard updates so players get up-to-date info sooner (no “wait for the next interval” feeling).
- Added syncing of each player’s current leaderboard info to the client, so UIs can show rank/score more reliably.
- Made scores feel instant: when a tracked stat changes, the displayed leaderboard score updates right away; the rank updates on the normal refresh cycle.
- Kept saves clean: leaderboard display data is treated as temporary and won’t be saved into player data.
V1.12 (Friday, December 26, 2025)
- Added
Clone()utility for deep cloning tables, with a new config option to enable automatic deep cloning globally. - Improved offline data manipulation logic to more reliably detect whether a player is already in an active server.
- Fixed several minor bugs and edge cases related to data consistency and offline writes.
V1.11 (Wednesday, July 30, 2025)
Made it so if a number is either a float (has decimals) or greater than the int value limit, it is created as a NumberValue instead of an IntValue in leaderstats.V1.1 (Wednesday, July 30, 2025)
This update requires an addition to the PlayerStateConfig. You MUST have Server.Leaderboard in the config.*
- Added
GetLeaderboard(statName, topCount) - Added
GetPlayerRank(player, statName) - Added
UpdateLeaderboard(player, statName, score)
Leaderboard Configuration
- Complete leaderboard setup guide with DataStore configuration
- TrackedStats system - Configure which data fields appear on leaderboards
- SyncInterval and UpdateOnPlayerLeave settings for performance tuning
- DataStore limits awareness - Built-in respect for Roblox’s 60 requests/minute limit
V1.0.5 (Wednesday, July 30, 2025)
- Added
WipePlayerDatamethod - Added
WipeOfflinePlayerDatamethod
V1.0.4 (Monday, July 25, 2025)
- Added
BeforeSaveevent - Added
ProfileLoadedevent - Added
ProfileUnloadedevent
V1.0.3 (Monday, July 14, 2025)
- Added
GetOfflineData(userId: number)method - Added
SetOfflineData(userId: number, path: string, value: any)method
V1.0.2 (Wednesday, July 9, 2025)
- Fixed
UpdateArrayItemsetting as a string index instead of a number index, which caused it to not function how it should have. - Fixed
OnChangednot firing whenAddToArraywas called - Added
PlayerState.Increment()function for safe numeric value incrementation - Added
PlayerState.Decrement()function for safe numeric value decrementation - Updated documentation server API with dedicated numeric operation functions
V1.0.1 (Saturday, June 21, 2025)
- Fixed edge case where
PlayerState.Get()could returnnilduring initial data load on client and server - Fixed
SetInDictnot functioning how it should have been. Same withRemoveFromDict. - Fixed dictionaries being reset to blank on load
- Added
GetFromDictto Server and Client
What it does
PlayerState combines ProfileStore (data persistence) and Replica (real-time client synchronization) behind a clean, type-safe API.
It handles all the difficult parts of player data automatically:
- Automatic session management
- Data validation and reconciliation
- Real-time client syncing
- Change detection events
- Batch operations for performance
- Leaderstats and global leaderboards
- Offline player data access
You focus on your game logic, PlayerState handles the infrastructure.
📖 The documentation includes examples for every function, plus advanced patterns for currencies, inventories, progression, leaderboards, and more. Browse the docs →
-- Server
PlayerState.Set(player, "Coins", 1000)
PlayerState.SetPath(player, "Inventory.Weapons", {"Sword", "Bow"})
-- Client
local coins = PlayerState.Get("Coins")
PlayerState.OnChanged("Coins", function(newValue, oldValue, changeInfo)
updateCoinsUI(newValue)
end)
That's the entire workflow. ProfileStore handles persistence, Replica handles networking, and PlayerState handles the integration.
Getting Started
⚠️ Required: You must call
PlayerState.Init(player)on the server when each player joins.
-- ServerScriptService
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateServer)
game.Players.PlayerAdded:Connect(function(player)
local success = PlayerState.Init(player)
if success then
PlayerState.Set(player, "Coins", 0)
else
warn("Failed to load data for", player.Name)
end
end)
-- Client
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
local coins = PlayerState.Get("Coins")
PlayerState.OnChanged("Coins", function(newValue)
updateCoinsUI(newValue)
end)
Full setup guide: playerstate.netlify.app/installation
Key Features
Global Leaderboards
Track stats automatically and query leaderboard ranks without writing OrderedDataStore code.
local leaderboard = PlayerState.GetLeaderboard("Coins", 10)
for _, entry in leaderboard do
print(entry.rank, entry.userId, entry.score)
end
Leaderboards update automatically when tracked stats change.
Offline Player Data
Modify data for players even when they are not in the game.
PlayerState.SetOfflineData(userId, "Coins", 5000)
This enables moderation tools, admin panels, refunds, and automated systems.
Batch Operations
Update many values efficiently in a single operation.
PlayerState.BatchSetValues(player, {
{path = "Coins", value = 500},
{path = "XP", value = 200},
{path = "Level", value = 5}
})
This reduces replication overhead and improves server performance.
Runtime (Non-Persistent) Data
Define session-only values that sync to the client but never save to DataStore.
session.isChopping
session.isInCombat
session.currentQuest
Perfect for gameplay state that should reset every session.
Legacy Data Migration
Automatically migrate existing DataStore systems into PlayerState.
Supports multiple migration strategies including overwrite, merge, and continuous migration.
Performance
Tested under heavy load conditions:
| Server throughput | 600,000 ops/sec |
| Batch processing | 300,000+ ops/sec |
| Client data access | 3M ops/sec |
| Client response time | Sub-microsecond |
| Concurrent players | 1,000+ |
PlayerState's caching and batching layer means it often outperforms raw ProfileStore/Replica implementations.
What you can build
- Multi-currency systems
- Inventory systems with nested metadata
- Progression systems (XP, levels, skill trees)
- Persistent worlds and base building
- User settings and preferences
- Global leaderboards
- Admin tools with offline data access