Before you install
Install either Astro Leaderboard Free or Astro Leaderboard in one experience. Do not install both editions. They contain separate runtimes, stat observers, refresh loops and test commands that would compete with each other.
The package maintains an OrderedDataStore snapshot for global ranking. It does not replace your game’s main player data system. Continue saving and loading coins, wins or other values through your existing data service.
Astro Leaderboard Free displays placement, Roblox username and the configured amount. It is suitable for coins, wins, points, levels, donations, fastest times and other numeric values.
Installation
Place the supplied objects at these exact locations:
ServerScriptService
└── FreeLeaderboard
├── Config
├── LeaderboardService
├── Main
└── NumberFormatter
Workspace
└── Leaderboard
└── FreeLeaderboard
The required row path inside the board model is:
Cube.Part.SurfaceGui.Placements.ScrollingFrame.Row_1.Frame
Every row Frame must contain Placement, Name and Amount TextLabels. Keep Row_1, Row_2, Row_3 and Row_4. The first three rows preserve their individual Studio designs. Row_4 is cloned for every later placement.
You may change sizes, colours, fonts, padding and layout order in Studio. Do not rename the required objects unless you also update the service code.
First configuration
- Open
ServerScriptService.FreeLeaderboard.Config. - Give
DataStore.Namea stable, unique name for this stat. - Set
Stat.Nameto the exact numeric stat name used by your game. - Confirm the configured
BoardPathmatches the model in Workspace. - Replace the supplied
TestCommand.AllowedUserIdsentry with your own trusted tester IDs. - Publish the experience before testing live OrderedDataStore data.
Keep DataStore.ReadInStudio and DataStore.WriteInStudio set to false unless Studio access to production data is deliberate.
Connect a stat
The service checks a matching ValueBase inside player.leaderstats first. If it is not present and Stat.ReadPlayerAttribute is enabled, the matching Player attribute is used as the fallback. Names are case-sensitive.
For a board whose ID and stat name are both Coins, either source works:
player.leaderstats.Coins.Value = 500
player:SetAttribute("Coins", 500)
IntValue and NumberValue are supported. Set Stat.PrintUpdates = true on a board while integrating if you want accepted changes printed to Output.
The first value discovered for a player is not immediately written. This avoids replacing a saved score with a temporary zero while another system is still loading player data. Later changes are buffered using DataStore.WriteFlushDelay.
Server API
Server scripts may update a configured board through the included service. Pass the board Id, not its visible title.
local Leaderboard = require(
game.ServerScriptService.FreeLeaderboard.LeaderboardService
)
Leaderboard:SetPlayerStat(player, "Coins", 500)
Leaderboard:AddPlayerStat(player, "Coins", 25)
local coins = Leaderboard:GetPlayerStat(player, "Coins")
print("Current coins:", coins)
SetPlayerStat changes the matching leaderstat when it exists. Otherwise, it changes or creates the Player attribute when attribute reading is enabled. GetPlayerStat returns nil when no numeric source exists. These methods are server-only and must not be called from a LocalScript.
Useful refresh helpers are also available:
local ids = Leaderboard:GetBoardIds()
Leaderboard:RefreshNow("Coins")
Leaderboard:RefreshNow()
RefreshNow reads the current OrderedDataStore. It does not bypass a pending buffered write, and it does not reset the normal refresh timer.
Add more boards
Copy the short Board({...}) entry inside Config.Boards, duplicate the board model in Studio and change these values:
| Setting | Purpose |
|---|---|
Id |
Unique internal identifier used by the server API |
BoardPath |
Path below Workspace to this board model |
Title |
Text displayed on the board |
Stat.Name |
Exact leaderstat or Player attribute name |
DataStore.Name |
Stable OrderedDataStore name for this ranking |
Each board inherits BOARD_DEFAULTS. Add only the settings that differ from those defaults.
Board({
Id = "Wins",
BoardPath = { "Leaderboard", "TopWins" },
Title = "Top Wins",
Stat = { Name = "Wins" },
DataStore = { Name = "Leaderboard_Wins_v1" },
Rows = { MaximumEntries = 50 },
})
Use a different score DataStore name for each different stat. Two visual boards may share a DataStore only when they intentionally display the same stat and ranking.
Practical three-board pattern
A live game integration can define separate boards for best win streak, total wins and coins. Use one Board entry and one OrderedDataStore name for each stat. Highest streak and total wins should normally use descending order. Coins can use the same order with compact formatting enabled. Monotonic values such as lifetime wins and best streak should also be protected by your main data system so an older value cannot overwrite a newer maximum.
Configuration reference
| Setting | Recommended use |
|---|---|
DataStore.Ascending |
false for highest first, true for lowest first such as fastest times |
DataStore.WriteFlushDelay |
Combines rapid stat changes before writing. Default is 15 seconds |
Rows.MaximumEntries |
Number of placements from 1 to 100 |
Rows.TemplateRank |
Keep at 4 for the supplied row design |
Rows.ShowEmptyRows |
Show or hide placeholder rows without entries |
Rows.BatchSize and BatchDelay |
Spread row rendering over small batches |
Refresh.InitialDelay |
Delay before the first server refresh. Default is 5 seconds |
Refresh.Interval |
Time between ranking reads. Default is 300 seconds |
Refresh.RetryCount and RetryDelay |
Recovery from temporary Roblox service failures |
Number formatting
Set Formatting.Abbreviate = false to show complete values with a thousands separator. When abbreviation is enabled, Formatting.Suffixes may use any sequence:
Suffixes = { "K", "M", "B", "T" }
Suffixes = { "A", "B", "C", "D" }
Suffixes = { "AA", "AB", "AC", "AD" }
Formatting.Base = 1000 makes each suffix the next power of 1,000. DecimalPlaces and TrimTrailingZeros control compact decimals.
Studio testing
Start a Studio playtest and run:
/lb load 100
This fills the display with temporary values and real public Roblox usernames. It does not write fake scores to OrderedDataStore. Run /lb clear to return to real data.
The command only accepts users listed in TestCommand.AllowedUserIds. Replace the included ID with your own trusted user ID before release. Disable the command entirely when it is not needed.
Troubleshooting
The board is empty in Studio
ReadInStudio is disabled by default to protect live data. Use /lb load 100 for safe temporary entries. Only enable Studio API access and real reads when you understand the production data impact.
A stat does not update
Check the exact capitalisation of Stat.Name, confirm the leaderstat is numeric, and temporarily enable Stat.PrintUpdates. Remember that writes wait for WriteFlushDelay and the board reads again on Refresh.Interval.
A row or title is missing
Read the first setup warning in Output. The service validates board paths, templates, labels and configuration once at startup without flooding Output. Restore the required object name or hierarchy, then restart the playtest.
Upgrading to Astro Leaderboard
Remove the complete FreeLeaderboard script folder and all Free board models first. Then install the paid package and its paid board models. Never leave both runtimes in the experience.
