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のデータとバックエンド

scenario-roblox-expertツールキットを介して操作する、macOS上のRoblox Studio(2026年10月ビルド)。

ここでいうエキスパートレベルとは、サーバーがクラッシュしたとき、書き込みが反映された後に失敗したとき、または2台のサーバーが同じプレイヤーを処理しようとしたときにも、データ層が正しく動作することを意味します。公開前のテスト場所からはDataStoreにアクセスできないため、公開テストを行う前に、シミュレートしたマルチサーバーのバックエンドでこれらのケースを検証してください。

ここに記載された関連スキルが利用可能なスキルにない場合は、ユーザーにインストールを依頼してください (npx skills add scenario-labs/skills --skill <name>)。無人で進める場合は、ツールのスキーマを確認して作業を進め、スキルが不足していることを明記してください。

必須の前提知識: scenario-roblox-expert(チャンネル、レビューの反復、ツールキット、現在のRoblox特有の落とし穴)。

基本方針(エキスパートとして押さえるべき点)

  1. セッションロックは重複を防ぎますが、UpdateAsyncだけでは防げません。 プレイヤーごとに1つの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. 2つのプロファイルをアトミックに保存することはできません。 yieldせずに両方を編集しても、メモリ内の保護にしかなりません(loleris)。両方の取引をエスクローに入れ、1つの取引ログレコードで結果を決定します[追加]。エスクローなしでは、2回の保存の間にクラッシュするとShieldが複製され、Swordが失われました。
  8. ストアを差し替えてテストします。 DataStoreServiceをMockDataStoreに差し替えられるようにデータコードを書き、変更していないProfileStoreを使って、複数のシミュレートされたサーバーを実行します[追加]。

まず確認すること

  • 公開済みで、Studio APIアクセスが有効ですか? 未公開の場合、DataStoreの呼び出しはすべてエラーになるため、モックを使ってテストします。公開済みでアクセスが有効な場合、Studioから本番キーに書き込まれるため、別のテスト用エクスペリエンス(Robloxのドキュメント)またはProfileStore.Mockを使用してください。
  • 何をどこに保存しますか? プレイヤーの状態は1つのプロファイルに保存します。ランキングには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. セッションを接続する。 目標:各プロフィールの所有者を1人に限定します。方法: 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} を使用し、古いバージョンごとにフィクスチャを用意します。ゲート条件:Reconcile の落とし穴と "newer" の拒否を含め、schema スイートが合格すること。
  2. 購入を付与する。 目標:各 PurchaseId に対して付与が正確に1回だけ行われるようにします。方法: Receipts.new{getProfile, isPresent, handlers} を使用し、1つのスクリプトで 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. テレポート。 目標:パーティー全員が一緒に到着するようにします。方法:グループには TeleportParty を使用して、 SafeTeleport.new{TeleportService} を作成します。ゲート条件:フェイクサービスに対して 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. 検証して報告する。 オフラインスイートを実行し、次に Studio Edit で rbx_run を通じて同じスイートを実行し、その後プレイテストのチェックを実行します。公開済みの場所が必要な項目を一覧にしてください。

数値

値対象
サーバーの読み取りまたは書き込み:1 分あたり 60 + プレイヤー数 x 40サーバーごと。デフォルト値。調整可能
経験値の読み取り:1 分あたり 300 + CCU x 40、書き込み:1 分あたり 300 + CCU x 20経験全体。Open Cloud と共有
UpdateAsync により、ProfileStore の保存ごとに読み取り 1 回と書き込み 1 回を消費両方の予算
キーごと:書き込みは 1 分あたり 4 MB、読み取りは 1 分あたり 25 MBリクエストごとに KB 単位で切り上げ
値は 4,194,304 文字、キー、名前、スコープは 50キーごと
リクエストの種類ごとにキューは 30 件。その後、301 から 306 のエラーが発生サーバーごと
GetAsync キャッシュは 4 秒データストアのインスタンスごと
キーごとに UTC 時間あたり 1 バージョンを保持し、上書き後 30 日間保持キーごと
ストレージは 500 MB + 生涯ユーザー数 x 1 MB圧縮された最新バージョン
ProfileStore の自動保存は 300 秒、ロック奪取は 40 秒、デッドと見なすまで 630 秒固定されたソース。デッド判定ではサーバー間で os.time() を比較
ProfileStore の初回再試行は 5 秒、その後は 10 秒ごと別のサーバーがロックを保持している間
プロファイルの読み込みが 5 秒を超えるバグとして扱う(loleris)
BindToClose の予算は 30 秒サーバーのシャットダウンごと
MessagingService のメッセージは 1 kB公開ごと

品質ゲート

  • run_offline.sh:すべての Lune スイートが合格し、rbx_check で kit、place、snippets のエラーがゼロであること。
  • run_live.py:Studio Edit で同じスイートが合格し、プレイテストのチェックも合格すること。
  • DataAudit:fixture 以外のエラー検出結果がゼロであること。
  • コンソール:未公開のプレイテストで「[ProfileStore]: Roblox API services unavailable」の行が 1 行表示され、保存エラーがないこと。
  • 公開済みテスト用エクスペリエンス(後日):2 つの実サーバー間で、5 秒未満でプレイヤーを引き継ぐこと。

よくある間違い

間違い症状修正
PlayerRemoving でロックせずに SetAsync を使うロールバック、サーバー移動後のアイテム重複PlayerData 経由で ProfileStore を使う
player.Name をキーにする名前変更後にデータが失われるPlayer_<UserId>
移行前に Reconcile する古いフィールドが取り残され、新しいフィールドが空になる最初に移行する
同じ書き込みを呼び出して再試行するカウンターが高くなりすぎる値に操作 ID を含め、KeyQueue を使う
保存前に PurchaseGranted にする支払い済みなのにクラッシュ後に付与されないReceipts
yield するハンドラー付与が二重に適用されるハンドラーはコピー上で実行し、yield してはいけません
取引として 2 つのプロフィールを保存するアイテムが重複または消失するエスクロー付きの Trade を使う
pcall の外で GetDataStore を呼び出すStudio の起動時にスクリプトエラーが発生する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 + プレイヤー数 x 10」と 6 秒のクールダウンは、どちらも廃止されています。
  • ストレージ制限は 2026-04-09 から適用されています。
  • HttpService:JSONEncode は 2026-03-10 以降、NaN と無限大をタグ付きオブジェクトとしてエンコードし、疎なエントリや型が混在するエントリを 黙って除外します(ライブテスト)。
  • ProfileService はサポートされていません。ProfileStore を使ってください。DataStore2 はレガシーです。
  • ReserveServerAsync は ReserveServer に代わるものです。
  • Studio の API アクセス:File > Experience Settings > Security > Enable Studio Access to API Services。 公開済みのゲームのみ対象です。
  • Studio の予算はサーバーの計算式に従いません。1 人でのプレイテストでは読み取りが 585 件でした。

参考資料

コメント