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
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