Codectionary / Developer documentation / Luau

Load and validate player data

A successful read returning nil means no stored value exists. A failed read means the value is unknown. Those cases must never share a fallback that later saves defaults over real data. Validate stored fields before marking a session ready.

Syntax

-- ready only after successful read AND validation
-- failed read → no gameplay writes and no save

Examples

Load a small versioned progress record

ModuleScript named LessonLoad in ServerScriptService of the separate test experience. Its nil result means loading failed or stored data was invalid. No session is created and nothing is written on either path. The schema is intentionally limited to a monotonic highest lesson.

local store = game:GetService("DataStoreService"):GetDataStore("LessonProgress_TEST_v1")
local LessonLoad = {}
function LessonLoad.load(userId)
    local ok, raw = pcall(function()
        return store:GetAsync("player:" .. tostring(userId))
    end)
    if not ok then return nil, "Read failed" end
    if raw == nil then return {version = 1, highestLesson = 0} end
    if type(raw) ~= "table" or raw.version ~= 1 then return nil, "Unsupported record" end
    local highest = raw.highestLesson
    if type(highest) ~= "number" or highest ~= highest or highest < 0 or highest > 32 or highest % 1 ~= 0 then
        return nil, "Invalid progress"
    end
    return {version = 1, highestLesson = highest}
end
return LessonLoad

Fail closed instead of inventing a session

Script in ServerScriptService using LessonLoad above. This wiring shows loading only; it does not save or award progress. A player who leaves during the request receives no session. A loading failure ends the session rather than allowing unsaved progress.

local Players = game:GetService("Players")
local LessonLoad = require(script.Parent:WaitForChild("LessonLoad"))
local started, sessions = {}, {}
local function onPlayer(player)
    if started[player] then return end
    started[player] = true
    local data = LessonLoad.load(player.UserId)
    if player.Parent ~= Players then return end
    if not data then
        player:Kick("Progress could not be loaded. Please rejoin later.")
        return
    end
    sessions[player] = {ready = true, data = data}
    print("Loaded", player.Name, data.highestLesson)
end
Players.PlayerAdded:Connect(onPlayer)
Players.PlayerRemoving:Connect(function(player)
    sessions[player], started[player] = nil, nil
end)
for _, player in Players:GetPlayers() do task.spawn(onPlayer, player) end

Best practices

  • Never write defaults after pcall returns false, or after validation rejects a record.
  • Do not silently reinterpret an unknown schema version as a new player.
  • Keep loading, ready, saving and failed states explicit; only trusted server actions may change a ready session.

At a glance

Purpose
Typed scripting and Roblox development
File extension
.luau
Runs in
Luau host; Roblox engine examples require Roblox Studio
Usually used with
Roblox APIs and Studio

Specifications & further reading

Related Luau documentation