roblox-datastores
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRoblox DataStores
Roblox DataStores
Persist data across sessions in Roblox with : loading on join,
saving on leave and shutdown, safe updates, retries, and ordered stores for
leaderboards. Server-side only.
DataStoreService在Roblox中使用跨会话持久化数据:实现加入时加载、离开和服务器关闭时保存、安全更新、重试,以及用于排行榜的有序存储。仅支持服务器端。
DataStoreServiceWhen to use
适用场景
- Use to save/load player progress (coins, inventory, levels), build persistent leaderboards, or fix data loss, overwrites, and throttling.
- Use when server code calls ,
DataStoreService,GetDataStore,GetAsync,SetAsync, orUpdateAsync.GetOrderedDataStore
When not to use: general scripting, services, remotes, the client/server
split → . High-frequency temporary state (matchmaking, per-round) →
memory stores (a different service). Engine-agnostic persistence theory →
.
roblox-luausave-systems- 用于保存/加载玩家进度(金币、背包、等级)、构建持久化排行榜,或解决数据丢失、覆盖和限流问题。
- 当服务器代码调用、
DataStoreService、GetDataStore、GetAsync、SetAsync或UpdateAsync时使用。GetOrderedDataStore
不适用场景: 通用脚本编写、服务、远程调用、客户端/服务器分离 → 请使用。高频临时状态(匹配、单回合数据)→ 使用内存存储(另一项服务)。与引擎无关的持久化理论 → 使用。
roblox-luausave-systemsCore workflow
核心工作流程
- Enable Studio access once. File → Game Settings → Security → Enable Studio
Access to API Services (use a test place; Studio hits live data). DataStores
work only from server s, never
Scripts.LocalScript - Get a store, then read/write by key. ; key per player is usually
DataStoreService:GetDataStore("Name")."Player_" .. player.UserId - Wrap every call in .
pcall/GetAsync/SetAsyncare network calls that can fail; an unguarded failure errors the thread and risks data loss.UpdateAsync - Load on , save on
PlayerAdded, and alsoPlayerRemoving. A leaving player and a shutting-down server both need a final save.BindToClose - Prefer for read-modify-write (multi-server safe) over
UpdateAsync(blind overwrite). On a failed load, do not overwrite with defaults — abort the save so you don't wipe good data.SetAsync - Use for ranked data (leaderboards) via
OrderedDataStore. Test by joining, changing data, rejoining, and confirming it persisted.GetSortedAsync
- 一次性启用Studio访问权限。文件 → 游戏设置 → 安全性 → 启用Studio对API服务的访问权限(使用测试场景;Studio会访问实时数据)。DataStores仅能在服务器中使用,绝不能在
Script中使用。LocalScript - 获取存储实例,然后按键读写数据。;每个玩家的键通常为
DataStoreService:GetDataStore("Name")。"Player_" .. player.UserId - 将所有调用包装在中。
pcall/GetAsync/SetAsync是可能失败的网络调用;未加防护的失败会导致线程报错,并有数据丢失风险。UpdateAsync - 在时加载数据,
PlayerAdded时保存数据,同时使用PlayerRemoving。玩家离开和服务器关闭时都需要最终保存。BindToClose - 优先使用进行读取-修改-写入操作(多服务器安全),而非
UpdateAsync(盲目覆盖)。加载失败时,不要用默认值覆盖——中止保存,避免擦除有效数据。SetAsync - 使用处理排名数据(排行榜),通过
OrderedDataStore实现。测试方法:加入游戏、修改数据、重新加入,确认数据已持久化。GetSortedAsync
Patterns
模式示例
1. Load on join (pcall-guarded)
1. 加入时加载(含pcall防护)
lua
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local store = DataStoreService:GetDataStore("PlayerData")
local DEFAULT = { Coins = 0, Level = 1 }
Players.PlayerAdded:Connect(function(player)
local key = "Player_" .. player.UserId
local ok, data = pcall(function()
return store:GetAsync(key)
end)
if not ok then
-- Load FAILED (network). Do not treat as a new player; flag so we never save
-- over their real data with defaults.
warn("Load failed for", player.Name, data)
player:SetAttribute("DataLoaded", false)
return
end
player:SetAttribute("DataLoaded", true)
local profile = data or DEFAULT -- nil == genuinely new player
applyToLeaderstats(player, profile)
end)lua
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local store = DataStoreService:GetDataStore("PlayerData")
local DEFAULT = { Coins = 0, Level = 1 }
Players.PlayerAdded:Connect(function(player)
local key = "Player_" .. player.UserId
local ok, data = pcall(function()
return store:GetAsync(key)
end)
if not ok then
-- 加载失败(网络问题)。不要视为新玩家;标记状态以避免用默认值覆盖真实数据。
warn("Load failed for", player.Name, data)
player:SetAttribute("DataLoaded", false)
return
end
player:SetAttribute("DataLoaded", true)
local profile = data or DEFAULT -- nil 代表真正的新玩家
applyToLeaderstats(player, profile)
end)2. Save with UpdateAsync (multi-server safe)
2. 使用UpdateAsync保存(多服务器安全)
lua
-- UpdateAsync reads the latest value, then writes what the callback returns.
-- The callback MUST NOT yield (no task.wait, no further Async calls inside it).
local function savePlayer(player)
if player:GetAttribute("DataLoaded") == false then return end -- never overwrite on a bad load
local key = "Player_" .. player.UserId
local newData = gatherDataFor(player) -- a plain table of serializable values
local ok, err = pcall(function()
store:UpdateAsync(key, function(old)
-- merge/decide here; return nil to cancel the write
return newData
end)
end)
if not ok then warn("Save failed for", player.Name, err) end
endlua
-- UpdateAsync会读取最新值,然后写入回调函数返回的结果。
-- 回调函数**禁止**挂起(不能调用task.wait,也不能在内部进行其他Async调用)。
local function savePlayer(player)
if player:GetAttribute("DataLoaded") == false then return end -- 加载失败时绝不覆盖数据
local key = "Player_" .. player.UserId
local newData = gatherDataFor(player) -- 可序列化值的普通表
local ok, err = pcall(function()
store:UpdateAsync(key, function(old)
-- 在此处合并/决策;返回nil可取消写入
return newData
end)
end)
if not ok then warn("Save failed for", player.Name, err) end
end3. Save on leave AND on shutdown
3. 离开时保存 + 服务器关闭时保存
lua
Players.PlayerRemoving:Connect(savePlayer)
-- BindToClose runs when the server shuts down; save everyone still in.
-- It has a limited time budget, so save in parallel and yield until done.
game:BindToClose(function()
local players = Players:GetPlayers()
local remaining = #players
if remaining == 0 then return end
for _, player in players do
task.spawn(function()
savePlayer(player)
remaining -= 1
end)
end
while remaining > 0 do task.wait() end
end)lua
Players.PlayerRemoving:Connect(savePlayer)
-- BindToClose在服务器关闭时运行;保存所有仍在服务器中的玩家数据。
-- 它有时间限制,因此需并行保存并等待完成。
game:BindToClose(function()
local players = Players:GetPlayers()
local remaining = #players
if remaining == 0 then return end
for _, player in players do
task.spawn(function()
savePlayer(player)
remaining -= 1
end)
end
while remaining > 0 do task.wait() end
end)4. Retry with backoff (transient failures)
4. 带退避策略的重试(临时故障)
lua
local function withRetry(fn, attempts)
attempts = attempts or 3
for i = 1, attempts do
local ok, result = pcall(fn)
if ok then return true, result end
if i < attempts then task.wait(2 ^ i) end -- 2s, 4s, ... backoff
end
return false
end
local ok, data = withRetry(function() return store:GetAsync(key) end)lua
local function withRetry(fn, attempts)
attempts = attempts or 3
for i = 1, attempts do
local ok, result = pcall(fn)
if ok then return true, result end
if i < attempts then task.wait(2 ^ i) end -- 退避间隔:2秒、4秒……
end
return false
end
local ok, data = withRetry(function() return store:GetAsync(key) end)5. Increment a counter
5. 递增计数器
lua
-- IncrementAsync is a convenience for integer read-modify-write (still wrap it).
local ok, newTotal = pcall(function()
return store:IncrementAsync("Visits_" .. player.UserId, 1)
end)lua
-- IncrementAsync是整数读取-修改-写入操作的便捷方法(仍需包装)。
local ok, newTotal = pcall(function()
return store:IncrementAsync("Visits_" .. player.UserId, 1)
end)6. Leaderboard with OrderedDataStore
6. 使用OrderedDataStore实现排行榜
lua
local boards = DataStoreService:GetOrderedDataStore("Coins")
-- Write a player's score (call when it changes, not every frame).
pcall(function() boards:SetAsync("Player_" .. player.UserId, coins) end)
-- Read the top 10, descending.
local ok, pages = pcall(function()
return boards:GetSortedAsync(false, 10) -- ascending=false → highest first
end)
if ok then
for rank, entry in ipairs(pages:GetCurrentPage()) do
print(rank, entry.key, entry.value) -- entry.value is the number
end
endlua
local boards = DataStoreService:GetOrderedDataStore("Coins")
-- 写入玩家分数(分数变化时调用,不要每帧调用)。
pcall(function() boards:SetAsync("Player_" .. player.UserId, coins) end)
-- 读取前10名,降序排列。
local ok, pages = pcall(function()
return boards:GetSortedAsync(false, 10) -- ascending=false → 从高到低排序
end)
if ok then
for rank, entry in ipairs(pages:GetCurrentPage()) do
print(rank, entry.key, entry.value) -- entry.value 为数值
end
endPitfalls
常见陷阱
- Unhandled failure wipes progress → always Async calls; on a failed load, mark the session and refuse to save so defaults never overwrite real data.
pcall - race between servers → two servers writing the same key can clobber each other. Use
SetAsyncfor read-modify-write so each write sees the latest.UpdateAsync - Yielding inside the callback → the callback can't call
UpdateAsyncor other Async functions; compute the new value beforehand and return it.task.wait - No save → players in the server at shutdown lose unsaved progress; add
BindToCloseand wait for saves to finish within its budget.game:BindToClose - Throttling / "too many requests" → respect per-key and per-minute limits; don't
save on every value change. Batch and save on a timer / on leave. is cached briefly, so immediate re-reads may be stale.
GetAsync - Storing non-serializable values → only JSON-serializable data persists: numbers,
strings, booleans, and tables with string/number keys. s,
Instance,Vector3, and functions do not — serialize them to plain tables first.CFrame - Testing without API access → DataStores silently can't be used in Studio until
Enable Studio Access to API Services is on (and they don't work from a
).
LocalScript - is nil for ordered stores →
DataStoreKeyInfodoesn't support versioning/metadata; use a regularOrderedDataStorewhen you need those.DataStore
- 未处理的故障会清除进度 → 始终用包裹Async调用;加载失败时,标记会话并拒绝保存,避免默认值覆盖真实数据。
pcall - 在多服务器间的竞争问题 → 两台服务器写入同一个键可能会互相覆盖。读取-修改-写入操作请使用
SetAsync,确保每次写入都能获取最新值。UpdateAsync - 在回调中挂起 → 回调函数不能调用
UpdateAsync或其他Async函数;需提前计算好新值并返回。task.wait - 未使用保存 → 服务器关闭时仍在服务器中的玩家会丢失未保存的进度;添加
BindToClose并在其时间限制内等待保存完成。game:BindToClose - 限流 / “请求过多” → 遵守单键和每分钟的请求限制;不要在每次值变化时都保存。批量处理并定时保存/离开时保存。会短暂缓存,因此立即重新读取可能会获取到过时数据。
GetAsync - 存储不可序列化的值 → 只有可JSON序列化的数据才能持久化:数字、字符串、布尔值,以及键为字符串/数字的表。、
Instance、Vector3和函数无法持久化——需先将它们序列化为普通表。CFrame - 未开启API访问权限进行测试 → 在Studio中,直到启用Studio对API服务的访问权限后,DataStores才能正常使用(且无法在中工作)。
LocalScript - 有序存储的为nil →
DataStoreKeyInfo不支持版本控制/元数据;需要这些功能时请使用常规OrderedDataStore。DataStore
References
参考资料
- For session locking (preventing duplicate data across servers), versioning/
metadata with , ordered-store pagination (
DataStoreSetOptions), the key error codes and request limits, and Right-to-be-Forgotten compliance, readAdvanceToNextPageAsync.references/sessions-and-limits.md
- 如需了解会话锁定(防止跨服务器数据重复)、使用进行版本控制/元数据管理、有序存储分页(
DataStoreSetOptions)、关键错误代码和请求限制,以及“被遗忘权”合规性,请阅读AdvanceToNextPageAsync。references/sessions-and-limits.md
Related skills
相关技能
- — services, instances, events, and the server/client model.
roblox-luau - — engine-agnostic serialization, slots, and migration.
save-systems
- — 服务、实例、事件,以及客户端/服务器模型。
roblox-luau - — 与引擎无关的序列化、存档槽和数据迁移。
save-systems