mcpField
R
roblox-datastores

roblox-datastores

github.com/sentinelcore開発29612 回インストール35 週間前に更新
リポジトリ

roblox-datastores について

サーバー上でプレイヤーデータを保存、読み込み、管理するRoblox開発者向けの`DataStoreService`リファレンススキルです。メソッドの説明や例に加え、信頼性、バージョン管理、データ損失への注意点を扱います。

主な機能

  1. 01DataStoreメソッドのリファレンス
  2. 02データの読み込みと保存の手引き
  3. 03再試行とエラー処理のパターン
  4. 04退出時と終了時の保存
  5. 05リーダーボード向けの順序付きストア
  6. 06バージョン管理と移行の手引き

活用シーン

  1. 01プレイヤーデータの読み込みと保存の実装
  2. 02ストレージ障害への対処と再試行
  3. 03サーバー終了時のセッションデータ保存
  4. 04スキーマ変更後のデータ移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sentinelcore/roblox-skills --skill roblox-datastores

インストール後、Claude Code で/roblox-datastoresを入力して読み込まれたか確認します。

詳細

roblox-datastores

Roblox の DataStoreService に関するリファレンス — サーバー上でのプレイヤーデータの保存、読み込み、管理について。

クイックリファレンス

メソッドシグネチャ備考
GetDataStoreDSS:GetDataStore(name, scope?)GlobalDataStore を返します
GetOrderedDataStoreDSS:GetOrderedDataStore(name, scope?)リーダーボード用
GetAsyncstore:GetAsync(key)値または nil を返します
SetAsyncstore:SetAsync(key, value)戻り値は不要です
UpdateAsyncstore:UpdateAsync(key, fn)アトミックな読み取り・変更・書き込み
RemoveAsyncstore:RemoveAsync(key)キーを削除し、古い値を返します
GetSortedAsyncorderedStore:GetSortedAsync(asc, pageSize)DataStorePages を返します

基本的なセットアップ

-- 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,
}

データの読み込み (GetAsync + pcall)

データストアの呼び出しは常に pcall でラップしてください。ネットワークの問題やレート制限により失敗する可能性があります。

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

データの保存 (SetAsync と UpdateAsync)

単純な上書きには SetAsync を使用します。値が現在保存されている値に基づく必要がある場合 (たとえば、複数のサーバーにまたがってカウンターを安全にインクリメントする場合) は UpdateAsync を使用します。

-- 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

リトライロジック

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
end

自動保存: PlayerRemoving + BindToClose

BindToClose を使わずにサーバーをシャットダウンすると、保存されていないデータは黙って破棄されます。

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)

Ordered DataStore (リーダーボード)

値は正の整数である必要があります。

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

データのバージョニング / マイグレーション

_version フィールドを含め、読み込みパスでマイグレーションを行います。

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
end

スキーマの破壊的変更には、バージョン付きのデータストア名 (PlayerData_v2) を使用してください。


よくある間違い

間違い結果修正方法
データストア呼び出しを pcall で囲まない未処理のエラーでスクリプトがクラッシュする常に pcall でラップする
Changed イベントごとに保存するレート制限に達する (60 + numPlayers×10 書き込み/分)スロットリングする。削除時 + 定期的な間隔で保存する
BindToClose ハンドラがないサーバーシャットダウン時にデータが失われるBindToClose ですべてのセッションを必ずフラッシュする
読み込み失敗時にデフォルトデータを返すプレイヤーが気づかないうちに進行状況を失う失敗時は nil を返す。キックするかリトライする
アトミックなカウンターに SetAsync を使う複数サーバー間で競合状態が発生する読み取り・変更・書き込みには UpdateAsync を使う
Instance や関数を保存するデータが黙って破棄される文字列、数値、真偽値、プレーンなテーブルのみを保存する
スキーマ変更後にデータストア名を再利用する古い形状が新しいコードと競合する破壊的変更時は名前に _v2、_v3 を付ける

コメント