roblox-datastores
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseroblox-datastores
Roblox数据存储
Reference for Roblox — saving, loading, and managing player data on the server.
DataStoreServiceRoblox 参考文档——在服务器端保存、加载和管理玩家数据。
DataStoreServiceQuick Reference
快速参考
| Method | Signature | Notes |
|---|---|---|
| | Returns a |
| | For leaderboards |
| | Returns value or nil |
| | No return value needed |
| | Atomic read-modify-write |
| | Deletes key, returns old value |
| | Returns |
| 方法 | 签名 | 说明 |
|---|---|---|
| | 返回一个 |
| | 用于排行榜 |
| | 返回值或nil |
| | 无需返回值 |
| | 原子化读-改-写操作 |
| | 删除键,返回旧值 |
| | 返回 |
Basic Setup
基础设置
lua
-- Server Script (ServerScriptService)
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local playerStore = DataStoreService:GetDataStore("PlayerData_v1")
local DEFAULT_DATA = {
coins = 0,
level = 1,
xp = 0,
}lua
-- Server Script (ServerScriptService)
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local playerStore = DataStoreService:GetDataStore("PlayerData_v1")
local DEFAULT_DATA = {
coins = 0,
level = 1,
xp = 0,
}Loading Data (GetAsync + pcall)
加载数据(GetAsync + pcall)
Always wrap datastore calls in . They can fail due to network issues or rate limits.
pcalllua
local function loadData(player)
local key = "player_" .. player.UserId
local success, data = pcall(function()
return playerStore:GetAsync(key)
end)
if success then
local result = {}
for k, v in pairs(DEFAULT_DATA) do result[k] = v end
if data then
for k, v in pairs(data) do result[k] = v end
end
return result
else
warn("Failed to load data for", player.Name, ":", data)
return nil -- signal failure; do not give default data silently
end
end始终用包裹数据存储调用,因为网络问题或速率限制可能导致调用失败。
pcalllua
local function loadData(player)
local key = "player_" .. player.UserId
local success, data = pcall(function()
return playerStore:GetAsync(key)
end)
if success then
local result = {}
for k, v in pairs(DEFAULT_DATA) do result[k] = v end
if data then
for k, v in pairs(data) do result[k] = v end
end
return result
else
warn("Failed to load data for", player.Name, ":", data)
return nil -- 标记失败;不要静默提供默认数据
end
endSaving Data (SetAsync vs UpdateAsync)
保存数据(SetAsync vs UpdateAsync)
Use for simple overwrites. Use when the value must be based on the current stored value (e.g., incrementing a counter safely across servers).
SetAsyncUpdateAsynclua
-- Simple save
local function saveData(player, data)
local key = "player_" .. player.UserId
local success, err = pcall(function()
playerStore:SetAsync(key, data)
end)
if not success then
warn("Failed to save data for", player.Name, ":", err)
end
end
-- Atomic increment with UpdateAsync
local function addCoinsAtomic(userId, amount)
local key = "player_" .. userId
pcall(function()
playerStore:UpdateAsync(key, function(current)
current = current or { coins = 0 }
current.coins = current.coins + amount
return current
end)
end)
end简单覆盖操作使用。当值必须基于当前存储的值时(例如跨服务器安全地递增计数器),使用。
SetAsyncUpdateAsynclua
-- 简单保存
local function saveData(player, data)
local key = "player_" .. player.UserId
local success, err = pcall(function()
playerStore:SetAsync(key, data)
end)
if not success then
warn("Failed to save data for", player.Name, ":", err)
end
end
-- 原子化递增(使用UpdateAsync)
local function addCoinsAtomic(userId, amount)
local key = "player_" .. userId
pcall(function()
playerStore:UpdateAsync(key, function(current)
current = current or { coins = 0 }
current.coins = current.coins + amount
return current
end)
end)
endRetry Logic
重试逻辑
lua
local MAX_RETRIES = 3
local RETRY_DELAY = 2
local function safeGet(store, key)
for attempt = 1, MAX_RETRIES do
local success, result = pcall(function()
return store:GetAsync(key)
end)
if success then return true, result end
warn(string.format("GetAsync attempt %d/%d failed: %s", attempt, MAX_RETRIES, result))
if attempt < MAX_RETRIES then task.wait(RETRY_DELAY) end
end
return false, nil
endlua
local MAX_RETRIES = 3
local RETRY_DELAY = 2
local function safeGet(store, key)
for attempt = 1, MAX_RETRIES do
local success, result = pcall(function()
return store:GetAsync(key)
end)
if success then return true, result end
warn(string.format("GetAsync attempt %d/%d failed: %s", attempt, MAX_RETRIES, result))
if attempt < MAX_RETRIES then task.wait(RETRY_DELAY) end
end
return false, nil
endAuto-Save: PlayerRemoving + BindToClose
自动保存:PlayerRemoving + BindToClose
Server shutdown without silently discards unsaved data.
BindToCloselua
local sessionData = {} -- [userId] = data table
Players.PlayerAdded:Connect(function(player)
local data = loadData(player)
if data then
sessionData[player.UserId] = data
else
player:Kick("Could not load your data. Please rejoin.")
end
end)
Players.PlayerRemoving:Connect(function(player)
local data = sessionData[player.UserId]
if data then
saveData(player, data)
sessionData[player.UserId] = nil
end
end)
-- Flush all sessions on server shutdown
game:BindToClose(function()
for userId, data in pairs(sessionData) do
local key = "player_" .. userId
pcall(function()
playerStore:SetAsync(key, data)
end)
end
end)如果没有,服务器关机时会静默丢弃未保存的数据。
BindToCloselua
local sessionData = {} -- [userId] = 数据表
Players.PlayerAdded:Connect(function(player)
local data = loadData(player)
if data then
sessionData[player.UserId] = data
else
player:Kick("无法加载你的数据,请重新加入。")
end
end)
Players.PlayerRemoving:Connect(function(player)
local data = sessionData[player.UserId]
if data then
saveData(player, data)
sessionData[player.UserId] = nil
end
end)
-- 服务器关机时刷新所有会话数据
game:BindToClose(function()
for userId, data in pairs(sessionData) do
local key = "player_" .. userId
pcall(function()
playerStore:SetAsync(key, data)
end)
end
end)Ordered DataStores (Leaderboards)
有序数据存储(排行榜)
Values must be positive integers.
lua
local coinsLeaderboard = DataStoreService:GetOrderedDataStore("Coins_v1")
local function setLeaderboardScore(userId, coins)
pcall(function()
coinsLeaderboard:SetAsync("player_" .. userId, math.floor(coins))
end)
end
local function getTopPlayers(count)
local success, pages = pcall(function()
return coinsLeaderboard:GetSortedAsync(false, count) -- false = descending
end)
if not success then return {} end
local results = {}
for rank, entry in ipairs(pages:GetCurrentPage()) do
table.insert(results, { rank = rank, userId = entry.key, score = entry.value })
end
return results
end值必须为正整数。
lua
local coinsLeaderboard = DataStoreService:GetOrderedDataStore("Coins_v1")
local function setLeaderboardScore(userId, coins)
pcall(function()
coinsLeaderboard:SetAsync("player_" .. userId, math.floor(coins))
end)
end
local function getTopPlayers(count)
local success, pages = pcall(function()
return coinsLeaderboard:GetSortedAsync(false, count) -- false = 降序
end)
if not success then return {} end
local results = {}
for rank, entry in ipairs(pages:GetCurrentPage()) do
table.insert(results, { rank = rank, userId = entry.key, score = entry.value })
end
return results
endData Versioning / Migration
数据版本控制 / 迁移
Include a field and migrate in the load path.
_versionlua
local CURRENT_VERSION = 2
local function migrateData(data)
local version = data._version or 1
if version < 2 then
data.coins = data.gold or 0 -- renamed field
data.gold = nil
data._version = 2
end
return data
endUse a versioned datastore name () for breaking schema changes.
PlayerData_v2在数据中加入字段,并在加载流程中进行迁移。
_versionlua
local CURRENT_VERSION = 2
local function migrateData(data)
local version = data._version or 1
if version < 2 then
data.coins = data.gold or 0 -- 字段重命名
data.gold = nil
data._version = 2
end
return data
end当出现破坏性的 schema 变更时,使用带版本号的数据存储名称(如)。
PlayerData_v2Common Mistakes
常见错误
| Mistake | Consequence | Fix |
|---|---|---|
No | Unhandled error crashes the script | Always wrap in |
Saving on every | Hits rate limits (60 + numPlayers×10 writes/min) | Throttle; save on remove + periodic interval |
No | Data lost on server shutdown | Always flush all sessions in |
| Giving default data on load failure | Player silently loses progress | Return |
| Race condition across servers | Use |
| Storing Instances or functions | Data silently drops | Store only strings, numbers, booleans, plain tables |
| Reusing datastore name after schema change | Old shape clashes with new code | Append |
| 错误 | 后果 | 修复方案 |
|---|---|---|
数据存储调用未用 | 未处理的错误会导致脚本崩溃 | 始终用 |
每次 | 触发速率限制(每分钟60 + 玩家数×10次写入) | 限制保存频率;在玩家离开时保存 + 定期保存 |
未设置 | 服务器关机时数据丢失 | 务必在 |
| 加载失败时提供默认数据 | 玩家进度被静默丢失 | 失败时返回 |
使用 | 跨服务器出现竞态条件 | 原子化读-改-写操作使用 |
| 存储实例或函数 | 数据被静默丢弃 | 仅存储字符串、数字、布尔值、普通表 |
| schema变更后复用数据存储名称 | 旧数据结构与新代码冲突 | 出现破坏性变更时,在名称后追加 |