Almacenes de datos de Open Cloud

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Además de acceder a los almacenes de datos desde la API del Motor en Studio o en juegos en vivo (DataStoreService), puedes usar las API de Open Cloud para acceder a almacenes de datos estándar y almacenes de datos ordenados desde scripts externos y otras herramientas.

El acceso de Open Cloud a tus almacenes de datos desbloquea muchos casos de uso potenciales, incluyendo:

  • Un portal de soporte al cliente que permite a tu equipo manejar directamente las solicitudes de soporte, como modificar inventarios de usuarios o emitir reembolsos
  • Tableros de líderes globales que puedes mostrar en un sitio web externo
  • Actualizaciones de esquema con scripts que leen entradas del almacén de datos actual, las mapean al nuevo esquema y escriben entradas de nuevo en un nuevo almacén de datos

Los ejemplos en esta página demuestran cómo construir un portal de soporte de inventario de usuario y un tablero de líderes externo con Node.js y Python, pero usa el lenguaje que prefieras; las API de Open Cloud son compatibles con cualquier lenguaje de programación que pueda enviar una solicitud HTTP.

Diferencias con la API del Motor

Aunque las API de Open Cloud acceden a los mismos almacenes de datos subyacentes y son similares a trabajar con DataStoreService, hay algunas diferencias clave:

  • ID de universo: A diferencia de la API del Motor, las API de Open Cloud son sin estado y pueden provenir de cualquier lugar, por lo que siempre debes proporcionar el ID de universo, el identificador único de tu juego.

  • Permisos separados para crear y actualizar: La API del Motor crea nuevas entradas si no existen cuando llamas a DataStore:SetAsync(), pero los métodos de Open Cloud para crear y actualizar entradas son separados. Los permisos separados pueden ser más seguros y flexibles en ciertas situaciones. Por ejemplo, podrías querer que tu herramienta de soporte al cliente solo pueda editar el perfil de un usuario existente, no crear uno nuevo.

  • Serialización de datos: Todos los puntos finales de Open Cloud requieren que serialices los datos antes de enviarlos. La serialización significa convertir un objeto en una cadena. La deserialización es lo opuesto, convertir una cadena en un objeto. La API del Motor serializa y deserializa el contenido de las entradas automáticamente, pero para Open Cloud, debes generar o analizar tu entrada hacia y desde JSON por tu cuenta.

Números no finitos

Las entradas de almacén de datos escritas a través de la API del Motor pueden contener números Luau no finitos. Debido a que JSON no puede representar estos números, las respuestas de Open Cloud los reemplazan con objetos JSON etiquetados en el valor de entrada devuelto:

  • Infinito positivo (inf): {"m": null, "t": "numeric", "v": "inf"}
  • Infinito negativo (-inf): {"m": null, "t": "numeric", "v": "-inf"}
  • NaN (nan): {"m": null, "t": "numeric", "v": "nan"}

Permisos

Los almacenes de datos a menudo almacenan información sensible, como perfiles de usuario y moneda virtual. Para mantener la seguridad, cada método de Open Cloud tiene permisos correspondientes, llamados alcances, que debes agregar a tu clave API, como el alcance universe-datastores.control:list para el método Listar Almacenes de Datos. Si no agregas los permisos requeridos, tu llamada a la API devuelve un error. Consulta la documentación de referencia para los alcances requeridos para cada punto final.

Para obtener más información sobre la gestión de permisos, consulta Gestionar claves API.

Portal de soporte de inventario de usuario

Este ejemplo utiliza un almacén de datos llamado Inventory y un esquema para cada entrada de "userId": {"currency": number, "weapon": string, "level": number}. La clave es userId.

Alcances requeridos

Al crear una clave API para este ejemplo, agrega los siguientes alcances a tu clave:

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

Opcionalmente, agrega los permisos solo para el almacén de datos Inventory, establece una restricción de dirección IP y establece una fecha de expiración.

Agregar scripts para el portal de soporte de inventario de usuario

Después de crear la clave API con los permisos requeridos para la aplicación de ejemplo, puedes crear un script para hacer solicitudes a los puntos finales. Estos scripts obtienen las primeras 10 entradas en el almacén de datos, incrementan el valor de currency para cada una en 10 y luego actualizan cada entrada. Para un almacén de datos más grande, necesitarías lidiar con paginación usando los parámetros de consulta maxPageSize y pageToken.

incrementCurrency.js
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('La variable de entorno API_KEY no está configurada.');
}
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) // El cuerpo debe ser una cadena
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\nProcesando entrada: ${path}`);
const currentData = await getEntry(path);
currentData.value.currency += 10;
const payload = { value: currentData.value };
const updateResponse = await updateEntry(path, payload);
console.log(`Estado: ${updateResponse.status}`);
console.log(`Respuesta: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('Ocurrió un error durante la ejecución:', error);
}
})();

Para probar, establece la variable de entorno API_KEY, instala las dependencias y ejecuta el script:

export API_KEY=<tu_clave>
node incrementCurrency.js

Tablero de líderes externo persistente

Este ejemplo crea una lista predefinida de usuarios para fines de demostración, pero para que sea útil en un juego real, necesitarías un almacén de datos real de usuarios.

Alcances requeridos

Al crear una clave API para este ejemplo, agrega los siguientes alcances a tu clave:

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

Agregar scripts para el tablero de líderes

Después de crear la clave API con los permisos requeridos para la aplicación de ejemplo, puedes crear un script para hacer solicitudes a los puntos finales. Estos scripts agregan algunas entradas de muestra al almacén de datos ordenado con números aleatorios y luego las recuperan de mayor a menor valor. Para un almacén de datos más grande, necesitarías lidiar con paginación usando los parámetros de consulta maxPageSize y 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(`Error de 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('Error: La variable de entorno API_KEY no está configurada.');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('Creando datos de muestra...');
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(`No se pudo crear la entrada para "${name}": ${error.message}`);
}
}
console.log('\nObteniendo lista ordenada de entradas...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`No se pudo listar las entradas: ${error.message}`);
}
}
main();

Para probar, establece la variable de entorno API_KEY, instala las dependencias y ejecuta el script:

export API_KEY=<tu_clave>
node leaderboard.js
©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.