นอกเหนือจากการเข้าถึงที่เก็บข้อมูลจาก Engine API ใน Studio หรือเกมที่ดำเนินการอยู่ (DataStoreService) แล้ว คุณยังสามารถใช้ Open Cloud APIs ในการเข้าถึง ที่เก็บข้อมูลมาตรฐาน และ ที่เก็บข้อมูลที่เรียงลำดับ จากสคริปต์ภายนอกและเครื่องมืออื่น ๆ ได้อีกด้วย
การเข้าถึง Open Cloud ไปยังที่เก็บข้อมูลของคุณเปิดโอกาสให้มีการใช้ในกรณีที่เป็นไปได้มากมาย รวมถึง:
- พอร์ทัลสนับสนุนลูกค้าที่ให้ทีมของคุณจัดการคำขอการสนับสนุนโดยตรง เช่น การแก้ไขสินค้าคงคลังของผู้ใช้หรือการออกเงินคืน
- กระดานผู้นำระดับโลกที่คุณสามารถแสดงบนเว็บไซต์ภายนอก
- การอัปเดตสคีมาด้วยสคริปต์ที่อ่านรายการจากที่เก็บข้อมูลปัจจุบัน ทำการแมพไปยังสคีมาใหม่ และเขียนรายการกลับไปยังที่เก็บข้อมูลใหม่
ตัวอย่างในหน้านี้จะแสดงให้เห็นวิธีการสร้าง พอร์ทัลสนับสนุนสินค้าคงคลังของผู้ใช้ และ กระดานผู้นำภายนอก โดยใช้ Node.js และ Python แต่คุณสามารถใช้ภาษาที่คุณชอบ; Open Cloud APIs รองรับภาษาโปรแกรมใด ๆ ที่สามารถส่งคำขอ HTTP ได้
ความแตกต่างจาก Engine API
แม้ว่า Open Cloud APIs จะเข้าถึงที่เก็บข้อมูลพื้นฐานเดียวกันและมีความคล้ายคลึงกับการทำงานกับ DataStoreService แต่ก็มีความแตกต่างหลักๆ อยู่บ้าง:
ID ของจักรวาล: แตกต่างจาก Engine API, Open Cloud APIs เป็น stateless และสามารถมาจากที่ใดก็ได้ ดังนั้นคุณต้องให้ ID ของจักรวาล ซึ่งเป็นตัวระบุที่ไม่ซ้ำกันของเกมของคุณ
สิทธิ์ที่แยกสำหรับการสร้างและการอัปเดต: Engine API จะสร้างรายการใหม่หากไม่มีอยู่เมื่อคุณเรียก DataStore:SetAsync() แต่ Open Cloud จะมีวิธีการสำหรับการสร้างและการอัปเดตรายการที่แยกจากกัน สิทธิ์ที่แยกอาจปลอดภัยกว่าและยืดหยุ่นกว่าในบางสถานการณ์ ตัวอย่างเช่น คุณอาจต้องการให้เครื่องมือสนับสนุนลูกค้าของคุณสามารถแก้ไขโปรไฟล์ผู้ใช้อยู่แล้วได้เท่านั้น ไม่ใช่สร้างโปรไฟล์ใหม่
การจัดรูปแบบข้อมูล: ทุก Endpoints ของ Open Cloud ต้องการให้คุณจัดรูปแบบข้อมูลก่อนส่งไปยังเซิร์ฟเวอร์ การจัดรูปแบบหมายถึงการแปลงวัตถุให้เป็นสตริง การแปลงย้อนกลับคือการแปลงสตริงให้อยู่ในรูปแบบของวัตถุ Engine API จะทำการจัดรูปแบบและแปลงรายการอัตโนมัติ แต่สำหรับ Open Cloud คุณต้องจัดการการสร้างหรือการแยกวิเคราะห์รายการของคุณจากและไปยัง JSON ด้วยตัวเอง
สิทธิ์
ที่เก็บข้อมูลมักจะเก็บข้อมูลที่ละเอียดอ่อน เช่น โปรไฟล์ผู้ใช้และสกุลเงินเสมือน เพื่อรักษาความปลอดภัย วิธีการของ Open Cloud แต่ละแห่งมีสิทธิ์ที่สอดคล้องกันซึ่งเรียกว่า scopes ซึ่งคุณต้องเพิ่มลงใน API key ของคุณ เช่น universe-datastores.control:list scope สำหรับ List Data Stores วิธีการ หากคุณไม่เพิ่มสิทธิ์ที่จำเป็น คำขอ API ของคุณจะส่งกลับข้อผิดพลาด ดูเอกสารอ้างอิงสำหรับ scopes ที่จำเป็นสำหรับแต่ละ endpoint
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการจัดการสิทธิ์ ให้ดูที่ Manage API keys.
พอร์ทัลสนับสนุนสินค้าคงคลังของผู้ใช้
ตัวอย่างนี้ใช้ที่เก็บข้อมูลชื่อว่า Inventory และสคีมาสำหรับแต่ละรายการคือ "userId": {"currency": number, "weapon": string, "level": number} คีย์คือ userId.
Scopes ที่ต้องการ
เมื่อ สร้าง API Key สำหรับตัวอย่างนี้ให้เพิ่ม scopes ต่อไปนี้ในกุญแจของคุณ:
- universe-datastores.objects:list
- universe-datastores.objects:read
- universe-datastores.objects:update
ในทางเลือก คุณสามารถเพิ่มสิทธิ์เฉพาะสำหรับที่เก็บข้อมูล Inventory, ตั้งค่าข้อจำกัดที่อยู่ IP และตั้งค่าวันที่หมดอายุ
เพิ่มสคริปต์สำหรับพอร์ทัลสนับสนุนสินค้าคงคลังของผู้ใช้
หลังจากสร้าง API key พร้อมสิทธิ์ที่จำเป็นสำหรับแอปตัวอย่างแล้ว คุณสามารถสร้างสคริปต์เพื่อทำการขอไปยัง endpoints สคริปต์เหล่านี้จะรับข้อมูล 10 รายการแรกในที่เก็บข้อมูล เพิ่มค่า currency สำหรับแต่ละรายการขึ้น 10 จากนั้นทำการอัปเดตแต่ละรายการ สำหรับที่เก็บข้อมูลที่ใหญ่กว่า คุณจะต้องจัดการกับ pagination โดยใช้พารามิเตอร์ query maxPageSize และ pageToken.
incrementCurrency.js
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('The API_KEY environment variable is not set.');
}
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) // The body must be a string
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\nProcessing entry: ${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(`Response: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('An error occurred during execution:', error);
}
})();
ในการทดสอบตั้งค่า API_KEY ในตัวแปรสภาพแวดล้อม ติดตั้ง dependencies และรันสคริปต์:
export API_KEY=<your_key>node incrementCurrency.js
กระดานผู้นำภายนาค
ตัวอย่างนี้สร้างรายชื่อผู้ใช้ที่กำหนดไว้ล่วงหน้าเพื่อวัตถุประสงค์ในการสาธิต แต่เพื่อให้มีประโยชน์ในเกมจริง คุณจะต้องมีที่เก็บข้อมูลจริงของผู้ใช้
Scopes ที่ต้องการ
เมื่อ สร้าง API Key สำหรับตัวอย่างนี้ให้เพิ่ม scopes ต่อไปนี้ในกุญแจของคุณ:
- universe.ordered-data-store.scope.entry:read
- universe.ordered-data-store.scope.entry:write
เพิ่มสคริปต์สำหรับกระดานผู้นำ
หลังจากสร้าง API key พร้อมสิทธิ์ที่จำเป็นสำหรับแอปตัวอย่างแล้ว คุณสามารถสร้างสคริปต์เพื่อทำการขอไปยัง endpoints สคริปต์เหล่านี้จะเพิ่มรายการตัวอย่างบางรายการไปยังที่เก็บข้อมูลที่เรียงลำดับพร้อมด้วยหมายเลขสุ่ม จากนั้นดึงข้อมูลจากค่าที่สูงสุดไปต่ำสุด สำหรับที่เก็บข้อมูลที่ใหญ่กว่า คุณจะต้องจัดการกับ pagination โดยใช้พารามิเตอร์ query 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 Error (${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: The API_KEY environment variable is not set.');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('Creating sample data...');
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(`Failed to create entry for "${name}": ${error.message}`);
}
}
console.log('\nGetting sorted list of entries...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`Failed to list entries: ${error.message}`);
}
}
main();
ในการทดสอบตั้งค่า API_KEY ในตัวแปรสภาพแวดล้อม ติดตั้ง dependencies และรันสคริปต์:
export API_KEY=<your_key>node leaderboard.js