Oprócz uzyskiwania dostępu do magazynów danych z API silnika w Studio lub w grach na żywo (DataStoreService), możesz używać interfejsów API otwartej chmury do uzyskiwania dostępu do standardowych i uporządkowanych magazynów danych z zewnętrznych skryptów i innych narzędzi.
Dostęp do magazynów danych w chmurze otwiera wiele potencjalnych zastosowań, w tym:
- Portal wsparcia klienta, który pozwala Twojemu zespołowi bezpośrednio obsługiwać zgłoszenia wsparcia, takie jak modyfikowanie inwentarzy użytkowników lub wydawanie zwrotów
- Globalne rankingi, które możesz wyświetlać na zewnętrznej stronie internetowej
- Aktualizacje schematu za pomocą skryptów, które odczytują wpisy z bieżącego magazynu danych, mapują je do nowego schematu i zapisują wpisy z powrotem do nowego magazynu danych
Przykłady na tej stronie pokazują, jak zbudować portal wsparcia inwentarza użytkownika oraz zewnętrzny ranking za pomocą Node.js i Pythona, ale użyj dowolnego języka, który preferujesz; interfejsy API otwartej chmury obsługują każdy język programowania, który może wysyłać żądanie HTTP.
Różnice w stosunku do API silnika
Chociaż interfejsy API otwartej chmury uzyskują dostęp do tych samych podstawowych magazynów danych i są podobne do pracy z DataStoreService, istnieje kilka kluczowych różnic:
ID uniwersum: W przeciwieństwie do API silnika, interfejsy API otwartej chmury są stateless i mogą pochodzić z dowolnego miejsca, więc zawsze musisz podać ID uniwersum, unikalny identyfikator Twojej gry.
Osobne uprawnienia do tworzenia i aktualizacji: API silnika tworzy nowe wpisy, jeśli nie istnieją, gdy wywołujesz DataStore:SetAsync(), ale metody otwartej chmury do tworzenia i aktualizacji wpisów są osobne. Oddzielne uprawnienia mogą być bezpieczniejsze i bardziej elastyczne w pewnych sytuacjach. Na przykład, możesz chcieć, aby Twoje narzędzie wsparcia klienta mogło tylko edytować profil istniejącego użytkownika, a nie tworzyć nowego.
Serializacja danych: Wszystkie punkty końcowe otwartej chmury wymagają, abyś serializował dane przed ich wysłaniem. Serializacja oznacza konwertowanie obiektu na ciąg znaków. Deserializacja to przeciwieństwo, konwertowanie ciągu znaków na obiekt. API silnika automatycznie serializuje i deserializuje zawartość wpisów, ale w przypadku otwartej chmury musisz samodzielnie generować lub analizować swój wpis w formacie JSON.
Uprawnienia
Magazyny danych często przechowują wrażliwe informacje, takie jak profile użytkowników i waluta wirtualna. Aby zachować bezpieczeństwo, każda metoda otwartej chmury ma odpowiadające jej uprawnienia, zwane zakresami, które musisz dodać do swojego klucza API, takie jak universe-datastores.control:list dla metody Lista magazynów danych. Jeśli nie dodasz wymaganych uprawnień, Twoje wywołanie API zwróci błąd. Zobacz dokumentację referencyjną, aby uzyskać wymagane zakresy dla każdego punktu końcowego.
Aby uzyskać więcej informacji na temat zarządzania uprawnieniami, zobacz Zarządzaj kluczami API.
Portal wsparcia inwentarza użytkownika
Ten przykład używa magazynu danych o nazwie Inventory oraz schematu dla każdego wpisu "userId": {"currency": number, "weapon": string, "level": number}. Klucz to userId.
Wymagane zakresy
Gdy tworzysz klucz API dla tego przykładu, dodaj następujące zakresy do swojego klucza:
- universe-datastores.objects:list
- universe-datastores.objects:read
- universe-datastores.objects:update
Opcjonalnie, dodaj uprawnienia tylko dla magazynu danych Inventory, ustaw ograniczenie adresu IP i datę wygaśnięcia.
Dodaj skrypty do portalu wsparcia inwentarza użytkownika
Po utworzeniu klucza API z wymaganymi uprawnieniami do przykładowej aplikacji, możesz utworzyć skrypt, aby wysyłać żądania do punktów końcowych. Te skrypty pobierają pierwsze 10 wpisów w magazynie danych, zwiększają wartość currency dla każdego o 10, a następnie aktualizują każdy wpis. W przypadku większego magazynu danych musiałbyś zająć się paginacją przy użyciu parametrów zapytania maxPageSize oraz pageToken.
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('Zmienna środowiskowa API_KEY nie jest ustawiona.');
}
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) // Ciało musi być ciągiem
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\nPrzetwarzanie wpisu: ${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(`Odpowiedź: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('Wystąpił błąd podczas wykonywania:', error);
}
})();Aby przetestować, ustaw zmienną środowiskową API_KEY, zainstaluj zależności i uruchom skrypt:
export API_KEY=<your_key>
node incrementCurrency.jsZewnętrzny, trwały ranking
Ten przykład tworzy wstępnie zdefiniowaną listę użytkowników do celów demonstracyjnych, ale aby był użyteczny w rzeczywistej grze, musiałbyś mieć rzeczywisty magazyn danych użytkowników.
Wymagane zakresy
Gdy tworzysz klucz API dla tego przykładu, dodaj następujące zakresy do swojego klucza:
- universe.ordered-data-store.scope.entry:read
- universe.ordered-data-store.scope.entry:write
Dodaj skrypty do rankingu
Po utworzeniu klucza API z wymaganymi uprawnieniami do przykładowej aplikacji możesz utworzyć skrypt, aby wysyłać żądania do punktów końcowych. Te skrypty dodają kilka przykładowych wpisów do uporządkowanego magazynu danych z losowymi liczbami, a następnie odzyskują je od najwyższej do najniższej wartości. W przypadku większego magazynu danych musiałbyś zająć się paginacją przy użyciu parametrów zapytania maxPageSize oraz pageToken.
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(`Błąd 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('Błąd: Zmienna środowiskowa API_KEY nie jest ustawiona.');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('Tworzenie przykładowych danych...');
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(`Nie udało się utworzyć wpisu dla "${name}": ${error.message}`);
}
}
console.log('\nPobieranie posortowanej listy wpisów...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`Nie udało się wylistać wpisów: ${error.message}`);
}
}
main();Aby przetestować, ustaw zmienną środowiskową API_KEY, zainstaluj zależności i uruchom skrypt:
export API_KEY=<your_key>
node leaderboard.js