除了使用Engine API访问数据存储(》)在工作室或实时服务器上外,您还可以使用开放云API访问和利用标准和订阅数据存储从外部脚本和工具与细粒度访问和安全控制。
使用
您可以通过使用这些 API 访问您的数据来改进工作流程的多个领域,例如:
客户支持门户 : 数据存储对于存储持续用户资源有用,例如用户库中的物品或技能点。有时您需要更新这些资源以支持客户协助。 rather than 使用工作室或手动加入体验,您可以构建一个 web 应用程序,以允许您的客服代理直接处理客户支持请求,例如查看和修改用户库存以及发布退款。
LiveOps 仪表板 : 您可以使用 API 构建 LiveOps 仪表板,以提高实时操作的效率。例如,你可以提前安排事件,将其隐藏在特性旗下作为数据存商店中的配置数据的一部分,并设置一个时间来翻转该旗以发布事件。您的体验服务器可以通过阅读旗帜来检测这个变化,启动事件。
外部排行榜 : 为了在 Roblox 之外促进您的经验,您可以在实时拉取您的经体验信息,例如比赛的排行榜,并在外部网站上显示它。您可以向订阅的数据存储授予只读访问权限,以便网站定期通过 HTTP 请求最新数据并更新网页。
数据迁移自动化 :数据可能会随着体验的发展而更改或更新,例如升级数据模型以容纳新功能。为了避免丢失现有用户数据,有时您需要将数据存储从旧模型迁移到新模型。您可以写一个外部脚本,从当前数据存储中阅读每个条目,将数据映射到新的模型,然后将条目写回新的数据存储来自动化数据迁移。
与引擎 API 的差异
虽然开放云 API 与 DataStoreService 相似,但存在一些关键差异:
宇宙ID和数据存储名称 : 与引擎API不同,开放云API是无状态的,可以来自任何地方,因此您必须始终提供 宇宙ID 、您体验的唯一标识符,以及数据存储 名称 ,当发送请求时。了解有关如何获取宇宙ID的更多信息,请参阅宇宙ID。
分开创建和更新权限 : 引擎 API 在你调用 DataStore:SetAsync() 时如果它们不存在,会创建新的条目,但创建和更新入口的开放云方法是分开的。在某些情况下,分离权限可能更安全且更灵活。例如,您可以创建一个客户支持工具,可以编辑现有用户的个人资料,但无法创建新用户的个人资料。
数据序列化 :所有开放云端端点都需要在网络传输之前将所有数据序列化。序列化意味着将对象转换为字符串。反序列化是相反的(将字符串转换为对象)。引擎 API 自动化地序列化和反序列化入口内容,但对于开放云,您需要拥有行生成或解析您的入口数据以 JSON 进行。
安全权限
数据存储经常存储敏感信息,例如用户资料和虚拟货币。为了维护安全,每个开放云 API 都有相应的必须添加到 API 密键的权限,例如列出 API 的 List Keys 权限。如果你没有添加必需的权限,你的 API 调用返回错误。对于每次操作所需的特定权限,请参阅 API 参考 标准 和 订阅数据存储 。
当配置您的 API 钥匙时,您可以为特定体验内的每个数据存储设置细粒度权限,例如阅已读、写入和列出入口,或者您可以向体验内的所有数据存储提供阅读或写入权限的钥匙。您还可以限制对工具所需的数据存储子集的访问,而不是暴露所有数据存储。这可以在您的密钥泄露的情况下减轻影响。
构建工具
您可以使用自己选择的语言来构建工具,使用 打开云端 API 为数据存储提供服务 来满足操作需求。以下例子通过使用标准数据存储和排序数据存储建立 用户库支持门户 的过程,使用订阅数据存储来构建 外部持久排行榜。
用户库存支持门户
本节提供了在 Python 中构建用户库存支持门户的具体示例,您可以在其中列出并阅读您用户道具的一部分,进行编辑,然后更新回体验的数据存商店。
对于这个例子,假设以关注中/正在关注内容:
存储用户库存的数据存储名称为 Inventory 。
每个数据入口的数据模型是 "userId": {"currency": number, "weapon": string, "level": number} 。钥匙只是 userId 。
Python 脚本列出基于前缀的用户库存子集,在促推广期间将其虚拟货币增加 10,并更新数据。
从高等级,您可以通过添加 API 钥匙权限然后添加脚本来构建 Python 应用程序。
为数据存储添加 API 钥匙权限
当 为此例创建 API 钥匙 时,请确保您执行以下设置:
从访问权限部分的 《选择 API 系统》菜单中选择 《宇宙数据存储》。
(可选) 在 数据存储 部分中,选择特定数据存储的 API 操作。
- 启用 特定数据存储操作 切换。默认情况下,五个数据存储自动加载,但您可以通过 + 添加数据存储到列表 按钮添加额外的数据存储。
- 选择数据存商店名称旁边的下拉箭头,然后选择您希望数据存储拥有的 API 操作。
选择整个体验的 API 操作。
- 点击 选择体验以添加 下拉列表,然后选择一个体验。
- 在 体验操作 中,单击下拉箭头并选择你想要添加到 API 的操作。这个例子选择 阅读入口 、 更新入口 和 列入口钥匙 对整个体验。
在 安全 部分,明确设置 IP 访问到钥匙使用 CIDR 记号,并设置明确的过期日期,以便您的钥匙在过期日期后自动停止工作。对于这个例子,如果你打算先进行本地测试,你可以将 IP 限制设置为 0.0.0.0/0 并让它在 30 天内过期 。
为用户库存支持门户添加脚本
创建了 API 钥匙与示例应用所需的权限后,您需要添加 JavaScript 代码来执行应用功能。该 文件显示了如何定义 , , 以及 方法。update_inventory.js 文件使用定义的方法来列出用户库的子集,增加每个用户的虚拟货币,并更新数据。
dataStoresMethods.js
const fetch = require('node-fetch');
class DataStores {
constructor() {
this._baseUrl =
'https://apis.roblox.com/datastores/v1/universes/{universeId}';
this._apiKey = process.env.API_KEY;
this._universeId = 'UNIVERSE_ID';
this.ATTR_HDR = 'Roblox-entry-Attributes';
this.USER_ID_HDR = 'Roblox-entry-UserIds';
this._objectsUrl = `${this._baseUrl}${this._universeId}/standard-datastores/datastore/entries/entry`;
this._incrementUrl = `${this._objectsUrl}/increment`;
this._listObjectsUrl = `${this._baseUrl}${this._universeId}/standard-datastores/datastore/entries`;
}
async _getHeaders() {
return { 'x-api-key': this._apiKey };
}
async getEntry(datastore, objectKey, scope = null) {
const url = `${
this._objectsUrl
}?datastoreName=${datastore}&entryKey=${objectKey}&scope=${scope || ''}`;
const response = await fetch(url, { headers: await this._getHeaders() });
const data = await response.json();
return data;
}
async listEntries(datastore, scope = null, prefix = '', limit = 100) {
const url = `${this._listObjectsUrl}?datastoreName=${datastore}&scope=${scope}&prefix=${prefix}&limit=${limit}`;
const response = await fetch(url, { headers: await this._getHeaders() });
const data = await response.json();
return data;
}
async incrementEntry(datastore, objectKey, incrementBy, scope = null) {
const url = `${
this._incrementUrl
}?datastoreName=${datastore}&entryKey=${objectKey}&incrementBy=${incrementBy}&scope=${
scope || ''
}`;
const response = await fetch(url, {
method: 'POST',
headers: await this._getHeaders(),
});
const data = await response.json();
return data;
}
}
module.exports = DataStores;
updateInventory.js
const DataStores = require('./dataStoresMethods');
const dataStoresApi = new DataStores();
// Set up
const datastoreName = 'Inventory';
// List keys for a subset of users
dataStoresApi.listEntries(datastoreName).then((keys) => {
console.log(keys);
});
// Read inventory for each user
for (let x = 0; x < 5; x++) {
const updatedObjectKey = `User_${x + 1}`;
dataStoresApi.getEntry(datastoreName, updatedObjectKey).then((value) => {
console.log(
`${updatedObjectKey} has ${value.gems} gems in their inventory`
);
});
}
// Update the currency of each user by 10
for (let x = 0; x < 5; x++) {
const updatedObjectKey = `User_${x + 1}`;
dataStoresApi
.incrementEntry(datastoreName, updatedObjectKey, 10)
.then((value) => {
console.log(
`${updatedObjectKey} now has ${value.robux} robux in their inventory`
);
});
}
要测试,设置 API_KEY 环境变量并运行 JavaScript 文件:
export API_KEY=... \node updateInventory.js
外部永久排行榜
本节将介绍在 Python 中创建外部永久排行榜的具体示例,在其中您可以列出并阅读您在订序数据存储中存储的用户信息,进行编辑,然后发布到外部网站进行推广进。
对于这个例子,假设以关注中/正在关注内容:
代码示例创建了一个预定义的用户名列表用于演示目的。对于自己的应用程序,您应该使用体验中的实际用户列表。
代码示例向每个加入体验的用户添加 50 分,用于演示目的。对于自己的应用程序,您应该根据经验设计来定义规则。
从高等级,您可以通过添加 API 钥匙权限然后添加脚本来构建 Python 应用程序。
为订购的数据存储添加 API 钥匙权限
示例应用需要四个方法来实现其功能:List , Create , Update 和 Increment ,因此您需要添加以下 API 钥匙权限:
- 阅读 对于 list 方法。
- 写入 对于 Create、Update 和 Increment 方法。
当 为此例创建 API 钥匙 时,请确保您执行以下操作:
从 选择 API 系统 菜单中的 访问权限 部分选择 排序数据存储 。
向 API 操作添加权限以读写目标体验中的排序数据存储。
- 单击 选择体验以添加 下拉列表,然后选择目标体验。
- 在 体验操作 中,扩展下拉列表以选择 阅读 和 写入 操作。
在 安全 部分,明确设置 IP 访问到钥匙使用 CIDR 记号,并设置明确的过期日期,以便您的钥匙在过期日期后自动停止工作。对于这个例子,如果你打算先进行本地测试,你可以将 IP 限制设置为 0.0.0.0/0 并让它在 30 天内过期 。
为排行榜添加脚本
创建了 API 钥匙与示例App用所需的权限后,您需要添加 JavaScript 代码来执行应用功能。
该 文件显示了如何定义 , , , 以及 方法。leaderboard.js 文件使用定义的方法来创建排序数据存储中的用户入口、显示得分、增加胜利用户的得分和更新排行榜。该 leaderboard.js 文件还导入配置宇宙ID、API域和您的API密键的config.json 文件。
ordered_data_stores.js
const axios = require('axios');
const fs = require('fs');
class DataStores {
constructor(configFile) {
this._config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
}
_H() {
return {
'x-api-key': this._config.api_key,
'Content-Type': 'application/json',
};
}
async list(
datastore,
scope,
pageSize = 10,
orderBy = '',
filter = '',
exclusiveStartKey = ''
) {
const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries`;
const response = await axios.get(url, {
headers: this._H(),
params: {
max_page_size: pageSize,
order_by: orderBy,
filter: filter,
page_token: exclusiveStartKey,
},
});
return response.data;
}
async create(datastore, scope, entry, data) {
const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries`;
const payload = JSON.stringify({ value: 11 });
const response = await axios.post(url, payload, {
headers: this._H(),
params: { id: entry },
});
return response.data;
}
async increment(datastore, scope, entry, incrementBy) {
const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries/${entry}:increment`;
const payload = JSON.stringify({ amount: incrementBy });
const response = await axios.post(url, payload, { headers: this._H() });
return response.data;
}
}
leaderboard.js
const leaderboardEndpoints = require('./ordered_data_stores');
const datastores = new leaderboardEndpoints.DataStores('config.json');
// Variables
const orderedDataStore = 'PlayerScores';
const scope = 'global';
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
// Create an entry and give each new player 50 points for joining the game
entryNames.forEach(async (name) => {
await datastores.create(orderedDataStore, scope, name, 50);
});
// Display the players' scores
datastores.list(orderedDataStore, scope).then((playerScores) => {
console.log(playerScores);
});
// Increment the first player's score for winning the game
datastores.increment(orderedDataStore, scope, entryNames[0], 100);
// Increment all the players' scores for participating in the game
entryNames.forEach(async (name) => {
await datastores.increment(orderedDataStore, scope, name, 10);
});
// Display the updated leaderboard
datastores.list(orderedDataStore, scope).then((updatedPlayerScores) => {
console.log(updatedPlayerScores);
});
配置
{
"universe_id": "",
"api_key_url": "https://apis.roblox.com/datastores/ordered-v1/”,
"api_key": ""
}
要测试,设置 API_KEY 环境变量并运行 leaderboard.js 文件:
export API_KEY=... \node leaderboard.js
完成测试后,您可以将排行榜发布或嵌入到 Roblox 之外的网站上,以获得更多曝光度。