Overview
EasyStats is a compact server-side Roblox module for creating leaderstats-style value instances and persisting them to DataStore. It provides a simple API to create stats (IntValue, NumberValue, StringValue, BoolValue), set defaults, read/write values, export data for storage, and automatically saves players on leave with periodic autosave.
1 — Setup
Place the module on the server and require
it from a Server script.
File layout
ServerScriptService/
├─ EasyStats.lua -- (module file)
└─ StatsLoader.server.lua -- (small script to require and use it)
Basic loader example
local Leaderstats = require(game.ServerScriptService.EasyStats)
game.Players.PlayerAdded:Connect(function(player)
-- Create or get stats and show them on the default leaderboard
local stats = Leaderstats.GetStats(player, "leaderstats")
-- Ensure default stats exist
stats:setDefault("Coins", "IntValue", 0, {IsPrimary = true})
end)
2 — How it works (quick)
- GetStats loads data for the player (DataStore), creates a Folder on the Player (default name
leaderstats
), and creates value Instances for each stat. - Each player's runtime object is tracked in an internal
session
registry. - On player leave, the module exports the stats to a plain table and saves with DataStore.
- An autosave loop periodically saves every active session (default: every 120s).
3 — Basic usage examples
Require the module
local Leaderstats = require(game.ServerScriptService.EasyStats)
Create / get stats
game.Players.PlayerAdded:Connect(function(player)
local stats = Leaderstats.GetStats(player, "leaderstats")
stats:setDefault("Coins", "IntValue", 0, {IsPrimary = true})
end)
Create a stat manually
local kills = stats:create("Kills", "IntValue", 0, {Priority = 10})
-- 'kills' is the actual Instance returned; you can connect events to it.
Update and read stat values
stats:update("Coins", 100)
local coinCount = stats:call("Coins") -- returns 100
4 — Advanced usage & options
When creating or setting defaults, pass an options
table with these keys:
IsPrimary = true
— adds a BoolValue childIsPrimary
on the stat instance.Priority = <number>
— adds a NumberValue childPriority
, helpful for ordering.
stats:create("Level", "IntValue", 1, {IsPrimary = true})
stats:create("Score", "IntValue", 0, {Priority = 100})
5 — Data persistence & autosave behavior
The module uses a DataStore and stores each player's exported data under tostring(player.UserId)
.
Loading
GetStats
attempts DataStore:GetAsync
and recreates value instances from saved data when present.
Saving
On player leave, ExportData()
is saved with DataStore:SetAsync
. An autosave loop runs every 120s for active sessions.
Exported data format
{
Coins = {
Type = "IntValue",
Value = 100,
Options = { IsPrimary = true, Priority = 10 }
},
}
6 — API reference
Assume: local Leaderstats = require(game.ServerScriptService.EasyStats)
Leaderstats.GetStats(key, leaderName)
Get/create a player's stats folder; leaderName
controls the Folder name (use leaderstats
for Roblox leaderboard visibility).
:setDefault(name, className, defaultValue, options)
:create(name, className, defaultValue, options)
:update(name, newValue)
:call(name)
:ExportData()
7 — Tweaks & customizations
Change autosave interval
task.spawn(function()
while true do
task.wait(120) -- change this number
for _, obj in pairs(session) do
savePlayer(obj._player)
end
end
end)
Use UpdateAsync for safer writes
local ok, err = pcall(function()
DataStore:UpdateAsync(tostring(player.UserId), function(oldData)
-- merge oldData with 'data' and return the merged table
return data
end)
end)
8 — Troubleshooting & best practices
- Stat not found: Ensure
setDefault
orcreate
ran beforeupdate
. - DataStore failures: Consider retry/backoff; log warnings.
- Concurrency: Prefer
UpdateAsync
for multi-server games. - Data size: Keep saved data small.
9 — Example complete server script
-- StatsLoader.server.lua
local Leaderstats = require(game.ServerScriptService.EasyStats)
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local stats = Leaderstats.GetStats(player, "leaderstats")
stats:setDefault("Coins", "IntValue", 0, {IsPrimary = true})
stats:setDefault("Kills", "IntValue", 0)
stats:setDefault("Level", "IntValue", 1, {Priority = 50})
local current = stats:call("Coins") or 0
stats:update("Coins", current + 10)
end)
10 — FAQ / Notes
Q: Are stats persisted across servers?
A: Yes — saved to a DataStore keyed by UserId.
Q: Will stats appear on Roblox's leaderboard?
A: If the folder name is leaderstats
, yes. Use another name to hide them.
Q: Can I use OrderedDataStore for ranked boards?
A: Extend the module to also write selected values into an OrderedDataStore.
Final notes & recommended improvements
- Switch to
UpdateAsync
if you expect concurrent writes. - Add logging hooks for save/load failures.
- Batch frequent small writes to reduce DataStore usage.