Oyuncu verisi (bulut kaydı)
Bu sayfa oyuncunun ilerlemesini (seviye, ayarlar, oyun kaydı) Open Play’de saklayıp başka cihazdan kaldığı yerden sürdürmeyi anlatır. Oyununuz kendi kayıt biçimini taşıyorsa ve onu oyuncu hesabına bağlamak istiyorsanız okuyun.
Ne işe yarar
Oyuncu verisi, oyuncu başına anahtar-değer deposudur. Değer, sunucuda yorumlanmayan opak bir JSON nesnesidir: Open Play onu saklar, boyutunu ölçer ve aynen geri verir; alanlarını sorgulamaz, hesap yapmaz. Kayıt biçimini ve sürümünü oyununuz belirler.
Bu özellik çekirdektedir: bir ürün modülüne bağlı değildir ve Oyun ayarları › Modüller sayfasından kapatılamaz.
Panelde nerede
| Ne yapacaksınız | Panel sayfası |
|---|---|
| Bir oyuncunun anahtarlarını, boyutlarını ve değerini görmek; gerektiğinde silmek | Oyuncular › oyuncu profili › “Oyun verisi” bölümü |
Panelden değer yazılamaz; yalnız salt okunur biçimli JSON olarak görünür. Operatör silmesi gerekçe (8–500 karakter) ister ve denetim kaydına yazılır.
Sınırlar
| Sınır | Değer |
|---|---|
| Anahtar | ^[a-z0-9][a-z0-9._-]{0,63}$ (küçük harf, rakam, ., _, -; en çok 64 karakter) |
| Değer | Üst düzey JSON nesnesi; serileşmiş (kompakt) boyutu en çok 65 536 bayt |
| Anahtar sayısı | Oyuncu başına en çok 16 |
| Yazım hızı | Oyuncu başına dakikada en çok 60 yazım (PUT ve DELETE; okumalar sayılmaz) |
Değerde kayan noktalı sayılar serbesttir: değer opak veridir, sunucu onunla hesap yapmaz. İki sınır vardır: nesne anahtarlarının sırası korunmaz ve 2^64’ten büyük tam sayılar ile çift duyarlığı aşan ondalıklar yuvarlanabilir; büyük sayıları dize olarak saklayın.
Sürümlü yazım (CAS)
Her anahtarın bir sürümü vardır: ilk yazımda 1, her başarılı yazımda 1 artar. Sürüm bir i64’tür ve JSON’da ondalık dize olarak taşınır ("3"); HTTP başlığında ETag: "3" biçimindedir.
Önkoşulsuz yazım yoktur. Her PUT tam olarak birini taşır:
If-None-Match: *— yalnız anahtar yoksa oluştur.If-Match: "<sürüm>"— yalnız güncel sürüm buysa güncelle.
İki başlığı birden göndermek, hiçbirini göndermemek ya da biçimsiz değer göndermek 428 precondition_required döner. Sürüm tutmazsa (başka cihaz aynı sırada yazdı) 409 version_conflict ve güncel sürüm döner; uzak kaydı okuyun, kendi kuralınızla birleştirin ve yeniden deneyin. Kaynak: crx-domain::player_data.
Oyunun yaptığı çağrılar
Oyuncu uçları oyuncu oturumuyla çalışır: __Secure-crx.session çerezi ya da opak oturum belirteci (Authorization: Bearer) ve x-crx-env: <ortam uuid>. Yazımlar (PUT, DELETE) mağaza satın alma ucuyla aynı tarayıcı korumasını taşır: Origin denetimi ve çerez oturumunda x-csrf-token (bkz. Oturumlar ve CSRF). Geliştirici anahtarı oyuncu uçlarında kabul edilmez (401).
| Yöntem ve yol | Kimlik | Ne yapar |
|---|---|---|
GET /v1/player/data | oyuncu oturumu | Kendi anahtarlarının özetini listeler |
GET /v1/player/data/{key} | oyuncu oturumu | Bir anahtarı okur (ETag: "<sürüm>") |
PUT /v1/player/data/{key} | oyuncu oturumu | Önkoşullu oluşturur ya da günceller |
DELETE /v1/player/data/{key} | oyuncu oturumu | If-Match ile siler (204) |
GET /v1/players/{player_id}/data | sunucu anahtarı | Bir oyuncunun anahtar özetleri (yalnız okuma) |
GET /v1/players/{player_id}/data/{key} | sunucu anahtarı | Bir oyuncunun bir anahtarı (yalnız okuma) |
Sunucu uçları Authorization: Bearer crx_live_<önek>_<gizli> ister; istemci anahtarı 403, oyuncu oturumu 401 alır. Yanıtlar Cache-Control: private, no-store taşır.
Listele
GET /v1/player/data
x-crx-env: 0192f000-0000-7000-8000-00000000e001
{ "items": [ { "key": "my-game.save", "version": "3", "size": 214, "updatedAt": "2026-11-03T10:15:30.120Z" } ] }
Oluştur
PUT /v1/player/data/my-game.save
x-crx-env: 0192f000-0000-7000-8000-00000000e001
x-csrf-token: <csrf belirteci>
If-None-Match: *
Content-Type: application/json
{ "value": { "level": 4, "stars": 17, "playTimeSeconds": 913.5 } }
{ "key": "my-game.save", "version": "1", "updatedAt": "2026-11-03T10:15:30.120Z" }
Yanıt ETag: "1" başlığını da taşır. Gövde tam olarak { "value": <nesne> } olmalıdır; başka alan 422 invalid_value döner.
Güncelle ve sil
PUT /v1/player/data/my-game.save
If-Match: "1"
{ "value": { "level": 5, "stars": 21 } }
DELETE /v1/player/data/my-game.save
If-Match: "2"
Silme 204 döner. Oyuncu silmesi de önkoşulludur; operatör silmesi panelden yapılır.
Okumak
GET /v1/player/data/my-game.save
{ "key": "my-game.save", "value": { "level": 5, "stars": 21 }, "version": "2", "updatedAt": "2026-11-03T10:16:02.004Z" }
Anahtar yoksa 404 ve {"code":"not_found"} döner.
Hata kodları
Bu uçların hataları, çekirdeğin geri kalanından farklı olarak JSON’dur: { "code": "…", "message": "…" }.
| Durum | code | Ne zaman |
|---|---|---|
| 404 | not_found | Anahtar yok (sunucu uçlarında oyuncu bu ortamda yoksa da) |
| 409 | version_conflict | Sürüm tutmadı ya da var olan anahtara If-None-Match: *; currentVersion güncel sürümü (anahtar yoksa null) taşır |
| 413 | value_too_large | Değer 64 KiB’ı aştı; limit alanı sınırı verir |
| 422 | invalid_key | Anahtar kalıba uymuyor |
| 422 | invalid_value | Değer nesne değil, gövde { "value": … } değil ya da JSON değil |
| 422 | too_many_keys | Oyuncu 16 anahtarda; limit alanı sınırı verir |
| 428 | precondition_required | If-Match / If-None-Match: * yok, çift ya da biçimsiz |
| 429 | rate_limited | Dakikada 60 yazım aşıldı; Retry-After saniye cinsinden bekleme verir |
| 401 | — | Oturum yok, geçersiz ya da yasaklı oyuncu (kimlik katmanı ban ayrımını sızdırmaz) |
| 403 | — | Origin, Fetch Metadata ya da CSRF reddi; sunucu uçlarında istemci anahtarı |
Yasaklı bir oyuncunun yazımı kimlik katmanında 401 ile biter; depo da savunma olarak yazımı 403 ve {"code":"account_banned"} ile reddeder.
TypeScript SDK
PlayerClient.data bu uçların tipli karşılığıdır: list(), get(key), create(key, value), update(key, value, version), delete(key, version). Sürümler bigint’tir; değer normal JS nesnesidir (güvenli tam sayılar number, aralık dışı tam sayılar bigint, ondalıklar number). CSRF belirtecini ve çerezi SDK yönetir. 409 çatışması PlayerDataConflict (+ currentVersion: bigint | null) olarak fırlar; diğer retler CrxError’dır ve playerDataErrorCode(error) ile ayrışır. Bkz. TypeScript SDK.
Çalışan örnek: kaydet, çatışmada birleştir
/** Oyuncu verisi (bulut kaydı): kaydı oku, değiştir, sürümlü yaz; çatışmada uzak kayıtla birleştirip yeniden dene. */
import { PlayerDataConflict, type PlayerClient, type PlayerDataValue } from "@ferdiunal/open-play-ts";
const KEY = "my-game.save";
/** İki kaydı oyunun kendi kuralıyla birleştirir: burada her bölümde en iyi yıldız sayısı kalır. */
function merge(remote: PlayerDataValue, local: PlayerDataValue): PlayerDataValue {
const best = (a: unknown, b: unknown): number => Math.max(typeof a === "number" ? a : 0, typeof b === "number" ? b : 0);
return { ...remote, ...local, stars: best(remote.stars, local.stars) };
}
/** Kaydı buluta yazar; sürüm çatışırsa (başka cihaz yazmıştır) uzak kaydı okur, birleştirir ve en çok 3 kez dener. */
export async function saveToCloud(player: PlayerClient, local: PlayerDataValue): Promise<void> {
let value = local;
let current = await player.data.get(KEY).catch((error: unknown) => {
if (error instanceof Error && "status" in error && error.status === 404) return null;
throw error;
});
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
if (current === null) await player.data.create(KEY, value);
else await player.data.update(KEY, value, current.version);
return;
} catch (error: unknown) {
if (!(error instanceof PlayerDataConflict)) throw error;
current = error.currentVersion === null ? null : await player.data.get(KEY);
if (current !== null) value = merge(current.value, value);
}
}
throw new Error("Kayıt üç denemede birleştirilemedi");
}
Saklama ve silme
- Oyuncu satırı hiç silinmez; terk edilmiş misafir hesapları kapatılırken (anonimleştirme) o hesabın oyuncu verisi aynı işlemde silinir.
- Operatör, panelden bir anahtarı gerekçeyle silebilir; oyuncu kendi anahtarını
If-Matchile siler. - Veri, oyuncu ve ortam başına yalıtılmıştır: bir ortamın oyuncusu başka ortamdan okunamaz.
Sık karşılaşılan durumlar
- Sürekli 409 alıyorum. Başka bir cihaz yazıyor ya da
update’e eski sürüm veriyorsunuz. Her yazımdan sonra dönenversiondeğerini saklayın; çatışmada uzak kaydı okuyup birleştirin, ham sürümü artırıp tekrar göndermeyin. - 429 alıyorum. Dakikada 60 yazım sınırı oyuncu başınadır. Her tuş vuruşunda değil, gecikmeli (debounce) ve anlamlı noktalarda yazın;
Retry-Afterkadar bekleyin. - 16 anahtar sınırı. Birden çok küçük anahtar yerine kayıt türü başına bir anahtar kullanın (ör.
my-game.save,my-game.settings).