Skip to content

Leaderstats

Maps profile or session fields onto the Roblox player-list leaderstats folder and keeps the values current while players are in the server.

Realm
Server
Require
ServerScriptService.ChloeKernelServer.Leaderstats
Since
v0.1.0
Source
Leaderstats.luau

Leaderstats wires the leaderstats folder that the Roblox player list reads: the per-server numbers beside each name. Declare which data field each stat shows, and the module creates the value objects when a session starts, keeps them current, and removes them when it ends. It never writes data anywhere. For a persisted, cross-server ranking, use Leaderboards; the two are independent and often fed from the same fields.

The player list works by convention: a Folder named leaderstats under the player, holding IntValue, NumberValue or StringValue instances. Leaderstats owns that folder against the kernel’s session lifecycle:

  1. On every session start (kernel:onSession), it creates the leaderstats folder, or asks your GetContainer for one, and one value object per declared stat, seeded from the data.
  2. It refreshes a session the moment its profile loads (Kernel.ProfileLoaded), so persisted values appear as soon as the DataDriver profile arrives instead of on the next sweep.
  3. A sweep copies current data into the value objects every UpdateSeconds (default 1 s) at Background priority. Plain data tables have no change events, which is why it sweeps. The sweep job starts with the first session and is cancelled when the last session ends, so an empty server runs nothing.
  4. On session end (session:bind), it destroys the folder it created, or only its own value objects when the folder came from GetContainer.

Each stat reads its field from session.Profile.Data (the persisted record) or session.Data (per-visit). By default it reads the profile when one exists and session data otherwise, so persisted stats show persisted values with no extra configuration.

A nil field leaves the value object as it is. An IntValue shows 0 until the field exists; seed the field in your profile Defaults if 0 on first join is wrong.

Each field is assigned on its own, so one bad field cannot stop the others. IntValue and NumberValue do not throw on a value they cannot convert: the engine prints a warning with a stack trace and keeps the old value. Leaderstats therefore checks number stats first, and a value that tonumber cannot convert (a table, a boolean) is skipped with one warning per stat: [Leaderstats] could not show Coins = true: expected a number, got boolean. StringValue assignments run inside a pcall and warn the same way. The warning fires once per stat name for the life of the handle, not once per player or sweep.

src/Server/Game/Services/StatsService.luau
--!strict
-- StatsService: coins, best stage and title in the player list.
local ServerScriptService = game:GetService("ServerScriptService")
local Leaderstats = require(ServerScriptService.ChloeKernelServer.Leaderstats)
local StatsService = {
Name = "StatsService",
Dependencies = { "DataService" },
}
function StatsService.init(self: any, kernel: any)
self.Stats = Leaderstats.attach(kernel, {
Coins = { Field = "Coins" }, -- session.Profile.Data.Coins
Stage = { Field = "BestStage" },
Title = { Field = "Title", Kind = "StringValue", From = "Session" }, -- session.Data.Title
})
end
function StatsService.stop(self: any)
if self.Stats then
self.Stats.detach()
end
end
return StatsService

session.Profile.Data.Coins += 100 shows in the player list within one sweep. The keys of the stats table are the names the player list shows.

Leaderstats.attach(kernel, stats, options?). stats maps each display name to:

Field Default Description
Field required The data field to read.
From the profile if one exists, else the session "Profile" reads session.Profile.Data; "Session" reads session.Data.
Kind "IntValue" "IntValue", "NumberValue" or "StringValue".

Options:

Option Default Description
UpdateSeconds 1 Sweep cadence while sessions exist.
GetContainer creates leaderstats (player) -> Instance. Supply a container another system also writes to; the module then owns only its value objects.
SkipLoop false Never schedule the sweep; specs call sweep() by hand.
Member Description
Leaderstats.attach(kernel, stats, options?) -> handle Wires the mapping. Hooks session start and Kernel.ProfileLoaded at once; the sweep starts with the first session.
handle.sweep() Runs one refresh pass over every tracked session.
handle.detach() Cancels the sweep, disconnects the session and profile listeners, and cleans up every tracked container or value object.

Leaderstats publishes nothing. It consumes Kernel.ProfileLoaded(session, profile) to refresh a session as soon as its profile loads.

ChloeKernel v0.9.1. Access to the framework is private and granted individually by Chloe. Games built with it credit CryptedChloes in their description.