mcpField
S
scenario-roblox-data

scenario-roblox-data

github.com/scenario-labs数据与数据库1.2k54 次安装1 天前更新
代码仓库

关于 scenario-roblox-data

面向 Roblox 开发者的数据层技能,涵盖持久化、购买、交易和传送,并提供应对重试、崩溃、模式变更及多服务器竞争的模式与测试工具。

主要功能

  1. 01Profile 会话锁
  2. 02版本化数据模式迁移
  3. 03幂等写入重试
  4. 04购买收据处理
  5. 05崩溃后的交易恢复
  6. 06模拟多服务器测试

适用场景

  1. 01安全持久化玩家 Profile
  2. 02迁移已有玩家数据
  3. 03防止重复发放购买物品
  4. 04服务器崩溃后恢复交易

MCP 相关

说明如何通过 Roblox tooling 工具包使用 Studio MCP 运行数据测试,并在 Edit 和 Playtest 会话中检查行为。

安装

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

$npx skills add https://github.com/scenario-labs/skills --skill scenario-roblox-data

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
critique.md
expert-notes.md
gui-paths.md
procedures.md
sources.md
scripts/
rbx_data.py
scripts/agentkit/data/
DataAudit.luau
DataCheck.luau
KeyQueue.luau
lune_data_audit.luau
Migrate.luau
MockDataStore.luau
PlayerData.luau
Receipts.luau
SafeTeleport.luau
ServerSim.luau
Trade.luau

详细介绍

Roblox 数据与后端

在 macOS 上使用 Roblox Studio(2026 年 10 月版本),通过 scenario-roblox-expert 工具包驱动。

这里的专家级别意味着:即使服务器崩溃、写入失败但实际已落盘,或两台服务器争抢同一名玩家,数据层也能保持正确。在进行任何已发布测试之前,先针对模拟的多服务器后端验证这些情况,因为未发布的体验无法访问 DataStores。

如果你可用的技能中缺少这里提到的同级技能,请让用户安装它 (npx skills add scenario-labs/skills --skill <name>);若无人值守,则依据工具架构继续操作,并指出这一缺口。

必备背景: scenario-roblox-expert(通道、审查流程、工具包、当前 Roblox 易踩的陷阱)。

原则(专家级差异)

  1. 会话锁可防止重复,单靠 UpdateAsync 做不到。 每名玩家的一个 ProfileStore 配置文件持有锁(loleris,ProfileStore 文档)。只有在转换函数读取旧值时,UpdateAsync 才比 SetAsync 安全(Suphi Kaner,00:04:43)。
  2. 写入失败时,数据可能已经落盘。 重新执行前,使用 UseCache = false 读取(Roblox 最佳实践)。在值中加入操作 ID,让重复操作变为无操作 [新增]。模拟运行中,30% 的写入在落盘后失败;简单重试导致 30 次操作最终存储了 39 次。
  3. 同一键的重试必须保持顺序。 较早值的延迟重试会覆盖较新的数据(Roblox 最佳实践)。KeyQueue 会按键串行处理。
  4. 购买只授予一次,并确认已保存。 在配置文件中记录每个 PurchaseId。只有当 LastSavedData 包含该 ID 时才返回 PurchaseGranted(Rileybytes,00:29:08;Roblox 购买文档)。其他所有情况都返回 NotProcessedYet。
  5. 先迁移,再执行 Reconcile。 Reconcile 会把模板中的 SchemaVersion 复制到旧数据中,导致迁移被跳过 [新增,已在真实 ProfileStore 上验证]。
  6. 旧服务器绝不保存更新版本的数据。 更新期间,新旧服务器会同时运行。遇到更高的 SchemaVersion 时,拒绝处理、不改动数据地释放锁,并踢出玩家 [新增]。多体验游戏要同时发布所有体验(CJ_Oyer,00:27:33)。
  7. 两个配置文件无法原子保存。 不让出执行权同时编辑两边,只能保护内存(loleris)。先将双方的操作放入托管区,再让一条交易日志记录决定结果 [新增]。没有托管区时,两次保存之间发生崩溃会导致盾牌重复、剑丢失。
  8. 使用注入式存储进行测试。 编写数据代码时,应支持将 DataStoreService 替换为 MockDataStore,并在不修改 ProfileStore 的情况下运行多个模拟服务器 [新增]。

首先确认

  • 已发布且启用了 Studio API 访问? 未发布:每次 DataStore 调用都会抛出异常,因此要使用模拟数据进行测试。已发布且启用了访问:Studio 会写入正式键,因此请使用单独的测试体验(Roblox 文档)或 ProfileStore.Mock。
  • 哪些数据持久化到哪里。 玩家状态存入一个配置文件。排行榜使用 OrderedDataStore。匹配和短期共享状态使用 MemoryStore。公告使用 MessagingService。
  • 有交易、Developer Products 或多个体验? 每项都需要执行下面的额外阶段。
  • CCU 峰值、每台服务器的玩家数、配置文件大小。 将这些数据提供给 rbx_data.budget。
  • 数据已经在线上使用? 那么键和存储名称都已固定;每项更改都要成为迁移步骤。

默认值:使用固定提交版本的 ProfileStore,存储名为 PlayerData,键为 Player_<UserId>,包含 SchemaVersion 字段,以及一份包含 100 条记录的 PurchaseIds 日志。

流程

模块位于 scripts/agentkit/data/,且不包含 requires。可通过 rbx_data.run(studio, code, dm, data=[...]) 内联运行,或通过 rbx_data.build_place(dir) 将其构建到某个位置。完整代码和结果:procedures.md。

  1. 探测环境。 目标:了解这里有哪些服务可用。方法: rbx_data.probe_services(studio, "Edit"),然后在试玩测试中运行 "Server"。门槛:记录错误文本, 并根据这些文本选择存储方式(真实或模拟)。
  2. 设计架构。 目标:让配置文件可按原样保存并安全扩展。方法: DataCheck.report(template) 和一个填满数据的示例,以及 DataCheck.key(key, {userId})。门槛:错误 发现为零,大小低于 4,194,304 个字符的 25%,且 JSON 往返结果与输入相同。
  3. 规划预算。 目标:高峰期不受限流。方法: rbx_data.budget(players, ccu, kb, saves_per_min)。门槛:高峰 CCU 下 ok 为 true,且每个键每分钟的写入次数足以覆盖自动保存和购买。
  4. 接入会话。 目标:每个配置文件只有一个所有者。方法:PlayerData 使用 Cancel 运行 StartSessionAsync,在返回 nil 时踢出玩家,然后调用 AddUserId、迁移、Reconcile,在 OnSessionEnd 时踢出玩家,并在离开时调用 EndSession。门槛:sessions 套件通过,且试玩测试显示已加载配置文件, DataStoreState 为 "NoAccess"。
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ProfileStore = require(ServerStorage.Packages.ProfileStore)
local PlayerData = require(ServerStorage.DataKit.PlayerData)
local Migrate = require(ServerStorage.DataKit.Migrate)

local data = PlayerData.new({
	ProfileStore = ProfileStore,
	Players = Players,
	storeName = "PlayerData",
	template = { SchemaVersion = 2, Coins = 0, Inventory = {}, PurchaseIds = {} },
	migrate = Migrate.new({
		latest = 2,
		steps = {
			-- v1 kept owned items as an array of names; v2 counts them by id.
			[2] = function(d: any)
				d.Inventory = d.Inventory or {}
				for _, item in ipairs(d.Items or {}) do
					d.Inventory[item] = (d.Inventory[item] or 0) + 1
				end
				d.Items = nil
			end,
		},
	}),
})
data:Start()
  1. 为架构添加版本。 目标:旧数据只升级一次。方法:Migrate.new{latest, steps},每个旧版本 一个测试样例。门槛:schema 套件通过,包括 Reconcile 陷阱和拒绝“更新版本”的情况。
  2. 发放购买物品。 目标:每个 PurchaseId 恰好发放一次。方法: Receipts.new{getProfile, isPresent, handlers};在一个脚本中赋值 ProcessReceipt。门槛: receipts 套件通过,并覆盖幽灵写入、崩溃和服务器变更。
  3. 原始存储。 目标:排行榜、计数器和日志在重试后仍能保留。方法:使用值中包含操作 ID 且带有 verify 函数的 KeyQueue.new(store)。门槛:keyqueue 套件通过。
  4. 交易。 目标:崩溃时不会创建或丢失物品。方法:对两名玩家调用 Trade.paidTradingAllowed(PolicyService IsPaidItemTradingAllowed,遇到故障时默认拒绝),使用 Trade.new{log = KeyQueue.new(tradeLogStore)}, 并在 onLoaded 中调用 trade:Recover(profile)。门槛:trade 套件在每个崩溃阶段都通过。
  5. 传送。 目标:队伍成员一同抵达。方法:使用 SafeTeleport.new{TeleportService},组队时使用 TeleportParty。门槛:teleport 套件通过模拟服务测试;真实传送需要已发布的体验。
  6. 审计。 目标:代码中不再遗留已知错误。方法:在 Edit 中运行 DataAudit.place(game),或离线运行 rbx_data.py audit place.rbxl。门槛:测试样例之外的错误发现为零。
  7. 运维。 使用 PlayerData:Restore(ProfileStore VersionQuery)回滚,在 OnLastSave 中进行最终编辑,使用 rbx_data.rtbf_config 配置 RTBF 模板:流程 13 至 15。
  8. 验证并报告。 运行离线套件,然后通过 rbx_run 在 Studio Edit 中运行相同套件,接着进行试玩测试检查。列出仍需已发布体验的事项。

数值

数值适用范围
服务器读取或写入:每分钟 60 + 40 x 玩家数每台服务器,默认值,可调整
每分钟体验读取 300 + 40 x CCU,写入 300 + 20 x CCU整个体验,与 Open Cloud 共享
UpdateAsync,因此每次 ProfileStore 保存都会消耗 1 次读取和 1 次写入两种预算
每个键:每分钟写入 4 MB,读取 25 MB每次请求按整 KB 向上取整
值最多 4,194,304 个字符;键、名称、范围最多 50 个字符每个键
每种请求类型的队列最多 30 个,之后返回错误 301 到 306每台服务器
GetAsync 缓存 4 s每个数据存储实例
每个键每 UTC 小时保留一个版本,覆盖后保留 30 天每个键
存储空间 500 MB + 1 MB x 用户生命周期数压缩后的最新版本
ProfileStore 自动保存间隔 300 s、抢占锁等待 40 s、判定为死亡需 630 s固定来源;判定死亡时会比较不同服务器上的 os.time()
ProfileStore 首次重试间隔 5 s,之后每 10 s 重试另一台服务器持有锁期间
配置文件加载超过 5 s视为错误(loleris)
BindToClose 预算 30 s每台服务器关闭时
MessagingService 消息 1 kB每次发布

质量门槛

  • run_offline.sh:所有 Lune 测试套件均通过,rbx_check 在 kit、place 和 snippets 中报告零错误。
  • run_live.py:相同的测试套件在 Studio Edit 中通过,且游戏测试检查通过。
  • DataAudit:除 fixtures 外,错误发现数为零。
  • 控制台:在未发布的游戏测试中出现一行 "[ProfileStore]: Roblox API services unavailable",且没有保存错误。
  • 已发布的测试体验(之后):两台真实服务器在 5 s 内完成玩家交接。

常见错误

错误症状修复
在 PlayerRemoving 上使用 SetAsync,且没有锁回滚、跨服务器跳转后物品重复通过 PlayerData 使用 ProfileStore
使用 player.Name 作为键重命名后数据丢失Player_<UserId>
在迁移前执行 Reconcile旧字段无处可用,新字段为空先迁移
重试时再次调用同一写入计数过高在值中加入操作 ID,使用 KeyQueue
在保存前返回 PurchaseGranted已付款,但崩溃后未发放Receipts
会让出执行权的处理程序发放操作应用两次处理程序会在副本上运行,且不得让出执行权
将两次档案保存当作交易物品重复或丢失使用带托管机制的 Trade
在 pcall 外调用 GetDataStoreStudio 启动时脚本报错在 pcall 内探测,然后回退到模拟对象
在未发布的试玩中等待 MessagingService线程挂起在 task.spawn 中调用
使用 ShouldReserveServer 重试传送队伍被拆分到不同服务器只预留一次,并重复使用访问码
在 Edit 中编辑 ModuleScript 的 Source,然后再次 require旧代码仍在运行先用 Clone 替换模块

交接

  • 来自 scenario-roblox-architecture:数据服务所在位置,以及各系统如何读取数据。
  • 来自 scenario-roblox-monetization:商品 ID 和金额。交回:Receipts 处理程序表。
  • 来自 scenario-roblox-networking:经过验证的交易和商店请求。交回:Trade 和库存 调用。
  • 交给 scenario-roblox-ui 和 scenario-roblox-gameplay:通过 Attributes 或信号公开的值,绝不 公开原始档案。
  • 交给 scenario-roblox-performance:关闭时序和更新发布。
  • 交给 scenario-roblox-tooling:用于 CI 的 Lune 测试套件。
  • 交给 scenario-roblox-luau:模板的类型。

Roblox 最新说明

  • DataStore 限制于 2026-07-29 起改为全体验共享;旧答案中提到的 60 + 10 x 玩家数和 6 秒冷却时间均已取消。
  • 存储限制从 2026-04-09 起开始实施。
  • 自 2026-03-10 起,HttpService:JSONEncode 会将 NaN 和无穷大编码为带标签的对象,并会静默丢弃 稀疏和混合条目(实时测试)。
  • ProfileService 不受支持;请使用 ProfileStore。DataStore2 已过时。
  • ReserveServerAsync 取代 ReserveServer。
  • Studio API 访问:File > Experience Settings > Security > Enable Studio Access to API Services, 仅限已发布的游戏。
  • Studio 预算不遵循服务器公式:单人试玩中一次读取的预算为 585。

参考资料

评论

支持 Markdown