mcpField
R
roblox-data-stores

roblox-data-stores

github.com/el4cteo数据与数据库56528 次安装2 周前更新
代码仓库

关于 roblox-data-stores

面向 Roblox 开发者的数据服务技能,涵盖玩家持久化数据、跨服务器临时状态、排行榜和消息传递,并说明如何选择服务及处理会话锁、重试、迁移和平台限制。

主要功能

  1. 01选择 Roblox 存储服务
  2. 02使用 ProfileStore 锁定玩家数据
  3. 03DataStore 写入、重试与迁移
  4. 04OrderedDataStore 排行榜模式
  5. 05MemoryStore 队列与临时状态
  6. 06MessagingService 使用说明

适用场景

  1. 01保存玩家进度与物品栏
  2. 02构建全局或周期排行榜
  3. 03协调跨服务器匹配队列
  4. 04发送跨服务器公告

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/el4cteo/roblox-skills --skill roblox-data-stores

安装后在 Claude Code 中输入/roblox-data-stores确认已加载。

附带文件references/memory-and-messaging.mdreferences/raw-datastore.md

详细介绍

Roblox 数据存储与云服务

选择服务

需求服务备注
玩家进度、物品栏、设置通过 ProfileStore 使用 DataStore(会话锁定)每个玩家一个键(User_{UserId}),≤ 4 MB。
全局排行榜OrderedDataStore仅支持整数值;定期更新,读取有缓存。
快速、临时、跨服状态MemoryStoreService有序映射、队列、哈希映射;所有内容都会过期(TTL ≤ 45 天)。
匹配队列、实时活动、服务器列表MemoryStore 队列 / 有序映射非持久化——绝不要作为宝贵数据的唯一副本。
广播到所有服务器MessagingService每条消息 ≤ 1 kB,尽力而为,不保证送达。
无需发布即可通过 live-ops 更改的值体验配置(ConfigService)参见 roblox-analytics-liveops。

玩家数据:使用会话锁定

两台服务器绝不能同时拥有同一玩家的数据(传送、快速重新加入),否则物品会重复 且进度会丢失。ProfileStore(loleris)实现了会话锁定、自动保存和 BindToClose 刷新,是推荐的默认方案。从 Wally/创作者商店安装它,并将其放在 ServerScriptService 中。

-- ServerScriptService/Server/Services/DataService.luau (ProfileStore)
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")

local ProfileStore = require(ServerScriptService.ServerPackages.ProfileStore)

local TEMPLATE = {
	version = 1,
	coins = 0,
	inventory = {} :: { [string]: number },
}
type PlayerData = typeof(TEMPLATE)

local PlayerStore = ProfileStore.New("PlayerData", TEMPLATE)
local profiles: { [Player]: typeof(PlayerStore:StartSessionAsync("")) } = {}

local function onPlayerAdded(player: Player)
	local profile = PlayerStore:StartSessionAsync(`User_{player.UserId}`, {
		Cancel = function()
			return player.Parent ~= Players -- stop waiting if the player left
		end,
	})
	if profile == nil then
		player:Kick("Could not load your data. Please rejoin.")
		return
	end
	profile:AddUserId(player.UserId) -- GDPR / right-to-be-forgotten linkage
	profile:Reconcile() -- fill in fields added to TEMPLATE since this profile was created
	profile.OnSessionEnd:Connect(function()
		profiles[player] = nil
		player:Kick("Your data was loaded on another server. Please rejoin.")
	end)
	if player.Parent == Players then
		profiles[player] = profile
	else
		profile:EndSession() -- left while loading
	end
end

for _, player in Players:GetPlayers() do
	task.spawn(onPlayerAdded, player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(function(player)
	local profile = profiles[player]
	if profile then
		profile:EndSession() -- saves and releases the lock
	end
end)

local DataService = {}
function DataService.get(player: Player): PlayerData?
	local profile = profiles[player]
	return profile and profile.Data
end
return DataService

在服务器上直接修改 profile.Data(它缓存在内存中并会自动保存)。不要 每次更改都 SetAsync。在依赖较少使用的功能(全局更新、MessageAsync、Studio 的模拟存储)之前, 请查阅 ProfileStore 文档以了解当前确切的 API。

原始 DataStoreService 规则(不使用封装时)

  • 在内存中缓冲:加入时加载一次,修改内存中的表,定期保存(例如每 2–5 分钟加上随机抖动)、离开时、检查点(购买)时,以及 game:BindToClose 中保存。
  • 当写入依赖于旧值或多个服务器可能写入该键时,使用 UpdateAsync 而非 SetAsync。转换函数必须是纯函数且快速(不能 yield);返回 nil 以取消。
  • 重试瞬时错误,使用指数退避 + 抖动,限制尝试次数;对某个键的重试按顺序处理 (旧的重试绝不能覆盖较新的数据)。将写入失败视为“结果未知”。
  • 绝不要保存 Instance、Vector3、CFrame、函数或混合/稀疏表:存储普通表、 数字、字符串、布尔值和 buffer。序列化数据类型({ x, y, z })。验证 UTF-8 字符串。
  • 键和数据存储名称 ≤ 50 个字符;值在 JSON 编码后 ≤ 4,194,304 个字符。
  • 稳定的键模式(User_{UserId}),绝不要使用显示名称。少量数据存储,每个玩家少量键。
  • Studio 需要 File → Experience Settings → Security → Enable Studio Access to API Services;使用单独的测试 universe 或不同的数据存储名称,以确保测试永远不会触及生产数据。

完整的原始实现(会话锁定、自动保存、BindToClose、重试): references/raw-datastore.md。

预算和限制(2026)

限制值
体验范围的标准读取 / 写入每分钟 300 + 40 × CCU / 300 + 20 × CCU(与 Open Cloud 共享)
默认每服务器读取/写入每分钟各 60 + 40 × players(可配置)
每服务器有序写入每分钟 30 + 5 × players
每键吞吐量每分钟读取 25 MB,每分钟写入 4 MB
存储(最新版本,压缩后)每个体验 500 MB + 1 MB × 生命周期用户数
每种请求类型的队列30 个待处理请求,之后报错 301–306
  • UpdateAsync 同时消耗读取和写入预算。
  • 在批量工作前检查 DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.StandardWrite);启动时使用 SetRateLimitForRequestType 一次性调整每服务器限制。
  • 不要预先压缩数据;Roblox 会自动压缩。仅对真正热门的键进行分片。
  • 使用版本(ListVersionsAsync、GetVersionAtTimeAsync)恢复数据,而不是写入备份键。

架构版本控制与迁移

存储一个 version 字段。加载时,按顺序运行迁移函数,直到数据达到当前版本,然后协调默认值。在同一版本中停止写入某个字段时,不要删除该字段;不要用不同含义复用字段名。

--!strict
type Data = { [string]: any }

local MIGRATIONS: { (Data) -> () } = {
	[1] = function(data) -- v1 -> v2: "gold" renamed to "coins"
		data.coins = data.gold or 0
		data.gold = nil
	end,
	[2] = function(data) -- v2 -> v3: inventory list -> counts
		local counts: { [string]: number } = {}
		for _, itemId in data.inventory or {} do
			counts[itemId] = (counts[itemId] or 0) + 1
		end
		data.inventory = counts
	end,
}
local CURRENT_VERSION = #MIGRATIONS + 1

local function migrate(data: Data): Data
	local version = data.version or 1
	while version < CURRENT_VERSION do
		MIGRATIONS[version](data)
		version += 1
	end
	data.version = CURRENT_VERSION
	return data
end

print(migrate({ gold = 5, inventory = { "Sword", "Sword" } }))

排行榜(OrderedDataStore)

  • 值必须为整数。按计划或在发生有意义的变化时写入,不要每次递增都写入。
  • 在一个服务端循环中(例如每 60 秒)使用 GetSortedAsync(false, 100) 读取前 N 名,缓存结果并复制到客户端。不要让每个服务端/客户端都不断读取。
  • 对于“本周”排行榜,将周期加入存储名称(Wins_2026W39),或使用 MemoryStore 排序映射。
  • Roblox 原生排行榜(已全面发布):在 Creator Hub(游戏 → 排行榜)中注册 OrderedDataStore,并开启“Active leaderboard”。Roblox 会在玩家列表中显示持久化的“Global”和“Friends”选项卡。该存储仍是事实来源(每个游戏只能有一个启用的排行榜)。玩家加入时初始化其分数,确保选项卡正确显示。

MemoryStore 要点

  • 配额按整个体验计算:内存为 64 KB + 1.2 KB × 用户数;请求为每分钟 1000 + 120 × CCU 个单位。
  • MemoryStoreSortedMap:用于排名数据、服务端浏览器和实时排行榜(SetAsync 并设置过期时间、GetRangeAsync、UpdateAsync)。
  • MemoryStoreQueue:用于匹配队列/工作队列(AddAsync、ReadAsync,成功处理后使用返回的 ID 调用 RemoveAsync;设置不可见超时时间以应对崩溃)。
  • MemoryStoreHashMap:用于无需排序的高吞吐量键值存储(SetAsync、GetAsync、UpdateAsync、ListItemsAsync)。
  • 始终设置过期时间;处理限流(pcall + 重试);保持条目精简。

MessagingService 要点

  • 消息大小 ≤ 1 kB。每个服务端:每分钟发送 600 + 240 × 玩家数 条;订阅数为 20 + 8 × 玩家数。
  • 消息传递是尽力而为且不保证顺序:将其用于通知(“刷新缓存”、全局公告),绝不要将其作为事实来源。状态数据应配合 DataStore/MemoryStore 使用。
  • 用 pcall 包装 SubscribeAsync/PublishAsync;这些操作可能失败或挂起。

更多代码(MemoryStore 队列匹配、跨服务端公告、RTBF):references/memory-and-messaging.md。

被遗忘权

使用静态键模式,以便自动 RTBF(在 Creator Dashboard 中配置)能够删除 PlayerData/User_{userId} 这类玩家数据模板。否则,应处理删除权 Webhook(参见 roblox-open-cloud)。将键关联到用户 ID:调用 SetAsync(key, value, { userId }),在 UpdateAsync 转换函数中将其作为第二个值返回,或调用 ProfileStore 的 AddUserId。

相关技能

roblox-security(复制与交易漏洞利用)、roblox-monetization(收据)、roblox-open-cloud(外部访问、批量操作)、roblox-teleport-matchmaking。

评论

支持 Markdown