オープンクラウドデータストア

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

スタジオやライブゲームのエンジンAPI(DataStoreService)からデータストアにアクセスすることに加えて、オープンクラウドAPIを使用して外部スクリプトや他のツールから標準および順序付きデータストアにアクセスできます。

オープンクラウドへのデータストアのアクセスは、多くの潜在的なユースケースを解放します。これには以下が含まれます:

  • ユーザーのインベントリを変更したり、返金を発行したりするなど、サポートリクエストを直接処理できるカスタマーサポートポータル
  • 外部ウェブサイトに表示できるグローバルリーダーボード
  • 現在のデータストアからエントリを読み取り、新しいスキーマにマッピングし、新しいデータストアにエントリを書き戻すスクリプトによるスキーマの更新

このページの例では、Node.jsとPythonを使用してユーザーインベントリサポートポータル外部リーダーボードを構築する方法を示しますが、好みの言語を使用できます。オープンクラウドAPIは、HTTPリクエストを送信できる任意のプログラミング言語をサポートしています。

エンジンAPIとの違い

オープンクラウドAPIは同じ基盤のデータストアにアクセスし、DataStoreServiceでの作業に似ていますが、いくつかの重要な違いがあります:

  • ユニバースID:エンジンAPIとは異なり、オープンクラウドAPIはステートレスであり、どこからでもアクセスできるため、常にゲームの一意の識別子であるユニバースIDを提供する必要があります。

  • 作成と更新のための別々の権限:エンジンAPIは、DataStore:SetAsync()を呼び出すときにエントリが存在しない場合、新しいエントリを作成しますが、オープンクラウドのエントリの作成と更新のメソッドは別々です。別々の権限は、特定の状況でより安全で柔軟です。たとえば、カスタマーサポートツールが新しいユーザープロフィールを作成するのではなく、既存のユーザープロフィールのみを編集できるようにしたい場合があります。

  • データのシリアル化:すべてのオープンクラウドエンドポイントは、データを送信する前にシリアル化する必要があります。シリアル化とは、オブジェクトを文字列に変換することを意味します。逆に、デシリアル化は文字列をオブジェクトに変換することです。エンジンAPIはエントリの内容を自動的にシリアル化およびデシリアル化しますが、オープンクラウドでは、エントリをJSONに生成または解析する必要があります。

非有限数

エンジンAPIを通じて書き込まれたデータストアエントリは、非有限のLuau数を含むことができます。JSONはこれらの数を表現できないため、オープンクラウドのレスポンスは、返されたエントリ値のタグ付きJSONオブジェクトで置き換えます:

  • 正の無限大(inf): {"m": null, "t": "numeric", "v": "inf"}
  • 負の無限大(-inf): {"m": null, "t": "numeric", "v": "-inf"}
  • NaN(nan): {"m": null, "t": "numeric", "v": "nan"}

権限

データストアは、ユーザープロフィールや仮想通貨などの機密情報を保存することがよくあります。セキュリティを維持するために、各オープンクラウドメソッドには、APIキーに追加する必要がある対応する権限(スコープ)があります。たとえば、データストアの一覧メソッドのためのuniverse-datastores.control:listスコープです。必要な権限を追加しないと、API呼び出しはエラーを返します。各エンドポイントに必要なスコープについては、リファレンスドキュメントを参照してください。

権限の管理に関する詳細は、APIキーの管理を参照してください。

ユーザーインベントリサポートポータル

この例では、Inventoryというデータストアと、各エントリのスキーマとして"userId": {"currency": number, "weapon": string, "level": number}を使用します。キーはuserIdです。

必要なスコープ

この例のためにAPIキーを作成する際には、次のスコープをキーに追加してください:

  • universe-datastores.objects:list
  • universe-datastores.objects:read
  • universe-datastores.objects:update

オプションで、Inventoryデータストアのみに対する権限を追加し、IPアドレス制限を設定し、期限日を設定します。

ユーザーインベントリサポートポータルのためのスクリプトを追加

例のアプリに必要な権限を持つAPIキーを作成した後、エンドポイントにリクエストを送信するスクリプトを作成できます。これらのスクリプトは、データストア内の最初の10エントリを取得し、それぞれのcurrency値を10増加させてから、各エントリを更新します。より大きなデータストアの場合、maxPageSizeおよびpageTokenクエリパラメータを使用してページネーションに対処する必要があります。

incrementCurrency.js
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('API_KEY環境変数が設定されていません。');
}
const apiHeaderKey = 'x-api-key';
const universeId = '';
const dataStoreId = 'Inventory';
const baseUrl = 'https://apis.roblox.com/cloud/v2/';
async function listEntries(universe, dataStore) {
const listPath = `universes/${universe}/data-stores/${dataStore}/entries`;
const url = baseUrl + listPath;
const response = await fetch(url, {
headers: { [apiHeaderKey]: apiKey }
});
return response.json();
}
async function getEntry(path) {
const url = baseUrl + path;
const response = await fetch(url, {
headers: { [apiHeaderKey]: apiKey }
});
return response.json();
}
async function updateEntry(path, payload) {
const url = baseUrl + path;
const response = await fetch(url, {
method: 'PATCH',
headers: {
[apiHeaderKey]: apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload) // 本文は文字列でなければなりません
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\nエントリを処理中: ${path}`);
const currentData = await getEntry(path);
currentData.value.currency += 10;
const payload = { value: currentData.value };
const updateResponse = await updateEntry(path, payload);
console.log(`ステータス: ${updateResponse.status}`);
console.log(`レスポンス: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('実行中にエラーが発生しました:', error);
}
})();

テストするには、API_KEY環境変数を設定し、依存関係をインストールしてからスクリプトを実行します:

export API_KEY=<your_key>
node incrementCurrency.js

外部永続リーダーボード

この例では、デモ目的のためにユーザーの事前定義リストを作成しますが、実際のゲームで役立つためには、実際のユーザーデータストアが必要です。

必要なスコープ

この例のためにAPIキーを作成する際には、次のスコープをキーに追加してください:

  • universe.ordered-data-store.scope.entry:read
  • universe.ordered-data-store.scope.entry:write

リーダーボードのためのスクリプトを追加

例のアプリに必要な権限を持つAPIキーを作成した後、エンドポイントにリクエストを送信するスクリプトを作成できます。これらのスクリプトは、ランダムな数値を持つサンプルエントリを順序付きデータストアに追加し、最高から最低の値まで取得します。より大きなデータストアの場合、maxPageSizeおよびpageTokenクエリパラメータを使用してページネーションに対処する必要があります。

leaderboard.js
const apiKey = process.env.API_KEY;
const apiHeaderKey = 'x-api-key';
const universeId = '';
const orderedDataStoreId = 'PlayerScores';
const scopeId = 'global';
const baseUrl = 'https://apis.roblox.com/cloud/v2/';
async function createOrderedEntry(universe, orderedDataStore, entryId, payload) {
const createPath = `universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries`;
const url = new URL(baseUrl + createPath);
url.searchParams.append('id', entryId);
const response = await fetch(url, {
method: 'POST',
headers: {
[apiHeaderKey]: apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`APIエラー (${response.status}): ${errorText}`);
}
return response.text();
}
async function listOrderedEntries(universe, orderedDataStore) {
const listPath = `universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries`;
const url = new URL(baseUrl + listPath);
url.searchParams.append('orderBy', 'value desc');
return fetch(url, {
headers: {
[apiHeaderKey]: apiKey,
},
});
}
async function main() {
if (!apiKey) {
console.error('エラー: API_KEY環境変数が設定されていません。');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('サンプルデータを作成中...');
for (const name of entryNames) {
try {
const randomValue = Math.floor(Math.random() * 50) + 1;
const payload = { value: randomValue };
const responseText = await createOrderedEntry(universeId, orderedDataStoreId, name, payload);
console.log(responseText);
} catch (error) {
console.error(`"${name}"のエントリを作成できませんでした: ${error.message}`);
}
}
console.log('\nエントリのソートされたリストを取得中...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`エントリのリストを取得できませんでした: ${error.message}`);
}
}
main();

テストするには、API_KEY環境変数を設定し、依存関係をインストールしてからスクリプトを実行します:

export API_KEY=<your_key>
node leaderboard.js
©2026 Roblox Corporation。Roblox(ロブロックス)、RobloxロゴおよびPowering Imaginationは、米国並びにその他の国における登録商標および非登録商標です。