Data stores¶
Nova implements the classic DataStoreService surface and backs it with the
persistence service on the official platform. The core operations work:
DataStoreService:GetDataStore(name, scope)DataStoreService:GetGlobalDataStore()DataStoreService:GetOrderedDataStore(name, scope)GlobalDataStore:GetAsync(key)GlobalDataStore:SetAsync(key, value)GlobalDataStore:IncrementAsync(key, delta)GlobalDataStore:UpdateAsync(key, transformFunction)GlobalDataStore:OnUpdate(key, callback)OrderedDataStore:GetSortedAsync(ascending, pageSize, minValue, maxValue)
Server only
Data stores are server-side. Call them from a Script or a ModuleScript
used by a server script, never from a LocalScript.
Basic usage¶
local DataStoreService = game:GetService("DataStoreService")
local playerStore = DataStoreService:GetDataStore("PlayerData")
local function keyFor(player: Player): string
return "player_" .. player.UserId
end
local function load(player: Player)
local ok, result = pcall(function()
return playerStore:GetAsync(keyFor(player))
end)
if not ok then
warn("load failed for", player.Name, result)
return nil
end
return result
end
local function save(player: Player, data)
local ok, err = pcall(function()
playerStore:SetAsync(keyFor(player), data)
end)
if not ok then
warn("save failed for", player.Name, err)
end
end
game:GetService("Players").PlayerAdded:Connect(function(player)
local data = load(player) or { Coins = 0 }
print(player.Name, "has", data.Coins, "coins")
end)
Wrap every data-store call in pcall. A failed request should not take down the
player's session.
UpdateAsync and session locking¶
UpdateAsync is the safe way to read-modify-write a key. The callback receives
the current value and returns the new value (or nil to cancel the write).
local function addCoins(player: Player, amount: number)
local ok, newValue = pcall(function()
return playerStore:UpdateAsync(keyFor(player), function(current)
current = current or { Coins = 0 }
current.Coins += amount
return current
end)
end)
if not ok then
warn("update failed", newValue)
end
end
For a full session-lock pattern:
local SESSION_TTL = 60 -- seconds; refresh while the player is in the server
local function acquire(player: Player)
local key = keyFor(player)
local ok, acquired = pcall(function()
return playerStore:UpdateAsync(key, function(current)
current = current or { Coins = 0 }
local now = os.time()
if current.Lock and current.Lock.expires > now
and current.Lock.jobId ~= game.JobId then
return nil -- someone else owns the session
end
current.Lock = {
jobId = game.JobId,
expires = now + SESSION_TTL,
}
return current
end)
end)
return ok and acquired ~= nil
end
Refresh the lock periodically with another UpdateAsync, and release it on
PlayerRemoving by setting Lock = nil. If the server crashes, the TTL lets the
next server take over.
OrderedDataStore¶
OrderedDataStore is for leaderboards. Values must be integers.
local leaderboard = DataStoreService:GetOrderedDataStore("Coins")
local function topTen()
local ok, pages = pcall(function()
return leaderboard:GetSortedAsync(false, 10) -- descending
end)
if not ok then
warn("leaderboard failed", pages)
return {}
end
return pages:GetCurrentPage()
end
GetSortedAsync returns a pages object. Use AdvanceToNextPageAsync() and
IsFinished to walk it.
Differences from modern Roblox¶
DataStoreOptions,DataStoreGetOptions,DataStoreSetOptions, and the request-budget API are not implemented yet.GetDataStoretakes only(name, scope).RemoveAsyncis not part of Nova's surface.- Request budgets and throttling are enforced by the platform backend rather than the Roblox data-store service. Treat limits as real and back off on errors.
- The backend stores data per universe. A place must belong to a universe for persistence to resolve.
Debugging¶
| Error | Meaning |
|---|---|
DataStore ... does not exist / empty result |
The key has never been written, or the universe/scope is wrong. |
403/401 from persistence |
The server is not authenticated to the platform backend. |
UpdateAsync returns nil |
Your callback returned nil (often a lock conflict). |
| Data appears in Studio but not live | Studio and the live server may resolve different universes. Check the place/universe mapping. |