Armazenamentos de dados na Nuvem Aberta

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Além de acessar os armazenamentos de dados pela Engine API no Studio ou em jogos ao vivo (DataStoreService), você pode usar as APIs da Nuvem Aberta para acessar armazenamentos de dados padrão e armazenamentos de dados ordenados a partir de scripts externos e outras ferramentas.

O acesso da Nuvem Aberta aos seus armazenamentos de dados desbloqueia muitos casos de uso potenciais, incluindo:

  • Um portal de suporte ao cliente que permite que sua equipe lidere diretamente com solicitações de suporte, como modificar inventários de usuários ou emitir reembolsos
  • Classificações globais que você pode exibir em um site externo
  • Atualizações de esquema com scripts que leem entradas do armazenamento de dados atual, mapeiam para o novo esquema e gravam entradas de volta em um novo armazenamento de dados

Os exemplos nesta página demonstram como construir um portal de suporte ao inventário de usuários e uma classificação externa persistente com Node.js e Python, mas use a linguagem que você preferir; as APIs da Nuvem Aberta suportam qualquer linguagem de programação que possa enviar uma solicitação HTTP.

Diferenças em relação à Engine API

Embora as APIs da Nuvem Aberta acessem os mesmos armazenamentos de dados subjacentes e sejam semelhantes ao trabalho com DataStoreService, existem algumas diferenças principais:

  • ID do universo: Ao contrário da Engine API, as APIs da Nuvem Aberta são sem estado e podem vir de qualquer lugar, portanto, você deve sempre fornecer o ID do universo, o identificador exclusivo do seu jogo.

  • Permissões separadas para criar e atualizar: A Engine API cria novas entradas se elas não existirem quando você chama DataStore:SetAsync(), mas os métodos da Nuvem Aberta para criar e atualizar entradas são separados. Permissões separadas podem ser mais seguras e flexíveis em certas situações. Por exemplo, você pode querer que sua ferramenta de suporte ao cliente possa apenas editar o perfil de um usuário existente, não criar um novo.

  • Serialização de dados: Todos os pontos finais da Nuvem Aberta exigem que você serialize os dados antes de enviá-los. Serialização significa converter um objeto em uma string. Desserialização é o oposto, convertendo uma string em um objeto. A Engine API serializa e desserializa o conteúdo das entradas automaticamente, mas para a Nuvem Aberta, você deve gerar ou analisar sua entrada para e a partir de JSON por conta própria.

Permissões

Os armazenamentos de dados costumam armazenar informações sensíveis, como perfis de usuários e moeda virtual. Para manter a segurança, cada método da Nuvem Aberta tem permissões correspondentes, chamadas escopos, que você deve adicionar à sua chave da API, como o escopo universe-datastores.control:list para o método Listar Armazenamentos de Dados. Se você não adicionar as permissões necessárias, sua chamada da API retornará um erro. Consulte a documentação de referência para os escopos necessários para cada endpoint.

Para mais informações sobre como gerenciar permissões, veja Gerenciar chaves da API.

Portal de suporte ao inventário de usuários

Este exemplo usa um armazenamento de dados chamado Inventory e um esquema para cada entrada de "userId": {"currency": number, "weapon": string, "level": number}. A chave é userId.

Escopos necessários

Ao criar uma chave da API para este exemplo, adicione os seguintes escopos à sua chave:

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

Opcionalmente, adicione as permissões apenas para o armazenamento de dados Inventory, defina uma restrição de endereço IP e defina uma data de expiração.

Adicionar scripts para o portal de suporte ao inventário de usuários

Após criar a chave da API com as permissões necessárias para o aplicativo de exemplo, você pode criar um script para fazer solicitações aos endpoints. Esses scripts obtêm as 10 primeiras entradas no armazenamento de dados, incrementam o valor de currency para cada uma em 10 e, em seguida, atualizam cada entrada. Para um armazenamento de dados maior, você precisaria lidar com paginação usando os parâmetros de consulta maxPageSize e pageToken.

incrementCurrency.js

const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('A variável de ambiente API_KEY não está definida.');
}
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) // O corpo deve ser uma string
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\nProcessando entrada: ${path}`);
const currentData = await getEntry(path);
currentData.value.currency += 10;
const payload = { value: currentData.value };
const updateResponse = await updateEntry(path, payload);
console.log(`Status: ${updateResponse.status}`);
console.log(`Resposta: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('Ocorreu um erro durante a execução:', error);
}
})();

Para testar, defina a variável de ambiente API_KEY, instale as dependências e execute o script:


export API_KEY=<your_key>
node incrementCurrency.js

Classificação externa persistente

Este exemplo cria uma lista pré-definida de usuários para fins de demonstração, mas para que seja útil em um jogo real, você precisaria de um armazenamento de dados real de usuários.

Escopos necessários

Ao criar uma chave da API para este exemplo, adicione os seguintes escopos à sua chave:

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

Adicionar scripts para a classificação

Após criar a chave da API com as permissões necessárias para o aplicativo de exemplo, você pode criar um script para fazer solicitações aos endpoints. Esses scripts adicionam algumas entradas de amostra ao armazenamento de dados ordenado com números aleatórios e, em seguida, recuperam-nas do maior para o menor valor. Para um armazenamento de dados maior, você precisaria lidar com paginação usando os parâmetros de consulta maxPageSize e 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(`Erro da 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('Erro: A variável de ambiente API_KEY não está definida.');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('Criando dados de amostra...');
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(`Falha ao criar entrada para "${name}": ${error.message}`);
}
}
console.log('\nObtendo lista de entradas ordenadas...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`Falha ao listar entradas: ${error.message}`);
}
}
main();

Para testar, defina a variável de ambiente API_KEY, instale as dependências e execute o script:


export API_KEY=<your_key>
node leaderboard.js
©2026 Roblox Corporation, Roblox, o logotipo Roblox e Powering Imagination estão entre nossas marcas registradas e não registradas nos EUA e em outros países.