§ How to · with Forge AI

How to make a Roblox leaderboard (with AI)

A Roblox top-scores leaderboard needs three wired-up parts: an OrderedDataStore for cross-server ranking, a 60-second refresh loop, and a SurfaceGui that displays the top N. Forge AI generates all three in 31 seconds.

4 files · 110 lines · 31 seconds · 1 credit. Cross-server, throttle-safe.

OrderedDataStore

Sorted by numeric score across all servers. Top 10 read in a single :GetSortedAsync call.

60-second refresh

Server polls top 10 every 60 seconds and pushes to clients via RemoteEvent. Roblox throttles OrderedDataStore reads — polling is bounded by design.

SurfaceGui display

Top 10 names + scores displayed on a SurfaceGui Part. Configurable for ScreenGui too.

Score push

On qualifying event (level up, death, win), server writes a new score to the OrderedDataStore atomically.

Cross-server ranking

Players see the same top 10 regardless of which server they joined. OrderedDataStore handles the sort.

Username resolution

Stored player UserIds are resolved to display names via Players:GetNameFromUserIdAsync, cached for 5 minutes.

Files Forge AI ships for this prompt

4 files · 110 lines · 31 seconds · 1 credit

ServerScriptService/LeaderboardServer.lua

OrderedDataStore reader + 60s loop + push to clients

64 lines

ServerScriptService/ScoreWriter.lua

Public API: ScoreWriter:Submit(player, score)

24 lines

ReplicatedStorage/Remotes/LeaderboardUpdate

Server → client top 10 push

instance

StarterGui/LeaderboardUI.lua

SurfaceGui rows + name resolution

22 lines

Sample output: ServerScriptService/LeaderboardServer.lua

--!strict
-- ServerScriptService/LeaderboardServer.lua  (Forge AI · excerpt)
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LeaderboardUpdate = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("LeaderboardUpdate")

local store = DataStoreService:GetOrderedDataStore("TopScores_v1")

local function fetchTop10(): { { userId: number, score: number } }
    local ok, pages = pcall(function()
        return store:GetSortedAsync(false, 10)
    end)
    if not ok then return {} end
    local entries: { { userId: number, score: number } } = {}
    for _, e in ipairs(pages:GetCurrentPage()) do
        table.insert(entries, { userId = tonumber(e.key) or 0, score = e.value })
    end
    return entries
end

task.spawn(function()
    while true do
        local top = fetchTop10()
        for _, player in ipairs(game.Players:GetPlayers()) do
            LeaderboardUpdate:FireClient(player, top)
        end
        task.wait(60)
    end
end)

Building a Roblox leaderboard

Cross-server leaderboards require OrderedDataStore — regular DataStore is not sorted, so retrieving top N is O(all keys). The Forge AI leaderboard uses OrderedDataStore with GetSortedAsync to read top 10 in one network call.

The 60-second refresh interval is set by Roblox throttling. OrderedDataStore reads are rate-limited per server — too-frequent polling drops requests. 60 seconds is the well-known sweet spot: low enough to feel live, slow enough to never throttle. Forge AI's default polling loop respects this; tune up to faster only if you have a small player base.

Score submission is atomic. ScoreWriter:Submit(player, score) writes the new score to the OrderedDataStore — Roblox handles the sort. The submit is fire-and-forget; the server poll picks up new entries on the next 60-second tick.

Username resolution is the often-missed part. Stored entries are UserIds (numeric, the only stable identifier). Display names need Players:GetNameFromUserIdAsync — but that has a per-call cost. Forge AI caches resolved names for 5 minutes per UserId, so a stable top 10 only resolves names once and reuses them across refreshes.

The display layer is decoupled. Same data flows into a SurfaceGui leaderboard board, a ScreenGui side panel, or a podium with 3D models for top 3. Default is a SurfaceGui Part — the highest-impact placement for most game lobbies.

See more on the Luau generator, the game builder, or browse the full blog.

Frequently asked

Why OrderedDataStore and not regular DataStore?+

OrderedDataStore is sorted by numeric value and supports GetSortedAsync — top N retrieved in one call. Regular DataStore would require iterating every key, which is impossible at scale.

How often does the leaderboard refresh?+

Default 60 seconds. Roblox throttles OrderedDataStore reads aggressively, so 60 seconds is the sweet spot for cross-server display.

Can I show top 100 instead of top 10?+

Yes. GetSortedAsync supports up to 100 per page. For 1000+, paginate via :AdvanceToNextPageAsync. Network cost grows with size — top 10 is the standard.

What about per-game-mode or per-week leaderboards?+

Use a separate OrderedDataStore per scope (TopScores_FFA, TopScores_Capture). For weekly resets, key by ISO week (TopScores_2026W14) and the new week starts empty.

Is the SurfaceGui the only display option?+

No — same data flows into a ScreenGui list, a podium with 3D models for top 3, or a full screen on death. The Forge AI default is a SurfaceGui Part because that is the most common; swap UI in StarterGui/LeaderboardUI.lua.

Related Forge AI prompts