Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

English

Switch to the English documentation

Open Play geliştirici dokümanı

Oyununuzu Open Play’e bağlamak ve yönetim panelini kullanmak için sayfalar aşağıdadır. Türkçe sayfalar kaynaktır; İngilizce sürüm çeviridir.

Henüz yazılmamış sayfalar planlıdır; bağlantıları sayfa eklenince çalışır. Durum: pages.yaml (docs/developer/pages.yaml).

Başlarken

Kimlik doğrulama

SDK’lar

Rehberler

Gerçek zamanlı

Eklentiler

Başvuru

Operasyon

Panel yardım sayfaları

Panel sayfaları yönetim panelindeki “Yardım” çekmecesinin ve bilgi balonlarının kaynağıdır.

Open Play’e genel bakış

Bu sayfa, Open Play’in oyununuz için hangi hazır parçaları sunduğunu ve bu parçalarla hangi kanaldan konuşacağınızı özetler. Entegrasyona başlamadan önce okuyun; ayrıntılar için her bölümün sonundaki bağlantıları izleyin.

Open Play nedir?

Open Play, oyunların arka uç ihtiyaçlarını tek bir sunucu olarak karşılar: oyuncu kimliği ve oturumu, karakterler, envanter, ekonomi, mağaza, ödüller, sosyal özellikler, skor tabloları, bildirimler, webhook’lar ve gerçek zamanlı bağlantı. Oyununuzu bu sunucuya HTTP (REST) ve WebSocket üzerinden bağlarsınız; oyunu yöneten ekip aynı sunucuyu yönetim panelinden yapılandırır.

Not

Open Play bugün bir Docker Compose yığınıdır ve sizin ya da ekibinizin altyapısında çalışır. Kurulum ve dağıtım için üretim dağıtımı rehberine (docs/production-and-adapters.tr.md) bakın; bu belge oyun geliştiricisinin bakış açısını anlatır.

Neler sunar? Modül haritası

Özelliklerin çoğu modül olarak açılıp kapatılır. Kapalı bir modülün uçları çalışmaz ve 404 ile module_disabled kodunu döndürür (bkz. Kavramlar). Modüller panelde Oyun ayarları › Modüller sekmesinden yönetilir.

ModülOyununuza ne verir?Rehber
charactersKarakter tanımları, karakterler, komutlar, kurallar, özellikler (trait), seviye eğrileriKarakterler
economyPara birimleri, cüzdanlar, transferler, defter (ledger)Ekonomi
itemsEşya tanımları, eşya örnekleri, envanterEşyalar ve envanter
tradingTüccar ve oyuncular arası takasHam REST: uç noktalar
storeMağaza kataloğu, satın alma, uygulama içi satın alma (IAP) doğrulamasıMağaza ve IAP
rewardsÖdül tabloları, görev, NPC ödülü, VIP, günlük giriş ödülüÖdüller
socialKlan ve partiSosyal
chatSohbetSohbet
competitionSkor tabloları ve turnuvalarRekabet
notificationsBildirim kanalları, şablonlar, oyuncu gelen kutusu, web pushBildirimler
webhooksOyun olaylarını kendi sunucunuza imzalı olarak iletmeWebhook’lar
scenesSahneler ve görsel varlıklarSahneler, varlıklar, modeller
pluginsEklenti platformu ve gerçek zamanlı yüzeyEklentiler, Gerçek zamanlı

Modül anahtarı olmayan çekirdek yüzeyler her zaman açıktır:

  • Oyuncu kimliği ve oturumu: misafir, e-posta/parola, Google. Bkz. Kimlik doğrulama.
  • İçerik grupları ve katalog: panelden yönetilen, oyunun okuduğu veri. Bkz. İçerik grupları.
  • Oyun kuralları ve otomasyonlar. Bkz. Oyun kuralları.
  • API anahtarları, ortamlar ve alan adları. Bkz. Kavramlar.

Hangi kanaldan konuşursunuz?

KanalKim kullanır?KimlikNe için?
REST (/v1/...) + sunucu anahtarıOyun sunucunuz (güvenilen arka uç)Authorization: Bearer crx_live_<önek>_<gizli>Yönetim ve parayı/eşyayı değiştiren işlemler
REST + istemci anahtarıOyun istemcinizaynı biçimde istemci anahtarıSınırlı, hassas olmayan işlemler
REST + oyuncu oturumuOyuncunun kendi istemcisi (tarayıcı, Unity)çerez ya da oturum belirteci + x-crx-envOyuncunun kendi verisi: giriş, katalog, mağaza, sıralama, eklenti komutları
WebSocket (/v1/realtime/ws vb.)Oyuncunun istemcisitek kullanımlık biletEklenti durumu, anlık girdi, kesintisiz akış
Webhook (Open Play → sizin sunucunuz)Sizin sunucunuzX-Crx-Signature imzasıOlay bildirimi
SDKTypeScript ve Unityyukarıdakilerin sarmalayıcısıREST ve WebSocket’i tipli kullanmak

Uyarı

Sunucu anahtarı bir sırdır. Tarayıcıya, mobil uygulamaya ya da oyun paketine koymayın. İstemci tarafında yalnızca oyuncu oturumu ve (gerekirse) istemci anahtarı kullanın.

gRPC arayüzü sunucu tarafında vardır ancak oyun geliştiricisi için dışa açık bir kanal değildir; varsayılan olarak yalnız loopback’te dinler ve SDK’lar kullanmaz. Bu belge onu anlatmaz.

Nereden başlamalı?

  1. Kavramlar: stüdyo, oyun, ortam, modül, anahtar türleri, oyuncu oturumu.
  2. Hızlı başlangıç: ilk sunucu çağrınız ve ilk oyuncu girişiniz.
  3. Mimari ve barındırma: aynı köken (same-origin) kuralı, alan adları, neyin nerede çalıştığı.
  4. Kimlik doğrulama ve SDK’lar.

Bugünkü sınırlar

Aşağıdakiler bilinçli olarak dürüst yazılmıştır; yol haritanızı buna göre kurun.

  • CORS yalnız ortamın alan adı listesindeki origin’lere açıktır ve çerez aynı site ister. Tarayıcı oyunu, alan adı bir ortama bağlıysa Open Play’i doğrudan çağırabilir; oyun Open Play’den farklı bir kayıtlı alan adındaysa SameSite=Lax oturum çerezi gitmez (ters vekil kullanın). Bkz. Mimari ve barındırma.
  • Üçüncü taraf eklenti yükleme desteklenmez. Eklentiler sunucuya derlenmiş olarak gelir; panelden eklenti yüklenmez. Bkz. Eklentiler.
  • SDK kapsamı kısmidir. TypeScript SDK’sında tipli istemci olmayan modüller ham REST ile kullanılır. Bkz. TypeScript SDK.
  • Unity’de yalnız Unity 6000.6.7f1 doğrulanmıştır. Bkz. Unity SDK.
  • Hata gövdeleri çoğunlukla düz metindir. Yalnız birkaç yerde JSON hata zarfı vardır. Bkz. Hatalar.

Temel kavramlar

Open Play’de her şey birkaç kavramın etrafında döner: verinin ait olduğu ortam, özellikleri açıp kapatan modüller, iki farklı API anahtarı türü, oyuncunun oturumu ve oyuna özel mantığı taşıyan eklenti. Bu sayfayı bir kez okuyun; diğer sayfalar bu kelimeleri bilindiğini varsayar.

Stüdyo, oyun, ortam

Open Play üç katmanlı bir hiyerarşi kullanır. Panelde Stüdyolar ve oyunlar ekranında (yalnız süper yönetici görür) bu ağacı yönetirsiniz.

KatmanAnlamıNot
StüdyoOyunlarınızı bir arada tutan kuruluşBir sunucuda birden çok stüdyo olabilir
OyunTek bir ürünYeni oyun oluşturulunca dev ve prod ortamları otomatik açılır
OrtamOyunun yalıtılmış bir kopyası: dev, prod, istenirse stagingOyuncular, karakterler, cüzdanlar, anahtarlar ortama aittir

Ortam, veri sınırıdır. Bir ortamdaki oyuncu, karakter, cüzdan ya da anahtar başka bir ortamdan görünmez. Her API çağrısı tek bir ortama karşı çalışır:

  • API anahtarıyla yapılan çağrılarda ortam anahtardan çıkar; ayrıca bir başlık göndermezsiniz.
  • Oyuncu oturumuyla yapılan çağrılarda ortamı x-crx-env: <ortam-kimliği> başlığıyla siz bildirirsiniz. Kimlik bir UUID’dir. SDK’lar bunu environmentId ayarından gönderir.

Not

Panel bugün ortam kimliğini (UUID) okunur bir alanda göstermez. Kimliği operatör ekibinizden isteyin ya da panel oturumundaki Oyun ayarları › Modüller sekmesinin yaptığı GET /v1/panel/modules isteğinin yanıtındaki environmentId alanından okuyun. Bu eksiklik kayıtlıdır.

Panel adımları için bkz. Hızlı başlangıç ve Oyun ayarları.

Modüller

Özelliklerin çoğu modüldür: characters, economy, items, trading, store, rewards, social, chat, competition, notifications, webhooks, scenes, plugins (13 modül). Modüller ortam başına açılıp kapatılır; kurulumda sihirbaz bir başlangıç kümesi seçtirir, sonrasında Oyun ayarları › Modüller sekmesi kullanılır (yalnız süper yönetici).

  • Kapalı modülün uçları çalışmaz: HTTP 404 ve gövde {"code":"module_disabled","message":"…","module":"<ad>"}. Kimliği geçersiz çağıran 401 alır; modülün kapalı olduğunu öğrenemez.
  • Kapatmak veriyi silmez, yalnız yüzeyi durdurur; yeniden açınca veri yerindedir.
  • Bazı modüller başkalarına bağlıdır (örneğin trading, economy ve items’a dayanır) ve etkin bir eklentinin ihtiyaç duyduğu modül kapatılamaz.
  • Çekirdek yüzeyler (oyuncu girişi, içerik grupları, API anahtarları) modül değildir ve kapatılamaz.

Hata ayrıntıları için bkz. Hatalar.

API anahtarları: sunucu ve istemci

API anahtarı, bir ortama bağlı gizli bir dizgedir ve Authorization: Bearer başlığıyla gönderilir. Biçimi:

crx_live_<önek>_<gizli>

Panelde yalnız crx_live_<önek> kısmı (önek) görünür; gizli değer oluşturulduğu anda bir kez gösterilir ve geri alınamaz. Kaybederseniz yeni anahtar oluşturur ya da anahtarı döndürürsünüz (rotate): eski gizli değer geçersiz olur, yenisi bir kez gösterilir.

Sunucu anahtarıİstemci anahtarı
Nerede kullanılır?Oyun sunucunuz, arka uç kodunuzOyun istemcisi (tarayıcı, mobil)
Gömülebilir mi?Hayır, gizli tutulurEvet
Ne yapabilir?Tüm işlemler: tanım oluşturma, para verme, eşya basma, webhook kaydı, yönetimYalnız izin verilen, hassas olmayan işlemler
Örnekadd_xp komutu, para verme, eşya basmakarakteri okuma, set_flag komutu

Kesin kural uç noktaya göre değişir ve kodda uç nokta başına sınanır. Hangi ucun hangi anahtar türünü kabul ettiği uç nokta başvurusunda listelenir. Anahtar türü yetersizse çağrı 403 alır.

Panelde anahtar oluşturma: Oyun ayarları › API anahtarları › Yeni API anahtarı (bkz. API anahtarları). Oluştururken ad, tür (Sunucu anahtarı ya da İstemci anahtarı), geçerlilik süresi ve denetim kaydına yazılan bir not (8–500 karakter) girilir.

Uyarı

Gelişmiş ayarlar (kapsam etiketleri, izinler, ek bilgiler) bugün yalnızca kayıt amaçlıdır; anahtarın neye erişebileceğini kısıtlamaz. Erişimi yalnızca anahtar türü belirler. Sunucu anahtarını “kısıtlı” saymayın.

Anahtar ve gizli değer yönetimi için bkz. Güvenlik.

Oyuncu oturumu

Oyuncular API anahtarıyla değil, kendi oturumlarıyla konuşur. Oturum şu yollarla açılır: misafir girişi, e-posta ve parola, Google. Başarılı girişte sunucu bir oturum çerezi (__Secure-crx.session) ve bir yenileme çerezi (__Secure-crx.refresh) yazar; aynı belirteçler yanıt gövdesinde de döner, böylece çerezsiz istemciler (Unity yerel derlemesi, komut satırı araçları) Authorization: Bearer <oturum-belirteci> kullanabilir.

Oyuncu oturumuyla yapılan her istek x-crx-env başlığı taşır. Çerezli (tarayıcı) oturumda durum değiştiren istekler ayrıca CSRF koruması ister (x-csrf-token başlığı). Bu ayrıntıları SDK’lar sizin yerinize yönetir; ham REST’te kendiniz uygularsınız:

Oyuncu oturumu oyun sunucunuza da taşınabilir: oyuncunun kısa ömürlü erişim JWT’si (EdDSA) ortak bir JWKS ile çevrimdışı doğrulanır. Bkz. Sunucuda doğrulama.

Eklenti (plugin)

Eklenti, bir oyuna özel sunucu mantığını (komutlar, sorgular, gerçek zamanlı modüller) taşıyan, sunucuya derlenmiş bir pakettir. Eklenti çağrıları /v1/plugins/{eklenti}/... altında yapılır; durum değiştiren komutlar idempotency anahtarı taşır ve şema isterse expectedVersion ile sürüm denetimi yapar. Eklentinin şeması bir artefakttır (<eklenti>.plugin.schema.json) ve TypeScript/Unity bağlamaları bu şemadan üretilir.

Not

Üçüncü taraf eklenti yükleme (panelden ya da API’den) bugün desteklenmez. Eklentiler Open Play sunucusunun yayınıyla birlikte gelir. Bkz. Eklentilere genel bakış.

Ortak çağrı kuralları

Birçok uçta aynı üç kural geçerlidir; ayrıntıları başvuru sayfalarında bulursunuz:

  • Idempotency-Key: durum değiştiren komutlarda aynı anahtar ve aynı gövde sonucu yeniden oynatır; aynı anahtarla farklı gövde 422 verir. Bkz. Idempotency ve eşzamanlılık.
  • If-Match / expectedVersion: kaynağın okuduğunuz sürümünü bildirerek eşzamanlı yazma çakışmasını yakalarsınız (409).
  • Sayılar: büyük tam sayılar (i64) JavaScript’te bigint olur; eklenti protokolünde ondalık dizge olarak taşınır. Bkz. Sayılar ve kodlama.

Terimlerin kısa tanımı için bkz. Sözlük.

Hızlı başlangıç

Bu rehberde sıfırdan şunları yaparsınız: panelde bir oyun ve sunucu anahtarı oluşturur, curl ve TypeScript ile ilk sunucu çağrınızı atar, tarayıcı oyununuzdan bir oyuncuyu misafir olarak girdirir ve aynısını Unity’den yaparsınız. Yaklaşık yarım saat sürer. Başlamadan önce Kavramlar sayfasına göz atın.

Ön koşullar

  • Çalışan bir Open Play sunucusu ve panele giriş yapabilen bir yönetici hesabı (süper yönetici). Kurulum için üretim dağıtımı rehberine (docs/production-and-adapters.tr.md) bakın.
  • Sunucunun genel HTTPS adresi. Bu sayfada OPEN_PLAY_URL olarak anılır (örneğin https://oyun.example.com/open-play).
  • Node.js 22 ya da üstü ve curl; ham REST örnekleri için jq ve openssl.

1. Oyun ve ortam oluşturun

Kurulum sihirbazı ilk stüdyoyu ve oyunu zaten açtıysa bu adımı atlayın.

  1. Panelde Stüdyolar ve oyunlar ekranını açın ve Yeni stüdyo düğmesiyle bir stüdyo oluşturun.
  2. Stüdyo kartında Yeni oyun’u seçin; oyunun adını (ve isterseniz kısa adını) girin. Oyunla birlikte dev ve prod ortamları otomatik oluşur.
  3. Üst çubuktaki Aktif oyun ve ortam seçicisinden oluşturduğunuz oyunun dev ortamını seçin. Panelin geri kalanı seçili ortamın verisini gösterir.

Ayrıntılar: Stüdyolar ve oyunlar.

2. Modülleri kontrol edin

Oyun ayarları › Modüller sekmesinde bu rehberdeki çağrı için characters modülünün Açık olduğunu doğrulayın. Kapalı modül 404 ve module_disabled döndürür. Ayrıntılar: Modüller.

3. Sunucu anahtarı oluşturun

  1. Oyun ayarları › API anahtarları sekmesinde Yeni API anahtarı’nı seçin.
  2. Bir ad verin (örneğin Oyun sunucusu), türü Sunucu anahtarı seçin, geçerlilik süresini belirleyin ve denetim kaydı için kısa bir not yazın (8–500 karakter).
  3. Oluştur’a basın. Açılan karttaki gizli değeri hemen kopyalayıp sunucunuzun gizli ayarlarına kaydedin. Bu değeri bir daha göremezsiniz.

Anahtar şu biçimdedir: crx_live_<önek>_<gizli>. Bu sayfada ortam değişkeni olarak kullanacağız:

export OPEN_PLAY_URL="https://oyun.example.com/open-play"
export OPEN_PLAY_SERVER_KEY="crx_live_<önek>_<gizli>"

Uyarı

Gerçek anahtarı belgeye, depoya, sohbete ya da günlüğe yazmayın. Sızdığından şüphelenirseniz anahtarı panelden döndürün (rotate) ya da silin.

4. İlk sunucu çağrınız

Sunucu anahtarıyla ortamdaki karakterlerin ilk sayfasını isteyin. Ortam anahtardan çıktığı için ek başlık gerekmez.

curl -sS "$OPEN_PLAY_URL/v1/characters?limit=1" \
  -H "Authorization: Bearer $OPEN_PLAY_SERVER_KEY"

Henüz karakter yoksa beklenen yanıt (200):

{"items":[],"next_cursor":null}

Aynı çağrı TypeScript SDK’sıyla (kurulum için TypeScript SDK):

import { CrxClient, CrxError } from "@ferdiunal/open-play-ts";

/**
 * Sunucu anahtarıyla ilk çağrı: ortamdaki karakterlerin ilk sayfasını okur.
 * Bu kod yalnızca güvenilen arka uçta çalışır; sunucu anahtarını tarayıcıya göndermeyin.
 */
export async function listFirstCharacters(baseUrl: string, serverKey: string): Promise<number> {
  const client = new CrxClient({ baseUrl, apiKey: serverKey });
  try {
    const page = await client.listCharacters({ query: { limit: 1 } });
    return page.items.length;
  } catch (error) {
    if (error instanceof CrxError) {
      // kind, status ve attempts güvenlidir; anahtar ve istek gövdesi hata nesnesine girmez.
      console.error("Open Play çağrısı başarısız:", error.kind, error.status);
    }
    throw error;
  }
}

// Kullanım (Node 22, ESM):
//   const count = await listFirstCharacters(process.env.OPEN_PLAY_URL!, process.env.OPEN_PLAY_SERVER_KEY!);

Olası sorunlar:

BelirtiNedenÇözüm
401Anahtar yanlış, silinmiş, süresi dolmuş ya da Bearer öneki eksikAnahtarı ve başlığı kontrol edin; gerekirse yeni anahtar oluşturun
404, gövde {"code":"module_disabled",…}characters modülü bu ortamda kapalıAdım 2
403Anahtar türü bu işlem için yetersiz (istemci anahtarı)Sunucu anahtarı kullanın
Bağlantı/TLS hatasıAdres yanlış ya da HTTPS değilGenel HTTPS adresini kullanın; yerelde http://localhost yalnız geliştirme içindir

5. Oyununuzun alan adını ortama bağlayın

Tarayıcı, durum değiştiren isteklere Origin başlığı ekler; sunucu bu origin’in o ortama ait olduğunu denetler. Oyun sayfanızın alan adı tanımlı değilse oyuncu girişi 403 ile reddedilir.

  1. Oyun ayarları › Ortamlar ve alan adları sekmesinde ilgili ortamın Alan adları alanına oyun sayfanızın alan adını yazın (satır başına bir alan adı; örneğin oyun.example.com). Joker karakter, IP ve port kabul edilmez.
  2. Bir gerekçe yazın ve Alan adlarını kaydet’e basın.

Alan adı girmezseniz sunucunun genel izinli listesi geçerlidir; bu liste operatörün ayarıdır. Ayrıntılar: Mimari ve barındırma.

6. Tarayıcıdan ilk oyuncu girişi (misafir)

Oyun sayfanız API’yi ya aynı origin’den (ters vekil) ya da alan adı bir ortama bağlıysa doğrudan Open Play adresinden (CORS) çağırır (bkz. Mimari ve barındırma). Aşağıdaki kod kayıtlı oturumu geri yükler; yoksa misafir oturumu açar. environmentId, ortamın UUID’sidir.

import { createPlayerClient, type PlayerClient } from "@ferdiunal/open-play-ts";

/**
 * Tarayıcıdaki oyun sayfasından oyuncu oturumu açar: kayıtlı oturum varsa onu geri yükler,
 * yoksa misafir oturumu açar. `baseUrl` oyun sayfasıyla aynı origin'dir (aynı köken kuralı).
 */
export async function signInAsGuest(environmentId: string): Promise<PlayerClient> {
  const client = createPlayerClient({ baseUrl: window.location.origin, environmentId });

  // restore() yalnızca bakar; oturum yoksa misafir yaratmaz.
  const restored = await client.session.restore();
  const session = restored.status === "authenticated" ? restored : await client.session.signInGuest();

  console.log("oyuncu:", session.playerId, "misafir mi:", session.isGuest, "ad:", session.displayName);
  return client;
}

SDK sizin yerinize misafir sırrını üretip saklar, CSRF belirtecini alır ve çerezleri yönetir. Aynı tarayıcıda sayfayı yenilediğinizde restore() aynı oyuncuyu geri getirir.

Aynısı ham REST ile

Aşağıdaki betik SDK’nın arka planda yaptığını gösterir (HTTPS gerekir; sunucu Secure çerez yazar):

export OPEN_PLAY_ENV="<ortam-kimliği-uuid>"
JAR="$(mktemp)"

# 1) CSRF belirteci al (çerez kavanozuna da yazılır)
TOKEN="$(curl -sS -c "$JAR" "$OPEN_PLAY_URL/v1/auth/csrf" | jq -r .token)"

# 2) 32 baytlık rastgele misafir sırrı: 64 küçük harfli onaltılık karakter. Sakladığınız sürece aynı oyuncuya döner.
BOOTSTRAP="$(openssl rand -hex 32)"

# 3) Misafir girişi
curl -sS -b "$JAR" -c "$JAR" -X POST "$OPEN_PLAY_URL/v1/auth/sign-in/guest" \
  -H "content-type: application/json" \
  -H "x-crx-env: $OPEN_PLAY_ENV" \
  -H "x-csrf-token: $TOKEN" \
  -d "{\"bootstrap_token\":\"$BOOTSTRAP\"}"

Yanıt (200; ilk kez ise is_new_player true):

{
  "player": { "id": "0192…", "display_name": "Guest", "email": null, "email_verified": false, "is_guest": true, "image": null, "created_at": "2026-11-03T10:00:00+00:00" },
  "tokens": { "session_token": "<64 hex>", "refresh_token": "<64 hex>", "access_token": "<jwt>", "expires_in_secs": 900 },
  "is_new_player": true
}

Çerez kavanozunda artık __Secure-crx.session ve __Secure-crx.refresh vardır. Oturumu doğrulayın:

curl -sS -b "$JAR" "$OPEN_PLAY_URL/v1/auth/session" -H "x-crx-env: $OPEN_PLAY_ENV"

Her durum değiştiren istekte CSRF belirteci tek kullanımlıktır: yanıtın x-csrf-token başlığı bir sonrakini taşır. Ayrıntılar: Oturumlar ve CSRF, Misafir girişi.

7. Unity’den ilk oyuncu girişi

Unity paketini kurduktan sonra (Unity SDK) aşağıdaki bileşeni bir GameObject’e ekleyin. baseUrl ve environmentId alanlarını doldurun. WebGL derlemesinde baseUrl sayfayla aynı origin olmalıdır.

using System;
using OpenPlay;
using OpenPlay.Session;
using OpenPlay.Unity;
using UnityEngine;

/// <summary>
/// Misafir girişi: kayıtlı oturum varsa geri yükler, yoksa misafir oturumu açar.
/// Bir GameObject'e ekleyin; baseUrl ve environmentId alanlarını doldurun.
/// </summary>
public sealed class QuickstartGuest : MonoBehaviour
{
    [SerializeField] private string baseUrl = "https://oyun.example.com";   // WebGL'de sayfayla aynı origin olmalı
    [SerializeField] private string environmentId = "";                      // ortam kimliği (UUID)
    [SerializeField] private bool allowInsecureTransport = false;            // yalnız http://localhost için

    private OpenPlayClient _client;

    private async void Start()
    {
        try
        {
            var options = OpenPlayUnity.Options(new Uri(baseUrl), Guid.Parse(environmentId));
            options.AllowInsecureTransport = allowInsecureTransport;
            _client = OpenPlayClient.Create(options);

            var state = await _client.Session.RestoreAsync();   // oturum yoksa misafir YARATMAZ
            if (state.Status != SessionStatus.Authenticated) state = await _client.Session.SignInGuestAsync();

            if (state.Status == SessionStatus.Banned)
            {
                Debug.Log("Hesap askıya alındı."); // terminal durum; yalnız LogoutAsync ile çıkılır
                return;
            }
            Debug.Log("hazır: oyuncu=" + state.PlayerId + " misafir=" + state.IsGuest);
        }
        catch (OpenPlayException ex)
        {
            Debug.LogError("Open Play hatası: " + ex.Kind + " (" + ex.Code + ")");
        }
    }

    private async void OnDestroy()
    {
        if (_client != null) await _client.DisposeAsync();
    }
}

Not

Unity SDK’sı yalnız Unity 6000.6.7f1 ile doğrulanmıştır. Ayrıntı için Unity SDK.

Sırada ne var?

Mimari ve barındırma

Bu sayfa, oyununuzu Open Play ile nasıl konumlandıracağınızı anlatır: yığının parçaları, tarayıcı oyunlarının Open Play’i doğrudan (CORS) ya da ters vekille nasıl çağıracağı, ortama alan adı bağlama ve çerez/TLS gereksinimleri. Bir tarayıcı ya da WebGL oyunu yayımlayacaksanız bu sayfayı baştan sona okuyun.

Neyin nerede çalıştığı

Referans dağıtım bir Docker Compose yığınıdır:

ParçaGöreviDışarı açık mı?
apiREST, WebSocket ve gerçek zamanlı yüzey; tüm iş mantığıYalnız ağ geçidi üzerinden
workerArka plan işleri: olay kuyruğu (outbox), webhook teslimatı, zamanlanmış işler, sıralama projeksiyonlarıHayır
PostgreSQLKalıcı veriHayır
RedisOran sınırları, CSRF kullanım kaydı, gerçek zamanlı anahtarlarHayır
converter3D model dönüştürme (yalnız amd64)Hayır
public-gatewayOyuncuya ve oyun sunucusuna açılan Caddy ağ geçidi; /v1/panel/* yolunu 404 ile keserEvet (TLS’i önündeki katman sonlandırır)
admin-gatewayYönetim paneliVarsayılan olarak yalnız loopback
grpc-proxyGüvenilen servisler için gRPCVarsayılan olarak yalnız loopback
Oyuncu tarayıcısı ──HTTPS──▶ oyun.example.com ─┬─ /            ▶ oyununuzun statik dosyaları
                                               └─ /open-play/* ▶ Open Play public-gateway ▶ api
Oyun sunucunuz ───────HTTPS──────────────────────────────────▶ Open Play public-gateway ▶ api
Open Play worker ──imzalı webhook──────────────────────────────▶ sizin sunucunuz

Public TLS, bu yığının içinde sonlandırılmaz; Cloudflare, bir yük dengeleyici ya da bir Caddy/nginx girişi gerekir. Panel çerezi Secure olduğundan panel de HTTPS ister. Kurulum, kaynak bütçeleri ve ağ ayrıntıları için üretim dağıtımı rehberine (docs/production-and-adapters.tr.md) bakın.

Not

gRPC arayüzü oyun geliştiricisi için dışa açık bir kanal değildir; varsayılan olarak yalnız loopback’te dinler ve SDK’lar onu kullanmaz.

Tarayıcıdan çağrı: doğrudan CORS ya da ters vekil

Oyununuzun alan adı ortamın alan adı listesindeyse tarayıcı oyununuz Open Play’i doğrudan çağırabilir. Sunucu, kamusal API’de (panel ve kurulum uçları hariç) CORS (cross-origin resource sharing, çapraz kaynak paylaşımı) başlıkları gönderir. İzin kümesi, panelde Oyun ayarları › Ortamlar ve alan adları altında tüm ortamlara yazılmış alan adları ile operatörün genel izinli listesinin (CRX_AUTH_TRUSTED_ORIGINS) birleşimidir; oyuna özel bir ayar yoktur. Örneğin oyun https://oyun.example.com adresinde, Open Play https://open-play.example.com adresinde çalışıyorsa ve oyun.example.com bir ortamın alan adıysa, oyun baseUrl: "https://open-play.example.com/open-play" ile doğrudan konuşur.

Kurallar:

  • İzinli origin tam olarak https://<alan adı> biçimindedir (yol yok, varsayılan olmayan port yok). Yanıt Access-Control-Allow-Origin olarak o origin’i aynen yansıtır ve Access-Control-Allow-Credentials: true ekler; * hiçbir zaman kullanılmaz, bilinmeyen origin’e başlık verilmez.
  • Önkontrol (preflight, OPTIONS) izinli origin için 204, izinsiz için 403 döner; Access-Control-Max-Age 600 sn’dir.
  • Üretim dışı ortamlarda (CRX_ENV development ya da test) listede localhost ya da 127.0.0.1 varsa http://localhost:<port> ve http://127.0.0.1:<port> de izinlidir.
  • Panelde yeni eklenen alan adı en geç birkaç saniyede (sunucu anlık görüntüsü 5 sn’de bir yenilenir) geçerli olur.
  • CORS yetki değildir. Origin/CSRF/ortam denetimleri aynen çalışır; CORS yalnız tarayıcının yanıtı okumasına izin verir. Panel yolu (/v1/panel/*) hiçbir koşulda CORS başlığı almaz. Gerçek zamanlı WebSocket el sıkışması değişmedi: Origin ortam başına denetlenir.

Uyarı

Oturum çerezi aynı site ister. Çerezler __Secure-, HttpOnly, SameSite=Lax ile yazılır. Oyun, Open Play’den farklı bir kayıtlı alan adındaysa (ör. oyun.example.com ve open-play.baska.dev) tarayıcı çapraz-site fetch isteklerine oturum çerezini eklemez; CORS başlıkları doğru olsa da oturum kurulamaz. Aynı kayıtlı alan adının alt alanları (ör. oyun.example.com ve open-play.example.com) aynı sitedir ve çalışır. Farklı sitede bu desteklenmez; bu durumda aşağıdaki ters vekili kullanın.

Ters vekil hâlâ bir seçenektir. Oyunu ve API’yi aynı origin’den sunmak istiyorsanız (ya da oyun farklı bir sitedeyse), oyunun kendi ağ geçidinde API yolunu Open Play’e yönlendiren bir ters vekil (reverse proxy) kurarsınız; bu durumda CORS devreye girmez. Örnek (Caddy):

oyun.example.com {
  # /open-play/ önekini sıyırıp Open Play public-gateway'e iletir.
  handle_path /open-play/* {
    reverse_proxy open-play-gateway:8080
  }
  # Geri kalan her şey oyununuzun statik dosyaları.
  handle {
    root * /srv/oyun
    try_files {path} /index.html
    file_server
  }
}

Ters vekil kurulumunda istemci tarafı ayarı şudur:

AyarDeğer
TypeScript baseUrlhttps://oyun.example.com/open-play (SDK’lar yol önekini kabul eder) ya da önek kullanmıyorsanız window.location.origin
Unity BaseUrlaynı adres; Unity WebGL SDK’sı bugün sayfayla aynı origin’i şart koşar, aksi halde webgl_cross_origin_unsupported yapılandırma hatası alırsınız (sunucu CORS’u bunu kaldırmaz; bkz. Unity SDK)
x-crx-envortam kimliği (SDK’lar environmentId ayarından gönderir)

Ters vekilde dikkat edin:

  • WebSocket yükseltmesi (Upgrade/Connection) geçmelidir; gerçek zamanlı bağlantı /v1/realtime/ws üzerindedir. Caddy reverse_proxy bunu kendiliğinden yapar; nginx’te proxy_http_version 1.1 ve Upgrade başlıklarını açıkça verin.
  • Origin ve Cookie başlıkları olduğu gibi iletilmelidir.
  • İstemci IP’si: X-Forwarded-For başlığını doğru yazın. Önde fazladan bir vekil varsa ve sunucuya güvenilen vekil adresleri tanıtılmadıysa bütün oyuncular tek IP kovasına düşer; belirtisi, farklı oyuncuların aynı anda 429 almasıdır. Bunu operatör ekibinizle çözün (bkz. Oran sınırları).
  • Panel yolunu açmayın. /v1/panel/* yolu public-gateway’de 404’tür; oyun alan adınız üzerinden panele yol vermeyin.

Çerez davranışı

ÇerezAmaçÖzellikler
__Secure-crx.sessionOyuncu oturumuHttpOnly; Secure; SameSite=Lax; Path=/
__Secure-crx.refreshOturum yenilemeaynı
__Secure-crx.csrfCSRF çift gönderim (double-submit) çereziaynı; 15 dakikalık ömür

Secure çerezler HTTPS ister. Yerel geliştirmede SDK’lar yalnız loopback adreslerinde (localhost, 127.0.0.1, ::1) düz http’ye izin verir; tarayıcınızın localhost için Secure çerezleri kabul edip etmediğini denetleyin, kabul etmiyorsa yerelde de HTTPS kullanın.

Alan adları ve güvenilir origin

Tarayıcı, durum değiştiren (POST, PUT, PATCH, DELETE) isteklere Origin başlığı ekler. Sunucu bu origin’in ilgili ortama bağlı bir alan adı olup olmadığını denetler:

  • Ortama bağlı alan adı: panelde Oyun ayarları › Ortamlar ve alan adları › Alan adları alanına yazdığınız adlardır. Her satır tam bir alan adıdır (örneğin oyun.example.com); joker karakter, IP ve port yoktur; ortam başına en çok 10 alan adı bağlanabilir. Aynı alan adı iki ortama bağlanamaz (409).
  • Genel izinli liste: operatörün sunucu ayarıdır (CRX_AUTH_TRUSTED_ORIGINS: tam alan adı ya da *.alan). Ortamın kendi alan adı yoksa bu liste geçerlidir.
  • Bir alan adı başka bir ortama bağlıysa o alan adından gelen tarayıcı, bu ortama yazamaz ve bu ortamı okuyamaz (403).
  • Origin göndermeyen istemciler (oyun sunucunuz, curl gibi tarayıcı dışı istemciler) origin denetimine takılmaz; onları çerez kullanıyorlarsa CSRF çift gönderim koruması denetler.
  • Sec-Fetch-Site: cross-site ile Sec-Fetch-Mode: navigate birlikte geliyorsa durum değiştiren istek reddedilir.

Gerçek zamanlı bilet de aynı origin kuralına bağlıdır: bilet tek kullanımlıktır ve alındığı origin’e bağlıdır.

Not

Unity yerel derlemeleri, bilet ve WebSocket isteklerinde yapılandırmadan türetilen sabit bir Origin gönderir (BaseUrl’in şeması, alan adı ve varsayılan olmayan portu; NativeOrigin ile değiştirilebilir). Bu origin’in ortamın alan adlarında ya da genel izinli listede bulunması gerekir. Bkz. Unity SDK.

Alan adı bağlama adımları: Ortamlar ve alan adları.

Oyun sunucunuzdan çağrı

Oyun sunucunuz sunucu anahtarıyla Open Play’e doğrudan (HTTPS) bağlanır. Ters vekil, CORS ya da alan adı bağlaması gerekmez (CORS yalnız tarayıcıyı ilgilendirir); ortam anahtardan çıkar. Anahtarı yalnız sunucu ortamında (ortam değişkeni ya da gizli yönetimi) tutun.

Oyuncunun kimliğini sunucunuzda doğrulamak için erişim JWT’sini çevrimdışı doğrulayabilirsiniz: Sunucuda doğrulama.

Webhook’lar (Open Play → sizin sunucunuz)

Worker, olayları imzalı HTTPS isteği olarak sizin adresinize gönderir. Adresiniz genel erişilebilir olmalı ve imzayı doğrulamalıdır. Bkz. Webhook’lar ve Güvenlik.

Yayın öncesi kontrol

Üretim kontrol listesi alan adı bağlama, anahtar yönetimi, TLS ve izleme maddelerini toplar.

Yapay zekâ araçlarıyla kullanım (MCP)

Open Play’in MCP (Model Context Protocol) sunucusu, yapay zekâ yardımcınızın (“misafir girişi nasıl yapılır”, “oyuncu verisi nasıl kaydedilir”, “eklenti nasıl çağrılır” gibi sorulara) tahmin yürütmek yerine bu sitedeki resmî belgelerden ve SDK API’sinden cevap vermesini sağlar. Sunucu yalnızca okur: Open Play sunucunuza, anahtarlarınıza dokunmaz; belgeler ve SDK dizini yalnızca herkese açık belge sitesinden okunur.

Kurulum

Barındırılan sunucu (önerilen)

Hiçbir şey kurmadan https://open-play.b4klabs.dev/mcp adresine bağlanın (Streamable HTTP, yalnız okur, kimlik doğrulama yok):

claude mcp add --transport http open-play https://open-play.b4klabs.dev/mcp

Cursor için .cursor/mcp.json, VS Code için .vscode/mcp.json (mcpServers yerine servers anahtarıyla) dosyasına şunu ekleyin:

{
  "mcpServers": {
    "open-play": {
      "url": "https://open-play.b4klabs.dev/mcp"
    }
  }
}

Sunucu belge ve SDK dizinini https://open-play.b4klabs.dev/docs/mcp/index.json adresinden alır; dizin belge sitesiyle birlikte yenilenir, yani yeni bir belge yayımlandığında sunucuyu güncellemeniz gerekmez.

Yerel paket

Ağ yerine yerel süreç istiyorsanız paket @ferdiunal/open-play-mcp GitHub Packages üzerinden yayımlanır (kaynak: ferdiunal/open-play-mcp). Kullanıcı .npmrc dosyanıza @ferdiunal:registry=https://npm.pkg.github.com satırını ve read:packages izinli bir GitHub belirtecini ekleyin (belirteci belgeye ya da depoya yazmayın), sonra:

claude mcp add open-play -- npx -y @ferdiunal/open-play-mcp

Cursor ve VS Code için "command": "npx" ve "args": ["-y", "@ferdiunal/open-play-mcp"] kullanın. Yerel paket de aynı dizini canlı adresten alır; ağ yoksa son önbelleği, o da yoksa pakete gömülü anlık görüntüyü kullanır.

Araçlar

Araç adları ve açıklamaları İngilizcedir; belge içeriğini Türkçe istemek için lang: "tr" verin (varsayılan en).

AraçNe yaparÖrnek girdi
search_docsBelgelerde arar; başlık, ara başlık ve gövde sırasıyla puanlanır; sonuç sayfa kimliği, açıklama ve genel adres verir{"query": "guest sign in"}
read_docBir sayfanın tam markdown metnini verir (en çok 40 000 karakter){"page": "auth/guest"}
list_docsSayfa kimliklerini ve başlıklarını listeler; section ile tek bölüm{"section": "auth"}
search_sdkTypeScript ve Unity SDK’larında tür, işlev ve üye adı arar{"query": "signInGuest", "sdk": "typescript"}
get_sdk_symbolBir türün imzasını, özetini ve üyelerini verir; Tür.Üye biçimi desteklenir{"name": "PlayerSession", "sdk": "unity"}

Her belge sayfası ayrıca open-play-docs://<dil>/<sayfa> adresli bir kaynaktır (ör. open-play-docs://en/auth/guest) ve integrate_open_play adlı bir istem (platform: web, unity ya da server) entegrasyon adımlarını ve doğru sayfa bağlantılarını verir.

Sınır: Unity API dizini C# kaynağından hafif bir ayrıştırıcıyla çıkarılır; kalıtım ve genel tür parametreleri gibi ayrıntılar eksik olabilir. Kesin başvuru için Unity SDK sayfasındaki üretilmiş başvuruya bakın. SDK özetleri şimdilik Türkçedir, çünkü kaynak koddaki Türkçe yorumlardan gelir.

llms.txt ve llms-full.txt

MCP kullanamayan araçlar (ya da bir sohbete belge yapıştırmak istediğinizde) için belgeler llmstxt.org biçiminde düz metin olarak da yayımlanır:

Dosyaİçerik
https://open-play.b4klabs.dev/llms.txtİngilizce dizin: her sayfanın başlığı, bir satırlık açıklaması ve .md kopyasının adresi
https://open-play.b4klabs.dev/llms-full.txtİngilizce belgelerin tamamı tek dosyada
https://open-play.b4klabs.dev/docs/tr/llms.txtTürkçe dizin
https://open-play.b4klabs.dev/docs/tr/llms-full.txtTürkçe belgelerin tamamı tek dosyada

İngilizce dosyalar https://open-play.b4klabs.dev/docs/llms.txt ve https://open-play.b4klabs.dev/docs/llms-full.txt adreslerinde de durur. Her sayfanın markdown kopyası, HTML adresinin .html yerine .md ile bittiği adrestedir (ör. https://open-play.b4klabs.dev/docs/en/auth/guest.md). Dosyalar belge sitesiyle birlikte yenilenir.

Örnek sorular

  • “Bir oyuncuyu misafir olarak nasıl girdiririm?” → search_docs + read_doc (auth/guest).
  • “Oyuncu verisini nasıl kaydederim?” → guides/player-data.
  • “Bir eklentiyi nasıl çağırırım?” → plugins/calling-plugins.
  • “Unity’de PlayerSession hangi yöntemlere sahip?” → get_sdk_symbol.

İlk bakış için Hızlı başlangıç sayfasından başlayın. Ajanınızın oyununuzda Open Play’i doğru kullanması için deponuza koyacağınız hazır kural dosyası: Oyununuz için AGENTS.md.

Oyununuz için AGENTS.md

AGENTS.md, yapay zekâ kodlama ajanlarının (Claude Code, Cursor, Codex ve benzerleri) deponuzun kökünde otomatik okuduğu kural dosyasıdır. Open Play’i oyununuza bağlarken ajanların en sık yaptığı hatalar şunlardır: bir endpoint’i ya da header’ı tahmin etmek, sunucu anahtarını istemciye koymak, CSRF ve oturum kurallarını atlamak, eklenti şemasını sabitlememek, domain’i erken silmek ve eski önbellekteki JavaScript’i canlıda bırakmak. Bu sayfadaki şablon bu hataları önleyen kuralları tek dosyada toplar. Kurallar gerçek oyun entegrasyonlarında (ArenaRace ve LanternTide örnek oyunları) yaşanan sorunlardan çıkarılmıştır ve her kural ilgili belge sayfasına bağlanır.

Nasıl kullanılır

  1. Şablonu indirin: https://open-play.b4klabs.dev/docs/AGENTS.md (ya da aşağıdaki bloğu kopyalayın) ve oyun deponuzun köküne AGENTS.md olarak koyun. Zaten bir AGENTS.md dosyanız varsa şablonu “Open Play entegrasyonu” başlığı altına ekleyin.
  2. “Project facts” bölümündeki <YOUR_DEV_ENV_ID>, <YOUR_PROD_ENV_ID>, <OPEN_PLAY_BASE_URL>, <DEV_GAME_ORIGIN>, <PROD_GAME_ORIGIN> ve <SDK_VERSION> yer tutucularını kendi değerlerinizle değiştirin. Ortam kimlikleri gizli değildir; anahtarları ve sırları dosyaya yazmayın.
  3. Ajanınıza Open Play MCP sunucusunu ekleyin (Yapay zekâ araçlarıyla kullanım): claude mcp add --transport http open-play https://open-play.b4klabs.dev/mcp. Şablon ajana önce search_docs, read_doc, search_sdk ve get_sdk_symbol araçlarını kullanmasını söyler; böylece ajan davranışı hafızasından değil güncel belgeden alır.
  4. Oyununuzda kullanmadığınız bölümleri silin (örneğin eklenti kullanmıyorsanız 6. bölüm). Kendi kurallarınızı (kod stili, test komutları, dal düzeni) şablonun üstüne ya da altına ekleyin.

Şablonun kapsamı

BölümNeyi önler
MCP önceTahminle yazılmış endpoint, header ve alan adları
Anahtarlar ve ortamlarSunucu anahtarının istemciye sızması, dev ile prod karışması
Giriş ve oturumMisafir akışı, Google popup + yönlendirme yedeği, yönlendirme tabanı (/auth/callback), CSRF yarışı, tek aktif oturum, hesap silme
Domain ve tarayıcıKayıtsız origin, canlı domain’i erken silmek, proxy’nin header kaybı
Oyuncu verisiBilinmeyen kaydetme formatı için ayrı sunucu, If-Match olmadan yazma
EklentilerŞema uyuşmazlığı, yeni anahtarla tekrar gönderilen komut, sürüm yükseltmede kapalı izin
Gerçek zamanlıBilet yeniden kullanımı, tekrar oynatılan girdi, gecikmeli girdi akışı
Önbellek ve dağıtımCDN’in no-cache başlığını ezip eski JavaScript’i tutması
Hatalar ve sınırlarHata metnini ayrıştırmak, paylaşılan IP kovası
Test ve Yap / YapmaBitti demeden önce kontrol listesi

Şablonun dili İngilizcedir: yapay zekâ araçları ve belge sitesinin MCP dizini İngilizce metinle en iyi çalışır. Şablon ayrıca https://open-play.b4klabs.dev/docs/llms.txt dizininde “Optional” bölümünde listelenir.

Not

Şablon bir kural dosyasıdır, Open Play’in garantisi değildir. Davranışın tek kaynağı belge sayfalarıdır; şablon ile bir sayfa ayrışırsa sayfa doğrudur. Şablonu gerçek bir ajan oturumunda deneyip kendi oyununuza göre sıkılaştırmanızı öneririz.

Şablonun tamamı

# AGENTS.md — integrating this game with Open Play

> Drop this file in the root of your game repository. Replace every `<PLACEHOLDER>` and delete rules that do not apply.
> Source and updates: https://open-play.b4klabs.dev/docs/AGENTS.md

This game uses **Open Play**, a game backend platform: player identity and sessions, player data (cloud save), characters, economy, store, rewards, social features, leaderboards, notifications, plugins and realtime connections. The game talks to Open Play over HTTP and WebSocket, through the TypeScript SDK (`@ferdiunal/open-play-ts`) or the Unity SDK. The game does **not** run its own account or save server for anything Open Play provides.

Docs root: https://open-play.b4klabs.dev/docs/en/ — every rule below links to its page.

## 0. Project facts (fill in)

- Environment ids (the `x-crx-env` value): dev `<YOUR_DEV_ENV_ID>`, prod `<YOUR_PROD_ENV_ID>`. One environment per stage; data is never shared between them.
- Open Play base URL: `<OPEN_PLAY_BASE_URL>` (the API address the browser calls).
- Game origins registered per environment: `<DEV_GAME_ORIGIN>`, `<PROD_GAME_ORIGIN>`.
- SDK version (exact, no `^`, no `latest`): `<SDK_VERSION>`.

## 1. Use the Open Play MCP server first

Before you write any call to Open Play, **look it up**. Never guess an endpoint, header, field or error code.

```bash
claude mcp add --transport http open-play https://open-play.b4klabs.dev/mcp
```

Cursor (`.cursor/mcp.json`) and VS Code (`.vscode/mcp.json`, key `servers`): `{"mcpServers":{"open-play":{"url":"https://open-play.b4klabs.dev/mcp"}}}`.

- `search_docs` then `read_doc` (for example `auth/oauth`) for behavior and HTTP contracts.
- `search_sdk` then `get_sdk_symbol` (for example `PlayerSession`, `sdk: "typescript"` or `"unity"`) for exact SDK signatures.
- The server is read-only and public; it never needs or sees a key. If the docs and your memory disagree, the docs win.
- No MCP available: read https://open-play.b4klabs.dev/docs/llms.txt (index) or `llms-full.txt` (everything).

Docs: [Using AI tools (MCP)](https://open-play.b4klabs.dev/docs/en/getting-started/ai-tools.html).

## 2. Keys and environments

- **Client** code (browser, WebGL, mobile) knows only the environment id and the player session. It carries **no secret**.
- A **server key** (`crx_live_<prefix>_<secret>`) lives only on your own server or backend-for-frontend (BFF): in its secret store, never in the client bundle, the repository, logs, or `.env` files that are committed. Use a separate key per environment and service, with an expiry.
- A client key is not a server key; calling a server endpoint with it is rejected. Player operations use the player session, not a key.
- Never point a dev build at the prod environment or the reverse. Read the environment id from per-stage configuration.
- Do not hard-code or print secrets, tokens, cookies or `x-csrf-token` values in code, tests, logs or analytics.

Docs: [Security](https://open-play.b4klabs.dev/docs/en/operations/security.html), [API keys](https://open-play.b4klabs.dev/docs/en/panel/game-settings-api-keys.html), [Production checklist](https://open-play.b4klabs.dev/docs/en/operations/production-checklist.html).

## 3. Sign-in and sessions

- **Guest first.** Open a guest session on first launch so the player plays at once; offer Google (or email) later and **link** it to the same player. Linking a guest upgrades the same player id; progress is kept.
- **Google OAuth:** try a popup window and fall back to a full-page redirect when the popup is blocked. Open the popup synchronously inside the click handler, before any `await`. Let the callback page hand the result back (for example through `BroadcastChannel`) and close itself.
- The server builds the Google `redirect_uri` from the configured **redirect base + `/google`**. The base must end at `/auth/callback` (not `/google`); the redirect URI registered with Google must be exactly `<base>/google`, and that path must be a page of your game that finishes the sign-in.
- The OAuth `code` and `state` are single-use: finish exactly once, then remove them from the address bar (`history.replaceState`). Use the SDK helpers; they bind `state` to the browser tab.
- A cookie session sends `x-crx-env` on every call and a CSRF token on every state-changing request. The token is **single-use and rotates**: never send two mutations at once with the same token, and keep the new token from each response. The SDKs serialize this for you; share the token between tabs with the SDK `csrf.channel` option. Retry a CSRF `403` once with a fresh token.
- Session lifetime is 30 days sliding, 180 days absolute. On `401` refresh or sign in again; do not loop.
- If the environment allows a **single active session**, a newer sign-in revokes the older one. Handle `reauthRequired` with a clear "signed in on another device" screen and a sign-in button, not a silent retry.
- Show the sign-in methods you actually enabled; hide the rest. Do not use SDK methods the docs mark as unsupported.
- **Account deletion** is a store requirement (Apple, Google Play): provide an in-game "Delete my account" action (`POST /v1/auth/account/delete`, `confirm: "delete_my_account"`). A linked account must have signed in within the last 10 minutes (`recent_sign_in_required`): ask the player to sign in again, then repeat.
- Verify identity on your own game server with the access JWT and the JWKS, never by trusting a client-sent player id.

Docs: [Auth overview](https://open-play.b4klabs.dev/docs/en/auth/overview.html), [Guest](https://open-play.b4klabs.dev/docs/en/auth/guest.html), [Google sign-in](https://open-play.b4klabs.dev/docs/en/auth/oauth.html), [Sessions and CSRF](https://open-play.b4klabs.dev/docs/en/auth/sessions-and-csrf.html), [Account linking](https://open-play.b4klabs.dev/docs/en/auth/account-linking.html), [Account deletion](https://open-play.b4klabs.dev/docs/en/auth/account-deletion.html), [Server-side verification](https://open-play.b4klabs.dev/docs/en/auth/server-verification.html).

## 4. Domains and calling from the browser

- Register **every origin your game runs on** in each environment's domain list (Game settings, Environments and domains): one full domain per line, no wildcards, no ports, at most 10, and a domain belongs to one environment only. An unregistered origin gets `403` on writes.
- Call Open Play either directly (your game domain is in the list; the server sends CORS headers with credentials) or through a same-origin reverse proxy that forwards `Cookie`, `Origin`, `x-crx-env`, `x-csrf-token`, `Idempotency-Key`, `If-Match` and the WebSocket upgrade, and that forwards the real client IP.
- **Moving to a new domain:** add the new domain and the new OAuth redirect first, test it, and only then retire the old one. **Removing a domain from an environment locks that live host out immediately.** Guest identities are per-origin: a new domain starts new guests.
- Send `Origin` only as the browser sets it. Never fake it in game code.

Docs: [Architecture and hosting](https://open-play.b4klabs.dev/docs/en/getting-started/architecture-and-hosting.html), [Environments and domains](https://open-play.b4klabs.dev/docs/en/panel/game-settings-environments.html).

## 5. Player data (cloud save)

- Use the generic per-player key-value store; **do not build a game-specific save server**. The value is an opaque JSON object the server never interprets; your game owns the format and its version field.
- Limits: key `^[a-z0-9][a-z0-9._-]{0,63}$`, at most 16 keys per player, 64 KiB per value, 60 writes per minute per player. Store large integers as strings.
- **There is no blind write.** Every `PUT` sends exactly one of `If-None-Match: *` (create) or `If-Match: "<version>"` (update). On `409 version_conflict` read the remote record, merge it with your own rule, retry. `428` means a precondition header is missing.
- Batch saves (on level end, on pause, on a timer), not every frame. Keep a local copy and reconcile on sign-in; a guest-to-Google link keeps the same player.
- Never write personal data you do not need. Player data is erased with the account.

Docs: [Player data](https://open-play.b4klabs.dev/docs/en/guides/player-data.html), [Idempotency and concurrency](https://open-play.b4klabs.dev/docs/en/reference/idempotency-and-concurrency.html).

## 6. Plugins (game-specific server logic)

- Call plugins only through **generated bindings**, never hand-written DTOs or copied field names. Get the schema document from `GET /v1/plugins/<PLUGIN_ID>/schema` and generate bindings from it.
- **Pin** `pluginId`, `schemaSha256`, `protocolVersion`, `codecProfile` and the plugin version in your release manifest. A mismatch must **fail closed**: show an "update the game" screen, never talk to the server with the wrong fields. After every server update compare the live digest with the pin (at startup or in CI).
- Every command carries an `Idempotency-Key`, generated **once** per logical action and reused on retries. If the outcome is uncertain (timeout, drop), resend with the **same key and body** or ask the result query; never generate a new key for the same action.
- Retry only when it is safe: idempotent calls on network errors, `429`, `503`, with exponential backoff and jitter, and honor `Retry-After`. Never retry business rejections (`422`).
- `int64` values travel as decimal strings. Send `If-Match` / `expectedVersion` when writing a shared resource; on `409` re-read first.
- If a plugin call suddenly returns `403`, check that the plugin is **enabled for that environment** and that the enabled version matches the server version. A server version bump needs its grant switched too.

Docs: [Plugins overview](https://open-play.b4klabs.dev/docs/en/plugins/overview.html), [Bindings and pinning](https://open-play.b4klabs.dev/docs/en/plugins/bindings.html), [Calling plugins](https://open-play.b4klabs.dev/docs/en/plugins/calling-plugins.html).

## 7. Realtime

- Use the SDK realtime client; if you implement the protocol yourself follow it exactly (single-use ticket, `Origin`, subprotocol).
- Ask for a **new ticket on every connection**. On a drop reconnect with exponential backoff, `Join` again with the last cursor (durable streams) or without one (ephemeral state), and on `ResyncRequired` drop the cursor and join fresh.
- Unacknowledged inputs are **not replayed**; continue from the latest snapshot. Pipeline inputs inside the SDK window (default 4 in flight); one input per round trip makes fast games feel laggy on real networks.
- Keep the frame rate below 40 per second; at most 4 sockets per player. Handle idle close (`4100`) with a "Continue" screen. An open socket does not keep the session alive.
- Notification and chat sockets cannot be opened straight from a browser; use inbox polling or REST.

Docs: [Realtime overview](https://open-play.b4klabs.dev/docs/en/realtime/overview.html), [Protocol](https://open-play.b4klabs.dev/docs/en/realtime/protocol.html), [Reconnect and limits](https://open-play.b4klabs.dev/docs/en/realtime/reconnect-and-limits.html).

## 8. Caching and deploys

- Serve your game's JavaScript modules and config (including the file that holds the environment id) with `Cache-Control: no-cache`, or content-hash the filenames. A stale config file after a deploy silently runs the old integration.
- If a CDN sits in front, set it to **respect existing headers** for HTML, JS and CSS. A CDN default browser TTL can override your `no-cache` and keep old code for hours.
- Never put a CDN cache in front of API responses that carry a cookie or `Authorization`.
- Deploy order when Open Play changes a plugin: new server and grant, then client with regenerated bindings. Pin the SDK version exactly and upgrade from the changelog.

Docs: [Production checklist](https://open-play.b4klabs.dev/docs/en/operations/production-checklist.html), [Versioning](https://open-play.b4klabs.dev/docs/en/reference/versioning.html).

## 9. Errors and rate limits

- Core endpoints return **plain-text** errors; plugin endpoints return a JSON envelope (`code`, `message`, `retryable`, `requestId`). Decide by status code and JSON `code`, **never by parsing message text**. Log `requestId` / `x-request-id`.
- There is no general per-key quota, but identity endpoints are limited per client IP (for example 30 guest sign-ins per minute). Behind a proxy or a shared network (NAT, a hosting platform's shared egress) many players share one IP: forward the real client IP.
- Back off on `429`, `503` and network errors; wait `Retry-After` when present. Never loop on `401`, `403` or `422`.

Docs: [Errors](https://open-play.b4klabs.dev/docs/en/reference/errors.html), [Rate limits](https://open-play.b4klabs.dev/docs/en/reference/rate-limits.html).

## 10. Testing checklist (before you call it done)

1. Fresh browser profile: guest sign-in, play, reload, same player and progress.
2. Guest upgraded to Google keeps progress; a second browser signs in to the same account and loads the save.
3. Two tabs open: no CSRF `403` storm; sign-out in one tab is handled in the other.
4. Same command sent twice with the same `Idempotency-Key` gives one effect.
5. Two devices write the same player-data key: the `409` is merged, nothing is lost.
6. Kill the network mid-command and mid-socket: the game recovers without duplicate effects.
7. Prod build points at the prod environment id and prod origin; dev at dev. No secrets in the bundle (search it).
8. Run these in a **headless browser or test harness** you control; do not test against another team's environment or real player data.

## Do

- Look up docs through MCP first; link the page you used in the PR description.
- Keep Open Play calls in one module/service layer, one writer per feature (never write the same state both locally and through Open Play).
- Handle every SDK session state, including `reauthRequired`, `banned` and `logoutUncertain`.
- Add a test with each integration change; report failing checks, never skip them.

## Don't

- Do not guess endpoints, headers, field names or error codes.
- Do not put a server key, token or cookie in client code, tests, logs or commits.
- Do not use `latest` or git/vendored copies of the SDK; do not hand-copy DTOs.
- Do not bypass CSRF, `Origin` or idempotency checks "just for testing".
- Do not remove a domain, redirect URI or environment that a live host still uses.
- Do not store save data in browser storage alone once the player can sign in.
- Do not write retry loops without backoff, or retry non-idempotent calls with new keys.
- Do not run production deploys or destructive operations unless the user explicitly asks.

Sonraki adım: Hızlı başlangıç ve Üretim kontrol listesi.

Kimlik doğrulamaya genel bakış

Bu sayfa oyuncunun Open Play’e nasıl giriş yaptığını özetler: hangi yöntemler var, hepsinin paylaştığı kurallar neler ve ayrıntı için hangi sayfaya gidilir. Oyuncu girişini bağlıyorsanız önce buradan başlayın.

Giriş yöntemleri

YöntemNe zaman?SDKAyrıntı
MisafirHesapsız hızlı başlangıç; ilerleme aynı cihazda saklanırTS: signInGuest · Unity: SignInGuestAsyncMisafir girişi
E-posta ve parolaKalıcı hesap; parola sıfırlama ve değiştirme dahilTS ve Unity: kayıt, giriş, terfi, parola işlemleriE-posta ve parola
Google (OAuth)Tek tıkla web girişi; doğrulanmış e-posta varsa mevcut hesaba otomatik bağlanırTS: startGoogleOAuth, completeGoogleOAuth · Unity: yokGoogle ile giriş
Yerel platform kanıtı (Apple, Game Center, Play Games)Mobil yerel girişlerSDK yardımcısı henüz yok; yalnız HTTP ucuGoogle ile giriş › Yerel kanıtlar
Magic linkE-postaya tek kullanımlık bağlantıSDK yardımcısı henüz yok; sunucu ucu varE-posta ve parola › Magic link

Hangi yöntemlerin oyuncuya sunulacağı oyunun tercihidir; Google’ın çalışması için ortamda yapılandırılmış olması gerekir (Oyun ayarları › Giriş yöntemleri).

Giriş sonrası konular: Oturumlar ve CSRF, Hesap bağlama, Sunucuda doğrulama.

Hangi yöntemi seçmeliyim?

  • Hesap zorunlu değilse misafirle başlayın; oyuncu ilerledikçe misafir terfisi ile e-postaya ya da Google’a bağlayın. Oyuncu kimliği (playerId) değişmez.
  • Çoklu cihaz istiyorsanız e-posta/parola ya da Google gerekir; misafir kimliği cihazın sakladığı sırra bağlıdır.
  • Mobil yerel giriş (Apple, Game Center, Play Games) için HTTP uçları vardır, ancak SDK’larda yardımcı yoktur.

Her çağrıda geçerli kurallar

Aşağıdaki kurallar /v1/auth/* altındaki tüm uçlar için geçerlidir.

1. Ortam başlığı. Her istek x-crx-env: <ortam-kimliği> taşır. Yoksa ya da UUID değilse 401.

2. İçerik türü. Gövdeli istekler Content-Type: application/json ile gönderilir.

3. CSRF. GET dışındaki her istek, çift gönderim (double-submit) CSRF koruması ister: __Secure-crx.csrf çerezi ve eşleşen x-csrf-token başlığı. Her başarılı kullanımda belirteç döner ve değişir. Önce GET /v1/auth/csrf ile bir belirteç alırsınız. Ayrıntı: Oturumlar ve CSRF.

4. Origin denetimi. Tarayıcıdan gelen durum değiştiren isteklerin Origin alan adı ortama bağlı olmalıdır. Bkz. Mimari ve barındırma.

5. Alan adları snake_case. İstek ve yanıt gövdeleri snake_case kullanır (display_name, bootstrap_token, old_password). İki istisna: GET /v1/auth/session yanıtında displayName ve POST /v1/auth/session/display-name gövdesi/yanıtı (displayName). SDK’lar bu farkı sizden saklar ve camelCase sunar.

6. Hata gövdeleri. Hatalar çoğunlukla düz metindir (permission denied, rate limited…). JSON gövdeli tek anlamlı hata, banlı hesap için 403 {"code":"account_banned","message":"account banned"} yanıtıdır. Durum kodları: 400 geçersiz girdi, 401 ortam ya da oturum yok/geçersiz, 403 reddedildi (yanlış parola, CSRF, süresi dolmuş token — ayrım bilerek sızdırılmaz), 409 çakışma, 422 işlenemez, 429 oran sınırı. Ayrıntı: Hatalar.

Başarılı giriş yanıtı

Giriş, kayıt, yenileme ve OAuth callback uçları aynı zarfı döndürür ve iki çerez yazar (__Secure-crx.session, __Secure-crx.refresh; HttpOnly; Secure; SameSite=Lax; Path=/, Max-Age=15552000 = 180 gün üst sınır):

{
  "player": {
    "id": "0192…",
    "display_name": "Guest",
    "email": null,
    "email_verified": false,
    "is_guest": true,
    "image": null,
    "created_at": "2026-11-03T10:00:00+00:00"
  },
  "tokens": {
    "session_token": "<64 hex>",
    "refresh_token": "<64 hex>",
    "access_token": "<jwt>",
    "expires_in_secs": 900
  },
  "is_new_player": true
}
AlanAnlamı
player.idKalıcı oyuncu kimliği (UUID). Misafir terfisinde değişmez
player.is_guestHesap hâlâ misafir mi
tokens.session_tokenOpak oturum belirteci. Çerez yerine Authorization: Bearer ile de kullanılabilir
tokens.refresh_tokenYalnız /v1/auth/refresh ve /v1/auth/logout için
tokens.access_tokenKısa ömürlü (15 dk) EdDSA JWT; oyun sunucunuzun çevrimdışı doğrulaması içindir. Bkz. Sunucuda doğrulama
tokens.expires_in_secsErişim JWT’sinin ömrü (saniye)
is_new_playerÇağrı yeni oyuncu yarattıysa true

Uyarı

Tarayıcı oyunlarında oturum ve yenileme belirteçlerini JavaScript’te saklamayın; çerezler HttpOnly’dir ve bunun için vardır. SDK’lar belirteç gövdelerini durumuna, günlüğe ve hatalara koymaz.

Oran sınırları

Kimlik uçları, sabit pencereli oran sınırlarına tabidir. Aşımda 429 ve düz metin rate limited döner; Retry-After başlığı yoktur: pencere süresini kendiniz beklemelisiniz. Başarısız denemeyi hemen yeniden göndermeyin.

SınırAnahtarDeğer
Misafir açma (yeniden giriş dahil)IP30 / 60 sn
E-posta girişiIP5 / 60 sn
Başarısız e-posta girişi kilidiadres + IP10 / 15 dk (kilitliyken doğru parola da 429; başarılı giriş hakkı iade eder)
KayıtIP · adres10 / 60 sn · 5 / 15 dk
Misafir terfisiIP · misafir oyuncu · adres10 / 60 sn · 30 / 60 sn · 5 / 15 dk
Parola değiştirmeIP · hesap10 / 60 sn · 6 / 60 sn
Parola sıfırlama isteği (forgot) ve magic linkIP · adres5 / 60 sn · 5 / 15 dk
Parola sıfırlama (reset)IP5 / 60 sn (forgot ve magic link ile ortak kova)
OAuth başlatmaIP30 / 60 sn
OAuth callbackIP60 / 60 sn
Görünen ad değiştirmeoyuncu10 / saat

Sunucu önünde fazladan bir vekil varsa ve güvenilen vekil adresleri tanıtılmamışsa bütün oyuncular tek IP kovasına düşer. Bkz. Mimari ve barındırma.

Uç nokta özeti

Yöntem ve yolOturumAçıklamaSayfa
GET /v1/auth/csrfgerekmezCSRF belirteci üretirOturumlar ve CSRF
POST /v1/auth/sign-in/guestgerekmezMisafir girişiMisafir
POST /v1/auth/sign-up/emailgerekmezKayıtE-posta
POST /v1/auth/sign-in/emailgerekmezGirişE-posta
POST /v1/auth/guest/upgrade/emailmisafir oturumuMisafiri e-postaya terfi ettirirE-posta
POST /v1/auth/change-passwordvarParola değiştirir; tüm oturumlar kapanırE-posta
POST /v1/auth/forgot-passwordgerekmezSıfırlama e-postası isterE-posta
POST /v1/auth/reset-passwordgerekmezToken ile yeni parolaE-posta
POST /v1/auth/sign-in/magic-link · GET /v1/auth/magic-link/verifygerekmezMagic linkE-posta
GET /v1/auth/oauth/providersgerekmezOrtamda etkin sağlayıcılarOAuth
POST /v1/auth/sign-in/oauth/{provider}gerekmezOAuth başlatır ({url, state})OAuth
GET /v1/auth/callback/{provider} · POST (form)gerekmez ya da link için varOAuth tamamlarOAuth
POST /v1/auth/native/{provider}gerekmezYerel platform kanıtıOAuth › Yerel
POST /v1/auth/link/{provider}varKimlik bağlamayı başlatırHesap bağlama
POST /v1/auth/unlink/{provider}varKimlik bağını çözerHesap bağlama
GET /v1/auth/identitiesvarBağlı kimliklerHesap bağlama
GET /v1/auth/sessionvarOturum ve profilOturumlar
POST /v1/auth/session/display-namevarGörünen ad değiştirirOturumlar
POST /v1/auth/account/deletevarOyuncunun kendi hesabını siler (geri alınamaz)Hesabı silme
POST /v1/auth/refreshyenileme belirteciOturumu yeniler (rotasyon)Oturumlar
POST /v1/auth/logoutyenileme belirteciOturumu kapatırOturumlar
GET /v1/auth/jwksgerekmezJWT doğrulama anahtarlarıSunucuda doğrulama

/v1/auth/organization/* uçları (oyuncu organizasyonları) kullanımdan kalkmaktadır: çalışır, ancak her yanıtı Deprecation başlığı taşır ve bu belgede anlatılmaz. /v1/auth/admin/* uçları sunucu anahtarıyla oyuncu yönetimi içindir (liste, ban, yeniden adlandırma, oturum iptali) ve uç nokta başvurusunda listelenir.

Banlı hesaplar

Operatör bir oyuncuyu banladığında oyuncunun tüm oturumları iptal edilir ve açık gerçek zamanlı bağlantısı kapanır. Oyuncu girişte nasıl görünür:

  • Parolası doğru banlı e-posta hesabı: 403 ve {"code":"account_banned","message":"account banned"}. Yanlış parola ya da bilinmeyen adres ayrımsız 403 düz metindir; ban durumu sızmaz.
  • Banlı misafir: aynı bootstrap sırrıyla ya da banlı oyuncunun eski oturumunu sunarak misafir girişi yapılırsa yine 403 account_banned döner ve yeni oyuncu açılmaz.
  • GET /v1/auth/session banlı hesapta 401 döndürür (ban kodu sızmaz); refresh, OAuth callback ve misafir terfisi banlı sahibe ayrımsız reddedilir.
  • Yanıtta ban gerekçesi ya da bitişi yoktur.
  • SDK davranışı: TypeScript’te oturum durumu banned olur; Unity’de SessionStatus.Banned. İkisi de yeni misafir açmaz ve kayıtlı misafir sırrını silmez (banlı oyuncu yeni misafir açarak kaçamasın). Oyuncuya hesabın askıya alındığını gösterin; yeniden deneme döngüsü kurmayın.

Misafir girişi

Misafir girişi, oyuncuya e-posta ya da parola sormadan kalıcı bir oyuncu kimliği verir. Oyuncu, cihazında saklanan bir sır sayesinde aynı hesaba geri döner; sonra isterse e-posta ya da Google ile kalıcı hesaba geçer. Bu sayfa HTTP ucunu, sırrın yaşam döngüsünü ve SDK davranışını anlatır.

Nasıl çalışır?

  1. İstemci 32 baytlık rastgele bir sır üretir ve 64 küçük harfli onaltılık karakter olarak yazar (bootstrap_token). Bu sır, oyuncunun hesabının anahtarıdır.
  2. İstemci sırrı POST /v1/auth/sign-in/guest ile gönderir. Sunucu sırrın yalnız SHA-256 özetini saklar; ham sır kaydedilmez, günlüğe yazılmaz.
  3. Sır ilk kez geliyorsa yeni bir misafir oyuncu (display_name Guest) ve yeni bir oturum açılır (is_new_player: true). Aynı sır tekrar gelirse aynı oyuncu için yeni bir oturum açılır (is_new_player: false).
  4. Sır, kullandıkça kayan 180 günlük bir ömre sahiptir. Süresi dolmuş ya da emekliye ayrılmış (örneğin hesap kalıcıya terfi etmiş) bir sır 403 ile reddedilir.

Uyarı

Misafir sırrı bir yetkidir: sırrı bilen oyuncu hesabına girer. Başka oyunculara, günlüğe ya da analitiğe taşımayın.

İstek

Önce bir CSRF belirteci alın (Oturumlar ve CSRF); sonra:

POST /v1/auth/sign-in/guest
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>
cookie: __Secure-crx.csrf=<csrf-çerezi>

{"bootstrap_token":"<64 küçük harfli onaltılık karakter>"}

Gövde yalnız bootstrap_token alanını taşır.

Yanıt

200 ve ortak giriş zarfı:

{
  "player": { "id": "0192…", "display_name": "Guest", "email": null, "email_verified": false, "is_guest": true, "image": null, "created_at": "2026-11-03T10:00:00+00:00" },
  "tokens": { "session_token": "<64 hex>", "refresh_token": "<64 hex>", "access_token": "<jwt>", "expires_in_secs": 900 },
  "is_new_player": false
}

Ayrıca iki Set-Cookie (__Secure-crx.session, __Secure-crx.refresh) ve yeni CSRF belirteci (x-csrf-token başlığı) döner.

DurumAnlamı
400bootstrap_token biçimi bozuk (64 küçük harfli onaltılık karakter değil) ya da JSON bozuk
401x-crx-env yok ya da UUID değil
403Sır emekliye ayrılmış/süresi dolmuş, hesap silinmiş ya da CSRF/origin reddi (düz metin); banlı sahip için JSON {"code":"account_banned"}
429IP başına 30 / 60 sn aşıldı (yeniden girişler de sayılır)

Banlı misafir: istek, banlı bir oyuncuya ait eski bir oturum çerezi/Bearer taşıyorsa ya da sır banlı oyuncuya aitse yeni misafir açılmaz ve 403 account_banned döner; bu yanıt çerezleri silmez. Bkz. Banlı hesaplar.

Görünen ad

Misafirin adı Guest’tir. Oyuncu kendi adını POST /v1/auth/session/display-name ile değiştirebilir (gövde {"displayName":"…"}): ad NFC’ye çevrilir, kırpılır, iç boşluklar teklenir; kontrol/yön/sıfır genişlikli karakterler reddedilir; sonuç 2–24 karakter olmalıdır; oyuncu başına saatte 10 değişiklik. Unity’de Session.RenameAsync; TypeScript SDK’sında bu işlem için yöntem yoktur, ham REST kullanın.

TypeScript SDK

session.signInGuest() sırrı sizin yerinize üretir, istekten önce saklar ve CSRF belirtecini yönetir; böylece yanıt kaybolsa ya da çerez silinse aynı sır aynı misafire döner. session.restore() yalnızca mevcut oturumu geri yükler, misafir yaratmaz.

Sır saklama seçenekleri (createPlayerClient’ın guestBootstrap ayarı):

SeçenekDavranış
VarsayılanTarayıcıda localStorage (erişilemezse bellek); SSR/Node’da bellek
falseHiç saklanmaz; her signInGuest yeni bir misafir doğurur
GuestBootstrapStoreKendi deponuz (get/set/clear; Keychain, IndexedDB, dosya). Zaman aşımı 3 sn; depo hata verirse SDK belleğe düşer

Davranış kuralları:

  • Sır; logout, başarılı e-posta girişi/kaydı, upgradeGuest ve isGuest: false görülen oturumdan sonra silinir. Banlı durumdan yapılan logout sırrı silmez (banlı oyuncu yeni misafir açarak kaçamasın).
  • Sunucu sırrı reddederse (uç işleyicisinin 403’ü; CSRF/origin 403’ü ve 401 sayılmaz) SDK sırrı bir kez siler ve yeni sırla tek yeniden deneme yapar.
  • banned durumunda yeniden deneme yapılmaz; yeni misafir açılmaz.

Uyarı

Varsayılan localStorage deposu aynı origin’deki her betiğe okunabilir; bir XSS açığı sırrı çalıp misafir hesabını devralabilir. Yüksek değerli hesaplarda guestBootstrap: false kullanın ya da platformun güvenli deposunu verin.

import {
  ACCOUNT_BANNED_CODE,
  CrxError,
  createMemoryGuestBootstrapStore,
  createPlayerClient,
  type PlayerClient,
} from "@ferdiunal/open-play-ts";

/** Varsayılan davranış: misafir kimliği tarayıcıda `localStorage`'da saklanır ve aynı oyuncuya döner. */
export function createDefaultClient(environmentId: string): PlayerClient {
  return createPlayerClient({ baseUrl: window.location.origin, environmentId });
}

/** Kalıcı kimlik istemeyen oyun: her `signInGuest` yeni bir misafir oyuncu açar. */
export function createEphemeralGuestClient(environmentId: string): PlayerClient {
  return createPlayerClient({ baseUrl: window.location.origin, environmentId, guestBootstrap: false });
}

/** Sunucu tarafında çalışan kod için (SSR/Node) isteğe özel bellek deposu; sır sunucu belleğinden çıkmaz. */
export function createRequestScopedClient(baseUrl: string, environmentId: string): PlayerClient {
  return createPlayerClient({ baseUrl, environmentId, guestBootstrap: createMemoryGuestBootstrapStore() });
}

/** Misafir girişi; banlı hesabı ayrı bir ekranla karşılar. */
export async function signInGuestOrShowBan(client: PlayerClient, showBanned: () => void): Promise<void> {
  try {
    await client.session.signInGuest();
  } catch (error) {
    if (error instanceof CrxError && error.code === ACCOUNT_BANNED_CODE) {
      showBanned(); // client.session.state.status === "banned"; yeniden deneme döngüsü kurmayın.
      return;
    }
    throw error;
  }
}

SSR’da (sunucuda çizim) SDK ortam localStorage’ını kullanmaz ve sır belleğe yazılır. Kalıcı misafir kimliği isteyen bir SSR uygulaması, istek başına kendi deposunu guestBootstrap ile vermelidir; yukarıdaki createRequestScopedClient örneği yalnızca süreçlik bellek deposu kullanır.

Unity SDK

Session.SignInGuestAsync() aynı akışı uygular; sır OpenPlayOptions.SecretStore içinde saklanır. Varsayılan PlayerPrefs deposu düz metindir ve güvenli depo değildir; üretimde Keychain/Keystore destekli kendi ISecretStore uygulamanızı verin (Unity SDK). Session.RestoreAsync() misafir yaratmaz. Banlı hesapta durum SessionStatus.Banned olur.

Misafiri kalıcı hesaba çevirme

Oyuncu ilerlemesini kaybetmeden kalıcı hesaba geçebilir; player.id değişmez:

Terfiden sonra misafir sırrı emekliye ayrılır; cihazdaki sır artık işe yaramaz.

E-posta ve parola

E-posta ve parola, oyuncuya cihazlar arası kalıcı bir hesap verir. Bu sayfada kayıt, giriş, misafirden kalıcı hesaba terfi, parola değiştirme ve sıfırlama akışlarını, istek ve yanıt örnekleriyle ve SDK çağrılarıyla bulacaksınız. Tüm çağrılar ortak kuralları (ortam başlığı, CSRF) izler.

Kurallar

  • E-posta kırpılır ve küçük harfe çevrilir; tam bir @ içermeli ve alan kısmında nokta bulunmalıdır. Aynı ortamda bir adres yalnız bir hesapta olabilir.
  • Parola 8–128 karakter (Unicode karakter sayılır, bayt değil). Parola Argon2id ile saklanır.
  • Görünen ad zorunludur; 1–100 karakter kullanın (sunucu bu sınırı misafir terfisinde uygular). Oyuncu sonradan adını 2–24 karakter kuralıyla değiştirebilir (Misafir girişi › Görünen ad).
  • Bugün e-posta doğrulama ucu yoktur; kayıtta email_verified false döner.
  • SDK’lar istek göndermeden önce yalın sınır denetimi yapar (e-posta 3–320, parola en çok 1024, görünen ad en çok 256 birim; kontrol karakteri yok). Asıl politika sunucudadır.

Kayıt

POST /v1/auth/sign-up/email
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"email":"[email protected]","password":"<en az 8 karakter>","display_name":"Ayşe"}

201 ve ortak giriş zarfı (is_new_player: true, player.is_guest: false, player.email dolu) + oturum çerezleri.

DurumAnlamı
400E-posta biçimi ya da parola politikası ihlali
409Adres bu ortamda kayıtlı (gövde adresi tekrar etmez)
429IP başına 10 / 60 sn ya da adres başına 5 / 15 dk aşıldı

Giriş

POST /v1/auth/sign-in/email
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"email":"[email protected]","password":"<parola>"}

200 ve giriş zarfı (is_new_player: false).

  • Yanlış parola ile bilinmeyen adres aynı yanıtı alır (403 düz metin); hangisi olduğu sızmaz.
  • Parolası doğru ama banlı hesap 403 ve {"code":"account_banned","message":"account banned"} alır; çerez silinmez.
  • Aynı adres + IP için 15 dakikada 10 başarısız giriş hesabı kilitler; kilitliyken doğru parola da 429 alır. Başarılı giriş hakkı iade eder. IP başına ayrıca 5 / 60 sn sınırı vardır.
  • İstekte başka bir oyuncunun (örneğin aynı tarayıcıdaki eski misafirin) kullanılabilir oturumu varsa, giriş başarılı olduktan sonra o oturum ailesi iptal edilir. Aynı oyuncunun oturumuna dokunulmaz.

Misafir terfisi

Misafir oyuncuyu aynı player.id ile kalıcı hesaba çevirir; ilerleme korunur. Misafir oturumu gerekir (çerez ya da Authorization: Bearer <oturum-belirteci>).

POST /v1/auth/guest/upgrade/email
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"email":"[email protected]","password":"<parola>","display_name":"Ayşe"}

200; aynı player.id, is_guest: false, yalnız yeni oturum çifti. Terfi tek bir veritabanı işlemidir: kimlik eklenir, misafir sırrı emekliye ayrılır, eski oturumlar iptal edilir ve yeni oturum açılır.

DurumAnlamı
400E-posta, parola ya da ad doğrulaması
401Ortam başlığı ya da oturum yok
403Oturum etkin değil, başka ortama ait ya da CSRF reddi
409Adres bu ortamda başka bir hesapta kayıtlı; işlem geri alınır, misafir oturumu bozulmaz
422Oyuncu artık misafir değil (yinelenen ya da eşzamanlı istek)
429IP 10 / 60 sn, misafir oyuncu 30 / 60 sn ya da adres 5 / 15 dk aşıldı

Yanıt kaybolursa (bağlantı koptu) eski misafir oturumları iptal edilmiş olabilir; kurtarma yolu normal e-posta girişidir.

Parola değiştirme

Oturum gerekir. Eski parola doğrulanır; başarıda tüm oturumlar (bu dahil) iptal edilir ve çerezler silinir; oyuncu yeniden giriş yapar.

POST /v1/auth/change-password
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"old_password":"<mevcut>","new_password":"<yeni>"}

204. Hatalar: 400 yeni parola politikası ihlali, 403 oturum ya da eski parola reddi, 429 IP 10 / 60 sn ya da hesap 6 / 60 sn. Yanlış mevcut parola, giriş kilidinin sayacına da sayılır.

Parola sıfırlama

  1. POST /v1/auth/forgot-password ile sıfırlama e-postası istenir. Hesap olsa da olmasa da yanıt aynı 200’dür (hesap varlığı sızmaz).
  2. E-postadaki token ile POST /v1/auth/reset-password çağrılır. Token tek kullanımlıktır ve 1 saat geçerlidir. Başarıda tüm oturumlar iptal edilir.
POST /v1/auth/forgot-password
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"email":"[email protected]"}
POST /v1/auth/reset-password
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"token":"<64 hex>","new_password":"<yeni>"}

reset-password hataları: 400 yeni parola politikası ihlali (token yanmaz, aynı bağlantıyla yeniden denenebilir), 403 token geçersiz/kullanılmış/süresi dolmuş ya da CSRF (ayrım sızmaz), 429 sınır aşımı (5 / 60 sn, forgot ve magic link ile ortak IP kovası).

Uyarı

Bilinen kısıt: Sıfırlama e-postasındaki bağlantı oyununuzun sayfasına değil, sunucunun API kökündeki /v1/auth/reset-password?token=… adresine gider. Bu yolda GET ucu yoktur; yani bağlantıya tıklamak bir sayfa göstermez. Oyununuzda sıfırlamayı sunmak için oyuncunun e-postadaki token değerini kendi sıfırlama ekranınıza taşıması gerekir (örneğin kendi ters vekilinizde bu yolu bir oyun sayfasına yönlendirmek; bu yönlendirme bu repoda doğrulanmadı). E-posta gönderimi operatörün SMTP ayarına bağlıdır.

SDK

import { CrxError, ErrorKind, type PlayerClient } from "@ferdiunal/open-play-ts";

/** Yeni hesap açar ve oturumu başlatır. Adres kayıtlıysa sunucu 409 döner (`ErrorKind.Http`, status 409). */
export async function register(client: PlayerClient, email: string, password: string, displayName: string): Promise<void> {
  await client.session.signUpEmail({ email, password, displayName });
}

/** Mevcut hesapla girer. Yanlış parola ile bilinmeyen adres aynı yanıtı alır; hangisi olduğu sızmaz. */
export async function login(client: PlayerClient, email: string, password: string): Promise<void> {
  await client.session.signInEmail({ email, password });
}

/**
 * Misafir ilerlemesini koruyarak kalıcı hesaba geçer: aynı `playerId`, yeni oturum.
 * Önce misafir olarak girilmiş olmalıdır; adres başka bir hesapta kayıtlıysa 409 döner ve misafir oturumu bozulmaz.
 */
export async function upgradeGuestToEmail(client: PlayerClient, email: string, password: string, displayName: string): Promise<void> {
  if (client.session.state.status !== "authenticated") await client.session.signInGuest();
  try {
    await client.session.upgradeGuest({ email, password, displayName });
  } catch (error) {
    if (error instanceof CrxError && error.kind === ErrorKind.Http && error.status === 409) {
      throw new Error("Bu e-posta adresi başka bir hesapta kayıtlı.");
    }
    throw error;
  }
}

/** Parola sıfırlama isteği: hesap olsa da olmasa da yanıt aynıdır. */
export async function requestReset(client: PlayerClient, email: string): Promise<void> {
  await client.session.requestPasswordReset({ email });
}

/** E-postadaki token ile yeni parola belirler; başarıda tüm oturumlar kapanır, oyuncu yeniden giriş yapar. */
export async function finishReset(client: PlayerClient, token: string, newPassword: string): Promise<void> {
  await client.session.resetPassword({ token, newPassword });
}

/** Oturum açıkken parola değiştirir; başarıda tüm oturumlar (bu dahil) kapanır. */
export async function changePassword(client: PlayerClient, currentPassword: string, newPassword: string): Promise<void> {
  await client.session.changePassword({ currentPassword, newPassword });
}
TypeScriptUnityUç
signUpEmailSignUpEmailAsyncsign-up/email
signInEmailSignInEmailAsyncsign-in/email
upgradeGuestUpgradeGuestAsyncguest/upgrade/email
changePassword({ currentPassword, newPassword })ChangePasswordAsyncchange-password (gövdede old_password)
requestPasswordResetRequestPasswordResetAsyncforgot-password
resetPasswordResetPasswordAsyncreset-password

SDK’ların ortak davranışı:

  • Parola ve token yalnız istek gövdesine yazılır; oturum durumuna, olay dinleyicilerine, hata nesnelerine ve günlüğe girmez.
  • Kimlik uçlarında 403 yalnızca CSRF katmanı reddettiyse (yanıtta yeni bir x-csrf-token yoksa) SDK bir kez taze belirteçle yeniden dener.
  • İstek gönderildikten sonra bağlantı kaybolursa giriş uçları en çok bir oturum yoklaması yapar; sonuç belirsizse UnknownOutcome hatası alırsınız. Parola değiştirme ve sıfırlama gibi sonuç döndürmeyen çağrılarda da kör yeniden deneme yapılmaz.
  • upgradeGuest oturum yokken ağ isteği göndermez; sunucunun kesin ret yanıtları (4xx) misafir oturumunu düşürmez.
  • changePassword başarıda logout ile aynı geçişi uygular (durum signedOut).

Unity örneği:

using System.Threading.Tasks;
using OpenPlay;
using OpenPlay.Session;
using OpenPlay.Support;

/// <summary>E-posta ve parola akışları. Parola SecretText ile taşınır; günlüğe ve ToString()'e girmez.</summary>
public static class EmailAuth
{
    public static Task<SessionState> Register(OpenPlayClient client, string email, string password, string displayName) =>
        client.Session.SignUpEmailAsync(new SignUpEmailInput { Email = email, Password = new SecretText(password), DisplayName = displayName });

    public static Task<SessionState> Login(OpenPlayClient client, string email, string password) =>
        client.Session.SignInEmailAsync(new SignInEmailInput { Email = email, Password = new SecretText(password) });

    /// <summary>Misafir ilerlemesini korur: aynı oyuncu, yeni oturum. Adres kayıtlıysa HTTP 409 gelir.</summary>
    public static async Task<SessionState> UpgradeGuest(OpenPlayClient client, string email, string password, string displayName)
    {
        try
        {
            return await client.Session.UpgradeGuestAsync(new UpgradeGuestInput { Email = email, Password = new SecretText(password), DisplayName = displayName });
        }
        catch (OpenPlayException ex) when (ex.Kind == ErrorKind.Http && ex.Status == 409)
        {
            throw new System.InvalidOperationException("Bu e-posta adresi başka bir hesapta kayıtlı.");
        }
    }

    public static Task RequestReset(OpenPlayClient client, string email) =>
        client.Session.RequestPasswordResetAsync(new PasswordResetRequestInput { Email = email });

    /// <summary>E-postadaki token ile yeni parola; başarıda tüm oturumlar kapanır.</summary>
    public static Task FinishReset(OpenPlayClient client, string token, string newPassword) =>
        client.Session.ResetPasswordAsync(new ResetPasswordInput { Token = new SecretText(token), NewPassword = new SecretText(newPassword) });

    /// <summary>Görünen ad kuralı: normalize sonrası 2..24 karakter, saatte 10 değişiklik.</summary>
    public static Task<SessionState> Rename(OpenPlayClient client, string displayName) =>
        client.Session.RenameAsync(displayName);
}

Magic link, e-postaya tek kullanımlık bir bağlantı gönderir. Sunucu uçları vardır, SDK yardımcısı henüz yoktur ve oyun içi bir akış olarak belgelenmemiştir.

POST /v1/auth/sign-in/magic-link
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"email":"[email protected]","name":"Ayşe"}

200 (e-posta varlığı sızmaz); token yalnız e-postadadır, 5 dakika geçerlidir ve tek kullanımlıktır. Sınır: IP 5 / 60 sn, adres 5 / 15 dk. Token GET /v1/auth/magic-link/verify?token=<token> ile tüketilir; başarıda 200, giriş zarfı ve oturum çerezleri döner, geçersiz token 403 verir.

Uyarı

verify ucu da x-crx-env başlığı ister. Tarayıcıda e-postadaki bağlantıya doğrudan tıklamak bu başlığı göndermez ve 401 alır. Bir magic link akışı kurmak istiyorsanız token’ı oyun sayfanızın alıp başlıklı bir istekle doğrulaması gerekir; bu akış SDK’larda ve bu belgede kapsanmaz.

Google ile giriş

Google ile giriş, oyuncunun tek tıkla web üzerinden girmesini sağlar. Bu sayfa akışın iki adımını (başlatma ve tamamlama), callback sayfanızın ne yapması gerektiğini, mevcut e-posta hesabına otomatik bağlanma kuralını ve SDK’sı olmayan yerel platform kanıtlarının HTTP sözleşmesini anlatır.

Not

Web için SDK yardımcıları bugün yalnız Google içindir. Sunucuda Apple form callback’i ve genel bir sağlayıcı kaydı da bulunur, ancak onlar için SDK yardımcısı ve bu belgede ayrıntı yoktur.

Ön koşul: sağlayıcıyı yapılandırın

Google yalnız yapılandırılmış ortamlarda çalışır. Operatör (panelde süper yönetici) Oyun ayarları › Giriş yöntemleri › Google › OAuth bilgilerini düzenle ekranına Google istemci kimliğini, istemci sırrını ve yönlendirme tabanını girer; sır yalnız yazılır, bir daha okunamaz. Yapılandırılmamışsa GET /v1/auth/oauth/providers boş liste döner ve başlatma isteği 403 alır.

Google Cloud konsolundaki Authorized redirect URI, sunucunun kullandığı redirect_uri ile birebir aynı olmalıdır: <yönlendirme tabanı>/google. Örneğin taban https://oyun.example.com/auth/callback ise Google’a https://oyun.example.com/auth/callback/google kaydedilir. Bu adres, sizin oyun sayfanızdır (aşağıdaki callback sayfası). Oyuncu Google’dan oraya code ve state ile döner. Tabanın kendisi /google ile bitmemelidir: https://oyun.example.com/auth/callback/google yazılırsa sunucu …/google/google üretir (Google redirect_uri_mismatch verir); panel ve API bu değeri düzeltilmiş örnekle reddeder. Yapılandırma ayrıntıları operatöre aittir; panel yardımı: Giriş yöntemleri.

Akış

  1. Oyun sayfası POST /v1/auth/sign-in/oauth/google ile {url, state} alır ve oyuncuyu url’ye yönlendirir.
  2. Oyuncu Google’da onay verir; Google oyuncuyu <yönlendirme tabanı>/google?code=…&state=… adresine döndürür.
  3. Callback sayfası GET /v1/auth/callback/google?code=…&state=… çağrısıyla oturumu tamamlar. Yanıt ortak giriş zarfıdır ve oturum çerezlerini yazar.

1. Başlatma

POST /v1/auth/sign-in/oauth/google
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{"callback_url":"https://oyun.example.com/auth/callback/google"}
{"url":"https://accounts.google.com/o/oauth2/v2/auth?client_id=…&state=…&code_challenge=…","state":"<64 hex>"}
  • url PKCE (code_challenge), state ve OIDC nonce taşır; state 10 dakika geçerlidir.
  • callback_url zorunludur (boşsa 400), ancak yönlendirmeyi belirlemez: Google’a giden redirect_uri sunucuda yapılandırılan tabandan gelir. Adresi oyun sayfanızla tutarlı verin.
  • 403: sağlayıcı bilinmiyor, yapılandırılmamış ya da devre dışı. 429: IP başına 30 / 60 sn.

2. Tamamlama

GET /v1/auth/callback/google?code=<code>&state=<state>
x-crx-env: <ortam-kimliği>
cookie: <varsa oturum çerezi>

Yanıt 200 ve giriş zarfı + çerezler. Tarayıcıda fetch(..., { credentials: "include" }) kullanın; çağrı ya API ile aynı origin’e gider ya da oyunun alan adı ortamın alan adı listesindeyse doğrudan Open Play adresine (CORS; bkz. Mimari ve barındırma). GET olduğu için CSRF belirteci istemez.

DurumAnlamı
403state yok/tüketilmiş/süresi dolmuş, sağlayıcı reddi ya da imza/nonce hatası; bağlama niyetinde oturumsuz ya da başka oyuncunun oturumu. Ayrım sızdırılmaz
409already exists: account_already_linked: Google kimliği başka bir oyuncuya bağlı; birleştirme yoktur
422unprocessable: provider_already_linked: oyuncuda bu sağlayıcı zaten bağlı
429Callback sınırı: IP başına 60 / 60 sn

code ve state tek kullanımlıktır; callback’i ikinci kez çağırmayın.

TypeScript SDK

SDK state’i sizin yerinize tarayıcıya bağlar: başlatmada sessionStorage’a (crx-oauth-pending:v1) {state, provider, intent, startedAt} yazar, tamamlamada aynı sekmede denetler. Kayıt yoksa, 10 dakikadan eskiyse, bozuksa ya da sağlayıcı/state uyuşmuyorsa ağ isteği hiç gönderilmez ve oturum durumu değişmez; CrxError kodu OAuthBindingCode.Missing (yok/süresi dolmuş/bozuk) ya da OAuthBindingCode.Mismatch (başka tarayıcı/sekme) olur. Bu, başkasının callback bağlantısıyla oturum devralmayı engeller.

import { CrxError, OAuthBindingCode, type PlayerClient } from "@ferdiunal/open-play-ts";

// Google'ın kullanıcıyı döndürdüğü sayfa: sunucuda yapılandırılan yönlendirme tabanı + "/google" ile aynı yol olmalıdır
// (örn. taban https://oyun.example.com/auth/callback ise sayfa https://oyun.example.com/auth/callback/google).
const CALLBACK_PATH = "/auth/callback/google";

/** 1. adım: Google'a yönlendirin. State'i saklamanız gerekmez; SDK onu bu sekmeye bağlar (sessionStorage). */
export async function beginGoogleSignIn(client: PlayerClient): Promise<void> {
  const callbackUrl = `${window.location.origin}${CALLBACK_PATH}`;
  const { url } = await client.session.startGoogleOAuth(callbackUrl);
  window.location.assign(url);
}

/** Misafir (ya da kayıtlı) oyuncunun hesabına Google kimliğini bağlar; tamamlama aynı callback sayfasında yapılır. */
export async function beginGoogleLink(client: PlayerClient): Promise<void> {
  const callbackUrl = `${window.location.origin}${CALLBACK_PATH}`;
  const { url } = await client.session.linkOAuth("google", callbackUrl);
  window.location.assign(url);
}

/** 2. adım: Google'ın döndüğü sayfada (`/auth/callback/google`) code ve state ile oturumu tamamlayın. */
export async function finishGoogleSignIn(client: PlayerClient): Promise<string> {
  const params = new URL(window.location.href).searchParams;
  const code = params.get("code");
  const state = params.get("state");
  history.replaceState(null, "", window.location.pathname); // code ve state adres çubuğunda kalmasın
  if (code === null || state === null) throw new Error("Callback adresinde code ya da state yok.");
  try {
    const session = await client.session.completeGoogleOAuth(code, state);
    return session.playerId;
  } catch (error) {
    if (error instanceof CrxError && error.code === OAuthBindingCode.Mismatch) throw new Error("Giriş başka bir tarayıcıda başlatılmış.");
    if (error instanceof CrxError && error.code === OAuthBindingCode.Missing) throw new Error("Giriş süresi dolmuş; yeniden başlatın.");
    throw error;
  }
}

/** Hesabıma bağlı sağlayıcı kimlikleri (sağlayıcı hesap kimliği ve e-posta dönmez). */
export async function linkedProviders(client: PlayerClient): Promise<string[]> {
  const result = await client.session.listIdentities();
  return result.identities.map((identity) => identity.provider);
}
  • startGoogleOAuth(callbackUrl) = startOAuth('google', callbackUrl); completeGoogleOAuth(code, state) = completeOAuth({ provider, code, state }).
  • linkOAuth(provider, callbackUrl) bağlama akışını başlatır (aşağıya bakın); tamamlama yine completeOAuth iledir.
  • listIdentities() bağlı kimlikleri verir.
  • Callback sayfasında code ve state’i hemen adres çubuğundan kaldırın (history.replaceState).
  • Unity SDK’sında OAuth yardımcısı yoktur.

Uyarı

TypeScript SDK’sında startIdentityTransfer adlı bir yöntem görünür; sunucuda karşılığı yoktur (operatör destekli kimlik devri iptal edilmiştir). Kullanmayın.

Misafir ilerlemesini Google’a bağlama

Oturum açık bir oyuncu (misafir ya da kayıtlı) için linkOAuth kullanılır: POST /v1/auth/link/{provider} oturum ister ve {url, state} döner; tamamlama aynı callback ucudur, ancak callback isteği başlatan oyuncunun oturumuyla gelmelidir (çerez ya da Bearer), aksi halde 403.

  • Oyuncu misafirse bağlama bir terfidir: aynı player.id, is_guest: false, misafir sırrı emekliye ayrılır ve yeni oturum açılır. Misafirin kendi koyduğu ad korunur; ad hâlâ varsayılan Guest ise Google adı uygulanır.
  • Oyuncu kayıtlıysa yalnızca yeni kimlik eklenir; adına dokunulmaz.
  • Sağlayıcı başına tek kimlik vardır (422 provider_already_linked).
  • Google kimliği başka bir oyuncuya bağlıysa 409 account_already_linked döner; ilerlemeler birleştirilmez.

Ayrıntılar: Hesap bağlama.

E-postayla otomatik bağlanma

Google ile yeni bir kimlikle giren oyuncunun e-postası, mevcut bir e-posta/parola hesabıyla eşleşiyorsa Google kimliği o hesaba otomatik bağlanır ve oyuncu o hesaba girer. Kuralın koşulları:

  • E-posta Google tarafından doğrulanmış olmalıdır ve Google bu e-postanın güncel sahipliğine kefil olmalıdır: alan adı gmail.com/googlemail.com ya da imzalı ID token’ın hd talebi e-postanın alan adına eşit (Google Workspace). Üçüncü taraf bir adreste email_verified tek başına yetmez.
  • Eşleşme oyuncunun profil e-postasıyla (players.email) yapılır; parolaya, mevcut oturumlara ya da profil e-postasına dokunulmaz.
  • Eşleşen hesap banlıysa bağ yazılmaz ve giriş reddedilir.
  • Eşleşen hesapta zaten başka bir Google kimliği varsa otomatik bağlanma yapılmaz; yeni bir oyuncu açılır.
  • Kayıt kapalı olsa da çalışır (yeni kayıt değildir).
  • Bu davranışı kapatan bir ayar yoktur; yerel (native) ID-token yolunda bu bağlama uygulanmaz.
  • Misafirin Google’a bağlanmasında (terfi) Google e-postası başka bir oyuncunun e-postasıyla eşleşiyorsa birleştirme yapılmaz; 409 account_already_linked döner.

Hesapta saklananlar: Google kimliği yalnız (ortam, sağlayıcı, sub) ile tanınır. Google’ın ad, doğrulanmış e-posta, dil ve profil görseli adresi kimliğe yazılabilir; sağlayıcının erişim ve yenileme belirteçleri saklanmaz. Bu bilgiler yalnız operatör panelinde görünür.

Yerel platform kanıtları

Mobil yerel girişler için tek bir uç vardır; SDK yardımcısı henüz yoktur (TypeScript’te ve Unity’de yok). Uç, ortak CSRF kurallarına tabidir ve çerez yazmaz: yanıtı Bearer olarak kullanın.

POST /v1/auth/native/{provider}
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>
providerGövde
apple, google{"id_token":"<OIDC ID token>","nonce":"<ham nonce>"}
gamecenter{"proof":{"player_id":"…","bundle_id":"…","public_key_url":"…","signature_b64":"…","salt_b64":"…","timestamp_ms":1700000000000,"game_player_id":"…"}}
playgames{"proof":{"server_auth_code":"…","application_id":"…"}}

200 ve {player, tokens, is_new_player} (çerez yok). 400: sağlayıcı yerel girişe uygun değil ya da ortamda kayıtlı değil, zarf biçimi bozuk. 403: kanıt reddedildi (imza, sertifika zinciri, paket kimliği, zaman aşımı, nonce gibi hiçbir ayrım sızdırılmaz). Kullanım için ortamda ilgili platformun yapılandırılmış olması gerekir.

Not

Yerel kanıt yolu e-postayla otomatik bağlanma uygulamaz; aynı kişi yerel ve web yoluyla ayrı oyunculara bölünebilir.

Oturumlar ve CSRF

Giriş yapan oyuncuya bir oturum verilir. Bu sayfa oturumun nasıl taşındığını, ne kadar yaşadığını, nasıl yenilenip kapatıldığını ve durum değiştiren isteklerde zorunlu olan CSRF (çapraz site istek sahteciliği) korumasını anlatır. SDK kullanıyorsanız çoğunu sizin yerinize yönetir; ham REST yazıyorsanız bu sayfa sözleşmedir.

Oturum nasıl taşınır?

Sunucu oturumu iki yerden birinden okur; önce çerez, yoksa Authorization başlığı:

KanalKim kullanır?Biçim
ÇerezTarayıcı oyunları__Secure-crx.session=<64 hex> (HttpOnly; Secure; SameSite=Lax; Path=/)
BearerÇerezsiz istemciler (komut satırı, bazı yerel istemciler)Authorization: Bearer <session_token> (giriş yanıtındaki opak belirteç)

Her oyuncu isteği ayrıca x-crx-env: <ortam-kimliği> taşır. Bearer yolunda taşınan opak oturum belirtecidir; erişim JWT’si değildir (o, oyun sunucunuzun çevrimdışı doğrulaması içindir: Sunucuda doğrulama).

Oturumu okuma

GET /v1/auth/session
x-crx-env: <ortam-kimliği>
{
  "environment_id": "0192…",
  "player_id": "0192…",
  "session_id": "0192…",
  "is_guest": false,
  "active_organization_id": null,
  "displayName": "Ayşe",
  "email": "[email protected]"
}

email misafir ve e-postasız (yalnız Google) hesaplarda null’dır. Yanıt kişisel veri taşır ve Cache-Control: private, no-store ile gelir. Oturum yoksa, iptal edilmişse, süresi dolmuşsa ya da hesap banlıysa 401 döner (ayrım sızmaz).

Süreler

ÖğeSüre
Oturum ve yenileme penceresi30 gün, kayan
Mutlak üst sınır180 gün (oturum ailesinin başlangıcından; hiçbir yolla aşılmaz)
Erişim JWT’si15 dakika
Çerez Max-Age15 552 000 sn (180 gün); yalnız üst sınırdır, karar sunucudadır
CSRF çerezi15 dakika
  • Kayma: oyuncu oturumla günde en az bir kez istek yaptığında oturum bitişi 30 gün ileri alınır (üst sınırı aşmadan). Her yenileme (refresh) de süreyi 30 güne çeker.
  • 30 gün hiç istek yapmayan oturum ölür. 180. günde aktif oturum bile biter; oyuncu yeniden giriş yapar (misafir, aynı sırla aynı hesaba döner).
  • Gerçek zamanlı bağlantıyı açık tutmak oturumu uzatmaz.

Yenileme (refresh)

POST /v1/auth/refresh
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{}

Yenileme belirteci gövdede ({"refresh_token":"…"}) ya da __Secure-crx.refresh çerezinde taşınır (gövde öncelikli; çerez varsa gövde boş olabilir). Yanıt 200 ve yeni oturum ve yenileme çifti (çerezler de yenilenir; ortak giriş zarfı). Eski yenileme belirteci kullanılmış sayılır.

  • Yeniden kullanım tespiti: kullanılmış bir yenileme belirtecinin tekrar sunulması tüm oturum ailesini iptal eder (çalıntı belirteç savunması).
  • Hata: 403 (belirteç geçersiz, süresi dolmuş, yeniden kullanılmış ya da CSRF reddi).
  • SDK’lar yenilemeyi gerektiğinde kendisi yapar; siz çağırmazsınız.

Çıkış

POST /v1/auth/logout
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{}

204; oturum iptal edilir ve çerezler silinir (Max-Age=0). Çağrı idempotenttir: bilinmeyen belirteç sessizce başarılı olur. Yenileme belirteci hiç yoksa (çerez ve gövde boşsa) 400 döner.

Parola değiştirme, parola sıfırlama ve operatör banı da oturumları iptal eder; ilgili sayfalara bakın.

Tek aktif oturum (isteğe bağlı)

Panelde Oyun ayarları › Giriş yöntemleri › Giriş ayarları › Tek aktif oturum anahtarı (yalnız süper yönetici), ortam başına açılır. Açıkken oyuncuya yeni oturum veren her giriş yolu, yeni oturum yazıldıktan sonra oyuncunun diğer oturumlarını iptal eder (“en son giriş kazanır”).

  • Anahtar açıldığı anda açık oturumları kapatmaz; yalnız bir sonraki girişten itibaren uygulanır.
  • Yenileme (refresh) bu politikayı tetiklemez.
  • Diğer cihazdaki oturum bir sonraki istekte 401 alır; SDK durumu reauthRequired olur. Oyununuz bu durumda “başka bir cihazdan giriş yapıldı” gibi bir ekran gösterebilir.

CSRF çift gönderim

Çerezle taşınan oturum, tarayıcı tarafından başka sitelerin isteklerine de otomatik eklenir. Bunu önlemek için durum değiştiren isteklerde çift gönderim (double-submit) CSRF koruması uygulanır: aynı belirteç hem çerezde hem başlıkta gelmelidir.

Kapsam. Aşağıdaki isteklerde zorunludur: /v1/auth/* altındaki tüm GET dışı istekler (giriş uçları dahil); oyuncu oturumuyla çağrılan eklenti komutları (ve POST ile çağrılan sorgular); oyuncu mağaza satın alma (yalnız çerez oturumunda; Authorization: Bearer oturumunda yalnız Origin ve Fetch Metadata denetimi uygulanır); gerçek zamanlı bilet isteği. GET istekleri muaftır. Sunucu anahtarıyla yapılan çağrılar CSRF’ye tabi değildir.

Akış.

  1. GET /v1/auth/csrf çağrılır. Yanıt: gövde {"token":"<hex>"}, başlık x-csrf-token: <aynı belirteç> ve Set-Cookie: __Secure-crx.csrf=<mühürlü grant>.
  2. Her durum değiştiren istek, çerezi (__Secure-crx.csrf) ve başlığı (x-csrf-token) birlikte gönderir. Tarayıcı çerezi kendisi ekler; başlığı kodunuz ekler.
  3. Başarılı her istekte belirteç döner (rotate): yanıt, yeni x-csrf-token başlığı ve yeni Set-Cookie taşır. Bir sonraki istekte yenisini kullanın.
  4. Belirteç tek kullanımlıktır. Eski belirteçle tekrar gönderilen istek 403 alır (yeniden oynatma koruması).
# Belirteç al
TOKEN="$(curl -sS -c "$JAR" "$OPEN_PLAY_URL/v1/auth/csrf" | jq -r .token)"

# Kullan; yanıt başlığındaki yeni belirteci bir sonraki istek için sakla
curl -sS -D headers.txt -b "$JAR" -c "$JAR" -X POST "$OPEN_PLAY_URL/v1/auth/logout" \
  -H "content-type: application/json" -H "x-crx-env: $OPEN_PLAY_ENV" -H "x-csrf-token: $TOKEN" -d '{}'
TOKEN="$(grep -i '^x-csrf-token:' headers.txt | awk '{print $2}' | tr -d '\r')"

Dikkat edilecekler:

  • Eşzamanlı iki mutasyonu aynı belirteçle göndermeyin; biri başarılı olunca diğeri 403 alır. İstekleri sıraya koyun. SDK’lar bunu tek bir kilitle yapar.
  • CSRF çerezi 15 dakika yaşar; süresi dolunca 403 alırsınız. Taze belirteç alıp bir kez yeniden deneyin. 403 düz metindir ve CSRF ile yetki reddi arasında ayrım yapmaz; yanıtta yeni bir x-csrf-token başlığı gelmediyse bunun CSRF katmanı reddi olduğunu anlarsınız.
  • Origin denetimi CSRF’nin ilk katmanıdır: tarayıcı isteğinin Origin alan adı ortama bağlı olmalıdır. Bkz. Mimari ve barındırma.
  • Çerezsiz (Bearer) istemciler de /v1/auth/* ve eklenti uçlarında CSRF çerezini ve başlığını taşımalıdır (mağaza satın almadaki Bearer kolu istisnadır). Unity SDK’sı bunu yerel derlemede çerezleri kendisi yöneterek, WebGL’de tarayıcı çerezleriyle yapar.

SDK ve oturum durumları

TypeScript session.state.status değerleri:

DurumAnlamı
signedOutOturum yok
authenticatingGiriş ya da geri yükleme sürüyor
authenticatedOturum açık; playerId, sessionId, isGuest, displayName, email dolu
refreshingYenileme sürüyor
reauthRequiredOturum kaybedildi ya da yenilenemedi; yeniden giriş gerekir
signingOut / logoutUncertainÇıkış sürüyor / sonucu belirsiz (yeniden denenebilir)
bannedHesap banlı; terminal

Unity’de karşılığı SessionStatus (SignedOut, Authenticating, Authenticated, Refreshing, ReauthRequired, SigningOut, LogoutUncertain, Banned, Disposed) değerleridir; durum değişimleri Session.StateChanged olayıyla gelir.

import { CrxError, ErrorKind, createPlayerClient, type PlayerClient, type SessionState } from "@ferdiunal/open-play-ts";

/** Oturum durumunu arayüze bağlar; `state.status` değerleri kapalı bir kümedir. */
export function watchSession(client: PlayerClient, render: (label: string) => void): () => void {
  const label = (state: SessionState): string => {
    switch (state.status) {
      case "authenticated": return state.isGuest === true ? "Misafir" : (state.displayName ?? "Oyuncu");
      case "banned": return "Hesap askıya alındı";
      case "reauthRequired": return "Yeniden giriş gerekiyor";
      case "signedOut": return "Giriş yapılmadı";
      default: return "…"; // authenticating, refreshing, signingOut, logoutUncertain
    }
  };
  render(label(client.session.state));
  return client.session.subscribe((state) => render(label(state))); // dönen işlev aboneliği kapatır
}

/** Sayfa kapanırken: sunucu oturumunu kapatmak için `logout`, yalnız yerel kaynakları bırakmak için `dispose`. */
export async function leave(client: PlayerClient, signOut: boolean): Promise<void> {
  try {
    if (signOut) await client.session.logout();
  } catch (error) {
    if (!(error instanceof CrxError && error.kind === ErrorKind.UnknownOutcome)) throw error;
    // Yanıt kaybolduysa sunucu oturumu kapanmamış olabilir (durum `logoutUncertain`); gerekirse logout'u yeniden deneyin.
  } finally {
    await client.dispose();
  }
}

export function createClient(environmentId: string): PlayerClient {
  return createPlayerClient({ baseUrl: window.location.origin, environmentId });
}

dispose() yalnızca yerel kaynakları bırakır; sunucu oturumunu kapatmak için önce logout() çağırın. AuthenticatedSession.email yalnız oyuncunun kendi ekranı içindir; günlüğe ve analitiğe yazmayın.

Hesap bağlama

Bir oyuncunun kimliği player.id ile tanınır; giriş yöntemleri bu kimliğe bağlanan yollardır. Bu sayfa misafirin kalıcı hesaba geçişini, Google kimliğinin bağlanmasını ve çözülmesini, bağlı kimliklerin listelenmesini ve çakışma durumlarında ne olduğunu anlatır.

Not

Open Play hesapları birleştirmez. Bir kimlik başka bir oyuncuya bağlıysa istek reddedilir; iki oyuncunun ilerlemesi hiçbir yolla tek hesaba katılmaz. Tek istisna, Google’ın e-postayla otomatik bağlanma kuralıdır; o da yeni bir oyuncu açılmasını önleyip kimliği mevcut hesaba ekler.

Misafirden kalıcı hesaba (terfi)

Misafir oyuncu iki yoldan kalıcı hesaba geçer; ikisinde de player.id değişmez, misafir sırrı emekliye ayrılır ve yeni oturum açılır:

YolÇağrıAyrıntı
E-posta ve parolaPOST /v1/auth/guest/upgrade/email · TS upgradeGuest · Unity UpgradeGuestAsyncE-posta ve parola
GooglePOST /v1/auth/link/google + callback · TS linkOAuth + completeOAuthGoogle ile giriş

Terfi yalnız misafir oyuncular içindir: misafir olmayan bir oyuncu için e-posta terfisi 422 verir. Google ile açılmış ya da zaten kayıtlı bir hesaba sonradan e-posta/parola eklenemez.

Bağlı kimlikleri listeleme

GET /v1/auth/identities
x-crx-env: <ortam-kimliği>
{
  "player_id": "0192…",
  "identities": [ { "provider": "google", "linked_at": "2026-11-03T10:00:00+00:00" } ]
}
  • Oturum gerekir (yoksa 401). Yanıt Cache-Control: private, no-store taşır.
  • Yalnız sağlayıcı kimlikleri listelenir (google, …); e-posta/parola girişi listelenmez. Sağlayıcının hesap kimliği (sub), e-postası ve belirteçleri yanıtta yoktur.
  • TypeScript: session.listIdentities() { playerId, identities: [{ provider, linkedAt }] } döndürür (camelCase); yanıt sıkı doğrulanır, beklenmeyen biçim CrxError(Contract) olur ve oturumu düşürmez.

“Hesabım” ekranınızda “Google bağla” ya da “Google bağlı” göstermek için bu uç yeterlidir.

Kimlik bağlama

POST /v1/auth/link/{provider} oturumdaki oyuncu için OAuth akışı başlatır ve {url, state} döner; oyuncu url’ye yönlendirilir, dönüşte callback tamamlanır. Kurallar:

  • Sağlayıcı başına tek kimlik: oyuncuda bu sağlayıcı zaten bağlıysa başlatma 422 (unprocessable: provider_already_linked) verir.
  • Oturum bağı: callback, bağlamayı başlatan oyuncunun oturumuyla gelmelidir; oturumsuz ya da başka oyuncunun oturumuyla gelen callback 403 alır.
  • Kimlik başka oyuncuda: Google kimliği başka bir oyuncuya bağlıysa callback 409 (already exists: account_already_linked) verir; misafir ilerlemesi dokunulmadan kalır.
  • Doğrulanmış e-posta gerekmez; e-postasız hesaplar da bağlayabilir.
  • Başlatma ve callback oranları: IP başına 30 / 60 sn ve 60 / 60 sn.

Kimlik çözme

POST /v1/auth/unlink/google
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

204. Oturum gerekir. Hatalar:

DurumAnlamı
400Son kalan sağlayıcı kimliği çözülemez (hesap kilitlenmesin diye)
404Bu sağlayıcıya bağlı kimlik yok

Not

Bugün bir oyuncunun genellikle tek sağlayıcı kimliği olduğundan, çözme çağrısı pratikte çoğunlukla 400 alır. Uç, ikinci bir sağlayıcı eklendiğinde kullanılmak üzere yerindedir. Unlink için SDK yardımcısı yoktur; ham REST kullanın.

Çakışma tablosu

DurumSonuç
Misafir, adresi başka hesapta kayıtlı bir e-postayla terfi etmeye çalışır409; misafir oturumu bozulmaz
Misafir, Google’a bağlanır ve Google kimliği başka oyuncuda409 account_already_linked
Misafir, Google’a bağlanır ve Google’ın doğruladığı e-posta başka oyuncunun e-postasıyla eşleşir409 account_already_linked (birleştirme yok)
Kayıtlı oyuncu aynı sağlayıcıyı ikinci kez bağlamaya çalışır422 provider_already_linked
Yeni Google kimliği, mevcut e-posta/parola hesabının e-postasıyla eşleşir (Google kefil)Kimlik o hesaba otomatik bağlanır, oyuncu o hesaba girer
Banlı hesaba bağlanmaReddedilir (403)

Hesabı silme

Oyuncu, hesabını oyunun içinden kendisi silebilir. Apple App Store ve Google Play, hesap oluşturmaya izin veren uygulamaların uygulama içinden hesap silmeyi de sunmasını bekler; bu uç o beklentiyi karşılar. Silme geri alınamaz: ne oyuncu ne de operatör silinmiş hesabı geri getirebilir.

İstek

Önce bir CSRF belirteci alın (Oturumlar ve CSRF); sonra:

POST /v1/auth/account/delete
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{ "confirm": "delete_my_account" }

Oyuncu kimliği gövdeden gelmez, oturumdan gelir. confirm açık bir onaydır; eksik ya da başka bir değer 422 ile reddedilir. Başarılı istek 204 döner ve oturum ile yenileme çerezlerini siler.

Not

Bu uç yalnız oyuncu oturumuyla çalışır. Sunucu anahtarı (Bearer crx_…) ile bir oyuncuyu bu uçtan silemezsiniz; 401 alırsınız. Bir oyuncuyu operatör olarak silmek için panelin oyuncu sayfasını kullanın.

Yakın giriş kuralı

  • Misafir hesap doğrudan silinir.
  • Bağlı kimliği olan hesap (e-posta/parola, Google ya da başka sağlayıcı) için oturumun giriş anı en çok 10 dakika önce olmalıdır. Aksi hâlde sunucu hiçbir şeyi silmeden 403 ve şu gövdeyi döner:
{ "code": "recent_sign_in_required" }

Uygulama bu durumda oyuncuyu yeniden giriş yaptırır ve isteği tekrarlar. Süre, oturum yenilemesinden (refresh) değil girişin kendisinden sayılır; oyuncu gün boyu açık bir oturumda olsa bile yeniden giriş gerekir. Giriş anı kanıtlanamıyorsa sunucu güvenli tarafta kalır ve aynı 403’ü verir.

Neler silinir, neler kalır?

Silme tek bir veritabanı işleminde yapılır; ya hepsi olur ya hiçbiri:

VeriSonuç
OturumlarHepsi iptal edilir; açık gerçek zamanlı bağlantılar kapanır.
Bağlı kimlikler (e-posta/parola, Google…)Ayrılır; parola ve sağlayıcı belirteçleri silinir. Aynı e-posta ya da aynı Google hesabı sonradan yeni bir oyuncu olarak başlayabilir.
Oyuncu verisi (player data blob’ları)Silinir.
Oyuncu satırıSilinmez; anonimleştirilir (Deleted player, e-posta ve görsel temizlenir) ve silinmiş olarak işaretlenir.
Denetim olaylarıDeğişmez. Silme, yeni bir player_self_deleted olayı olarak eklenir; olayda e-posta ya da sağlayıcı kimliği yoktur.
Cüzdan defteri ve eklenti kayıtlarıDeğişmez (append-only); oyuncu satırı anonim olduğu için kişi bu kayıtlardan çözülemez.

Uyarı

Eklentilerin kendi tablolarında tuttuğu serbest kişisel veri bu uçla silinmez. Eklentiniz kişisel veri saklıyorsa kendi silme akışınızı ayrıca bağlamanız gerekir.

Hata ve sınır tablosu

DurumAnlamı
204Hesap silindi.
401Oturum yok/geçersiz, oyuncu aktif değil (banlı ya da zaten silinmiş) ya da sunucu anahtarı sunuldu. Banlı oyuncu silme yoluyla yasaktan kaçamaz.
403 gövdesizCSRF reddi.
403 recent_sign_in_requiredYeniden giriş gerekli; hiçbir şey silinmedi.
422confirm eksik ya da delete_my_account değil.
429Oyuncu başına saatte 5 deneme aşıldı.

SDK

TypeScript SDK’sında:

try {
  await client.session.deleteAccount({ confirm: "delete_my_account" });
  // state artık signedOut; oyuncuyu giriş ekranına götürün.
} catch (error) {
  if (error instanceof CrxError && error.reason === "recent_sign_in_required") {
    // oyuncuyu yeniden giriş yaptırın, sonra çağrıyı tekrarlayın
  } else {
    throw error;
  }
}

Başarıda SDK, parola değişikliğindeki gibi oturumu sonlandırır ve { status: "signedOut" } döner. recent_sign_in_required durumunda oturum ve hesap değişmez. İstek gönderildikten sonra yanıt kaybolursa sonuç belirsizdir (UnknownOutcome): SDK kör tekrar yapmaz, gerçek durumu uygulamanın restore() çağrısı belirler. Unity SDK’da bu yöntem henüz yoktur.

Mağaza gizlilik notu

Uygulama içinden erişilebilir bir “Hesabımı sil” düğmesi koyun, tıklamadan önce açık bir onay isteyin ve düğmeyi deleteAccount çağrısına bağlayın. Hesap silinince oyuncuya hangi verinin kalacağını (cüzdan defteri anonimleşmiş olarak kalır) gizlilik metninizde belirtin. Silinen hesabın e-postası ve sağlayıcı kimliği serbest kalır; aynı kişi yeniden kayıt olursa sıfırdan başlar.

İlgili sayfalar

Oyuncuyu oyun sunucunuzda doğrulama

Oyun sunucunuz, kendisine gelen bir istek ya da bağlantının hangi oyuncuya ait olduğunu bilmek ister. Bu sayfa iki yolu anlatır: Open Play’in imzaladığı erişim JWT’sini çevrimdışı doğrulamak (JWKS ile) ve oturumu Open Play’e sorarak doğrulamak. Hangi yolun sizin için uygun olduğunu sayfanın sonundaki karşılaştırma tablosu söyler.

Uyarı

Bugünkü SDK sınırı: TypeScript ve Unity SDK’ları erişim JWT’sini uygulamaya açmaz; belirteç yalnızca ham REST giriş yanıtının gövdesinde (tokens.access_token) bulunur. SDK kullanan bir istemci JWT’yi sunucunuza veremez. SDK’lı istemciler için aşağıdaki Yol B (oturum doğrulama) kullanılabilir. Bu eksiklik kayıtlıdır.

Yol A: erişim JWT’sini çevrimdışı doğrulama

Belirteç

Giriş, kayıt, yenileme ve OAuth callback yanıtları tokens.access_token döndürür: EdDSA (Ed25519) ile imzalı, 15 dakikalık compact JWT. Belirteç yalnız gövdededir; çerezde taşınmaz. Süresi dolunca POST /v1/auth/refresh yenisini verir.

Başlık: {"alg":"EdDSA","typ":"JWT","kid":"<anahtar-kimliği>"}. Yük:

AlanAnlamı
subOyuncu kimliği (UUID)
envOrtam kimliği (UUID)
sidOturum kimliği (UUID)
orgEtkin organizasyon (yoksa alan yok)
iat, expÜretim ve bitiş anı: Unix milisaniye (saniye değil)

Uyarı

iat ve exp milisaniyedir ve belirteçte iss/aud yoktur. Standart JWT kütüphaneleri exp’i saniye sayar; milisaniye değerini uzak gelecekte bir tarih sanıp süresi dolmuş belirteci kabul eder. Kütüphane kullanırsanız imzayı ona doğrulatın ama exp denetimini kendiniz yapın (exp > Date.now()).

Anahtarlar (JWKS)

curl -sS "$OPEN_PLAY_URL/v1/auth/jwks"
{"keys":[{"kty":"OKP","crv":"Ed25519","kid":"<anahtar-kimliği>","use":"sig","alg":"EdDSA","x":"<base64url genel anahtar>"}]}

Kimlik gerekmez. Sunucu imzalayıcıyı yayınlamadıysa 404 döner. Yanıtı önbelleğe alın ve yalnızca belirteçteki kid listede yoksa yenileyin; her istekte çekmeyin.

Doğrulama adımları

  1. Belirteci üç parçaya ayırın; başlıktaki alg değeri EdDSA olmalı, aksi halde reddedin.
  2. kid ile JWKS’ten anahtarı bulun; yoksa JWKS’i bir kez yenileyin.
  3. İmzayı header.payload metni üzerinde Ed25519 ile doğrulayın.
  4. exp > şimdi (ms) olduğunu denetleyin.
  5. env değerini kendi ortam kimliğinizle karşılaştırın.

Uyarı

Aynı Open Play sunucusundaki bütün oyunların belirteçleri aynı JWKS ile doğrulanır. env karşılaştırmasını atlarsanız, başka bir oyunun oyuncusu sizin sunucunuzda geçerli görünür. Bu adım zorunludur.

Node.js (yalnız yerleşik node:crypto, bağımlılık yok):

import { createPublicKey, verify, type JsonWebKey } from "node:crypto";

/** Open Play erişim JWT'sinin (EdDSA) yük alanları. `iat` ve `exp` saniye değil, Unix MİLİSANİYEDİR. */
export interface AccessClaims {
  readonly sub: string; // oyuncu kimliği
  readonly env: string; // ortam kimliği
  readonly sid: string; // oturum kimliği
  readonly org?: string;
  readonly iat: number;
  readonly exp: number;
}

interface Jwks {
  readonly keys: ReadonlyArray<JsonWebKey & { readonly kid?: string }>;
}

function decode(part: string): Buffer {
  return Buffer.from(part, "base64url");
}

/**
 * Oyuncunun erişim JWT'sini çevrimdışı doğrular: imza, süre ve ortam eşleşmesi.
 * JWKS'i her istekte çekmeyin; `jwks` parametresini önbellekten verin (anahtar döndürülürse bilinmeyen `kid` gelince yenileyin).
 */
export function verifyAccessToken(token: string, jwks: Jwks, expectedEnvironmentId: string, nowMs: number = Date.now()): AccessClaims {
  const parts = token.split(".");
  const [header, payload, signature] = parts;
  if (parts.length !== 3 || header === undefined || payload === undefined || signature === undefined) {
    throw new Error("Biçimi bozuk JWT.");
  }
  const head = JSON.parse(decode(header).toString("utf8")) as { alg?: string; kid?: string };
  if (head.alg !== "EdDSA") throw new Error("Beklenmeyen algoritma.");
  const jwk = jwks.keys.find((key) => key.kid === head.kid);
  if (jwk === undefined) throw new Error("Bilinmeyen anahtar kimliği; JWKS'i yenileyin.");
  const publicKey = createPublicKey({ key: jwk, format: "jwk" });
  const signed = Buffer.from(`${header}.${payload}`, "utf8");
  if (!verify(null, signed, publicKey, decode(signature))) throw new Error("İmza doğrulanamadı.");

  const claims = JSON.parse(decode(payload).toString("utf8")) as AccessClaims;
  if (claims.exp <= nowMs) throw new Error("Belirtecin süresi dolmuş.");
  // JWKS tüm oyunlar için ortaktır: başka bir ortamın oyuncusunu kabul etmemek için `env` mutlaka karşılaştırılır.
  if (claims.env !== expectedEnvironmentId) throw new Error("Belirteç başka bir ortama ait.");
  return claims;
}

/** JWKS'i çeker (kimlik gerekmez). */
export async function fetchJwks(baseUrl: string): Promise<Jwks> {
  const response = await fetch(`${baseUrl}/v1/auth/jwks`);
  if (!response.ok) throw new Error(`JWKS okunamadı: ${response.status}`);
  return (await response.json()) as Jwks;
}

Sınırlar

  • İptal görmez. Çevrimdışı doğrulama oturumun kapatıldığını, iptal edildiğini ya da oyuncunun banlandığını bilemez; belirteç exp’ine kadar (en çok 15 dakika) geçerli görünür. İptal duyarlı olması gereken işlemlerde Yol B’yi kullanın ya da bu 15 dakikalık pencereyi kabul edin.
  • Anahtar döndürme. Anahtar değişimi sunucuyu yeniden başlatır; yeni kid JWKS’te görünür. Önbelleğiniz bilinmeyen kid görünce yenilenmelidir.

Yol B: oturumu Open Play’e sorma

Oyuncunun oturum belirtecini (çerez değeri ya da Authorization: Bearer belirteci) ve ortam kimliğini Open Play’e ileterek oyuncuyu çözersiniz. Tarayıcı oyununda oyunun origin’ine giden isteklerde oturum çerezi (__Secure-crx.session) zaten gelir; sunucunuz bu değeri okuyup Open Play’e Bearer olarak iletebilir.

curl -sS "$OPEN_PLAY_URL/v1/auth/session" \
  -H "Authorization: Bearer $PLAYER_SESSION_TOKEN" \
  -H "x-crx-env: $OPEN_PLAY_ENV"

200 ve {"environment_id","player_id","session_id","is_guest","displayName","email",…}. İptal edilmiş, süresi dolmuş ya da banlı oturum 401 verir. GET olduğu için CSRF belirteci gerekmez.

Uyarı

Oturum belirteci bir yetkidir. Açık metin günlüğe yazmayın, başka servislere yaymayın ve yalnızca TLS üzerinden iletin.

Hangi yolu seçmeliyim?

Yol A: JWT çevrimdışıYol B: oturum sorgusu
Open Play’e istekYok (JWKS önbellekte)Her doğrulamada bir istek
İptal/ban görürHayır (en çok 15 dk gecikme)Evet
Belirteç kaynağıYalnız ham REST giriş yanıtı; SDK vermezÇerez ya da Bearer oturum belirteci; SDK’lı istemciyle de çalışır (tarayıcıda çerez)
Ortam doğrulamasıenv talebini siz karşılaştırırsınızOrtam, istekteki x-crx-env ile sınanır
Ne zaman?Çok yüksek istek hacmi, iptal gecikmesi kabul edilebilirVarsayılan öneri; oyuncu kimliği kritik işlemler

Sunucu-sunucu çağrılarında oyuncu yerine sunucu anahtarı kullanılır (Kavramlar).

TypeScript SDK

TypeScript SDK’sı Open Play’e tarayıcıdan, Node.js’ten ve oyun motorlarının JavaScript katmanından bağlanmanın tipli yoludur: oyuncu girişi, eklenti komutları, gerçek zamanlı bağlantı ve sunucu anahtarıyla yönetim çağrıları. Bu sayfa kurulumu, giriş noktalarını, temel istemcileri ve hangi modüllerin tipli istemcisi olduğunu anlatır.

Not

Bu belge @ferdiunal/open-play-ts 0.1.15 sürümüyle doğrulanmıştır. Paket GitHub Packages’ta yayımlıdır; depo bugün özeldir, bu yüzden kurulum için yetkili bir GitHub belirteci gerekir.

API başvurusu: tüm tür ve işlevlerin üretilmiş başvurusu https://open-play.b4klabs.dev/docs/sdk/typescript/ adresindedir (@open-play/sdk ve @open-play/panel-kit).

Kurulum

Gereksinimler: Node.js 22 ya da üstü; paket yalnız ESM’dir (require ile yüklenmez). Tarayıcıda Fetch, AbortController ve BigInt destekli modern bir tarayıcı gerekir.

  1. Kayıt defterini tanıtın. Projenizin kökündeki .npmrc dosyasına kapsamı (scope) ve belirteç değişkenini yazın. Belirteç read:packages yetkili bir GitHub klasik belirtecidir (ince ayarlı belirteç denenmedi); dosyaya değer yazmayın, ortam değişkeni kullanın:

    @ferdiunal:registry=https://npm.pkg.github.com
    //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
    
  2. Paketi kurun:

    export GITHUB_TOKEN="<read:packages yetkili belirteç>"
    npm install @ferdiunal/[email protected]
    

    pnpm aynı .npmrc ile çalışır. Belirteci depoya, imaja ya da CI günlüğüne koymayın.

  3. İçe aktarın:

    import { CrxClient, createPlayerClient } from "@ferdiunal/open-play-ts";
    

Uyarı

Paket adı @ferdiunal/open-play-ts’tir. SDK deposundaki bazı dosyalarda ve örneklerde geçen @open-play/sdk adı yalnızca kaynak deposundaki iç addır; yayımlanan pakette kullanmayın.

Giriş noktaları

İçe aktarmaİçerikOrtam
@ferdiunal/open-play-tsCrxClient, createPlayerClient, createGameClient, hata ve sayı yardımcıları, katalog/mağaza/sıralama tipleriTarayıcı ve Node
.../playerOyuncu istemcisi ve eklenti bağlama oluşturma tipleriTarayıcı ve Node
.../gameGameClientTarayıcı ve Node
.../realtimeGerçek zamanlı istemci tipleriTarayıcı ve Node
.../browserIndexedDB günlüğü ve sekme koordinasyonu (createBrowserAdapter)Tarayıcı
.../codegenBağlama ve tip üreticisiYalnız Node

CLI: crx (npx crx --help). Kod üretimi bölümüne bakın.

İstemciler

CrxClient: sunucu anahtarıyla REST

CrxClient, API anahtarıyla çalışan tipli REST istemcisidir. Sunucu anahtarını yalnız güvenilen arka uçta kullanın; tarayıcıya ya da oyun paketine koymayın.

const client = new CrxClient({ baseUrl: "https://oyun.example.com/open-play", apiKey: serverKey });

ClientOptions: baseUrl, apiKey, isteğe bağlı fetch, timeoutMs (1–120 000 ms, varsayılan 15 sn), maxRetries (0–5, varsayılan 2), maxResponseBytes (varsayılan 2 MiB, en çok 16 MiB). Her çağrı { path, query, body, headers } girdisi ve isteğe bağlı { signal, timeoutMs, maxRetries } alır.

import { CrxClient, CrxError, ErrorKind } from "@ferdiunal/open-play-ts";

/**
 * Sunucu anahtarıyla bir karaktere XP verir. Aynı `requestKey` ile yapılan tekrar aynı sonucu döndürür;
 * `If-Match`, okuduğunuz sürümle yazdığınız sürümün çakışmasını yakalar.
 */
export async function grantXp(
  baseUrl: string,
  serverKey: string,
  characterId: string,
  amount: bigint,
  requestKey: string,
): Promise<bigint> {
  const client = new CrxClient({ baseUrl, apiKey: serverKey });
  const current = await client.getCharacter({ path: { id: characterId } });
  try {
    const result = await client.executeCommand({
      path: { id: characterId },
      body: { type: "add_xp", amount },
      headers: { "Idempotency-Key": requestKey, "If-Match": current.version.toString() },
    });
    return result.version;
  } catch (error) {
    if (error instanceof CrxError && error.kind === ErrorKind.Http && (error.status === 409 || error.status === 422)) {
      // 409: sürüm çakışması; yeniden okuyup karar verin. 422: aynı anahtar başka bir gövdeyle kullanılmış.
      // Yeni bir anahtarla kör yeniden deneme yapmayın.
      throw error;
    }
    throw error;
  }
}

/** Sayfalamayı SDK yürütür: imleç (cursor) opaktır, `break` yeni sayfa istemez. */
export async function countCharacters(baseUrl: string, serverKey: string, definitionKey: string): Promise<number> {
  const client = new CrxClient({ baseUrl, apiKey: serverKey });
  let total = 0;
  for await (const _character of client.iterateListCharacters({ query: { definition: definitionKey, limit: 100 } })) {
    total += 1;
  }
  return total;
}

Çağrı kuralları

  • baseUrl HTTPS olmak zorundadır; yalnızca loopback (localhost, 127.0.0.1, [::1]) düz HTTP kabul eder. Adres kullanıcı bilgisi, sorgu ya da parça içeremez. Yönlendirmeler (redirect) izlenmez.
  • Anahtar yalnız Authorization başlığındadır; hata nesnelerinde ve hata ayıklama çıktısında görünmez.
  • Yeniden deneme yalnızca okuma GET’lerinde ve gerçekten verilmiş aynı Idempotency-Key taşıyan komutlarda yapılır. Geçici 408, 429, 500, 502, 503, 504 ve güvenli işlemde ağ hatası, sınırlı üstel geri çekilmeyle denenir; Retry-After toplam süre sınırı içindedir. 409 ve 422 yeniden denenmez. Oluşturma ve silme çağrıları otomatik tekrarlanmaz.
  • Komutlarda If-Match (kaynağın okuduğunuz sürümü) ve Idempotency-Key başlıklarını siz verirsiniz (Idempotency ve eşzamanlılık).
  • Sayfalama imleçleri opaktır; iterate… yöntemleri (iterateListCharacters, iterateListCharacterEvents, iterateGetCharacterTimeline, iterateListRules, iterateListWebhooks) sayfaları sizin yerinize yürütür.

PlayerClient: oyuncu oturumu

createPlayerClient({ baseUrl, environmentId }) oyuncu tarafı istemcisidir (yalnız çerez kipi; sunucu anahtarı bu istemciye girmez). İçerdikleri:

ÜyeNe yapar?
sessionGiriş, oturum durumu, yenileme, çıkış: Kimlik doğrulama
plugin(binding) · publicPlugin(binding)Eklenti komut/sorgu/modülleri: Eklenti bağlamaları
realtimeGerçek zamanlı soket (aşağıda)
catalogPanelden yönetilen eşya/veri/model/içerik grubu okuması (koşullu GET, watch… yoklayıcıları)
leaderboard(key, { limit })Çekirdek skor tablosu okuması (limit 1–50)
store.catalog() · store.buy()Mağaza vitrini ve satın alma
data.list() · data.get() · data.create() · data.update() · data.delete()Oyuncu verisi (bulut kaydı): sürümlü yazım, çatışmada PlayerDataConflict; bkz. Oyuncu verisi
notificationsCihaz kaydı, gelen kutusu, web push
dispose()Yerel kaynakları bırakır (sunucu oturumunu kapatmaz)

İlk kullanım için Hızlı başlangıç. Oturum durumları için Oturumlar ve CSRF.

createPlayerClient ağ isteğini kurucuda başlatmaz; baseUrl oyun sayfanızla aynı origin’dir (ters vekil) ya da oyunun alan adı ortama bağlıysa Open Play adresidir (CORS; aynı site gerekir) (Mimari ve barındırma).

GameClient: oyuncu + eklenti + realtime tek yaşam döngüsü

createGameClient, bir oyuncu istemcisini, eklenti bağlamalarını ve tek gerçek zamanlı soketi aynı yaşam döngüsünde tutar. dispose() çocuk bağlamaları ve kendi yarattığı oyuncu istemcisini kapatır; sunucu çıkışı için ayrıca game.session.logout() çağırın. Dışarıdan verdiğiniz PlayerClient GameClient tarafından sahiplenilmez.

import { createGameClient, type GameClient } from "@ferdiunal/open-play-ts";
import { createBrowserAdapter } from "@ferdiunal/open-play-ts/browser";

/**
 * Bir oyunun oyuncu istemcisi, eklenti bağlamaları ve tek realtime soketi aynı yaşam döngüsünde tutulur.
 * IndexedDB günlüğü (journal) yarım kalan eklenti komutlarının sayfa yenilendikten sonra kurtarılmasını sağlar.
 */
export function startGame(environmentId: string): { game: GameClient; stop: () => Promise<void> } {
  const browser = createBrowserAdapter({ journal: "indexeddb", allowMemoryFallback: false });
  const game = createGameClient({
    playerOptions: { baseUrl: window.location.origin, environmentId, browser },
  });
  return {
    game,
    stop: async () => {
      await game.dispose();
      await browser.dispose();
    },
  };
}

journal: "indexeddb" yarım kalan eklenti komutlarının sayfa yenilendikten sonra kurtarılmasını sağlar. SSR/Node’da journal: "memory" seçin ve istemciyi istekler arasında paylaşmayın.

Gerçek zamanlı soket

client.realtime.connect() tek kullanımlık bilet alır, soketi açar ve kimlik doğrular; kopmada bileti yenileyip imleç/epoch ile kaldığı yerden sürdürür. Sunucu etkinlik göstermeyen soketi 4100 koduyla kapatır; onIdleClose verirseniz SDK otomatik yeniden bağlanmaz ve “Devam et” kararı sizindir. Protokol ayrıntıları Gerçek zamanlı bölümündedir.

import { createPlayerClient, isIdleClose, type PlayerClient } from "@ferdiunal/open-play-ts";

/**
 * Realtime soketini açar. Sunucu, etkinlik göstermeyen soketi 4100 koduyla kapatır; `onIdleClose` verirseniz
 * SDK otomatik yeniden bağlanmaz ve "Devam et" kararını size bırakır.
 */
export function createRealtimeClient(environmentId: string, showContinue: (resume: () => Promise<unknown>) => void): PlayerClient {
  const client: PlayerClient = createPlayerClient({
    baseUrl: window.location.origin,
    environmentId,
    realtime: {
      onIdleClose: () => showContinue(() => client.realtime.connect()),
    },
  });
  return client;
}

/** Durumu yoklayan uygulama aynı bilgiyi `lastClose` üzerinden okuyabilir. */
export function wasIdleClosed(client: PlayerClient): boolean {
  return isIdleClose(client.realtime.lastClose);
}

/** Oturum açıldıktan sonra soketi kurar; bilet yenileme ve yeniden bağlanma SDK'dadır. */
export async function connect(client: PlayerClient): Promise<void> {
  const connection = await client.realtime.connect();
  console.log("realtime durumu:", connection.state);
}

Kapsam: hangi modülün tipli istemcisi var?

SDK, REST yüzeyinin tamamını sarmaz. Aşağıdaki tablo bugünkü durumu gösterir; “ham REST” olan modüller için uçları uç nokta başvurusunda bulur, HTTP çağrısını kendiniz yaparsınız (anahtar ve başlık kuralları aynıdır).

Modül / alanTipli istemciNerede
Karakter tanımları, özellikler (trait), seviye eğrileri, karakterler, komutlar, geçmiş, istatistikVarCrxClient (sunucu anahtarı)
KurallarVarCrxClient (createRule, listRules, dryRunRule, …)
Webhook uç kaydı (oluştur/listele/oku/sil)VarCrxClient; imza doğrulaması sizin kodunuzdur (Webhook’lar)
KlanlarVarCrxClient (20 işlem)
Katalog (eşya/veri/model) ve içerik gruplarıVarPlayerClient.catalog (oyuncu), CrxClient.catalog (sunucu)
Ödül tablosu kaydı ve vermeVarCrxClient.createRewardTable, getRewardTable, grantRewardTable
Mağaza vitrini ve satın almaVarPlayerClient.store
Çekirdek skor tablosu okumaVarPlayerClient.leaderboard
Bildirimler: cihaz kaydı, gelen kutusu, web pushVarPlayerClient.notifications
Eklentiler ve gerçek zamanlıVar (şemadan üretilen bağlamayla)PlayerClient.plugin, GameClient
Eşyalar ve envanterYokHam REST
Para birimleri, cüzdanlar, transferler, defterYokHam REST
Takas (trading), tüccarlarYokHam REST
Mağaza yönetimi, IAP doğrulamaYokHam REST
Görev, VIP, günlük giriş ve NPC ödülleriYokHam REST
PartilerYokHam REST
Sunucu tarafı skor tabloları, turnuvalarYokHam REST
SohbetYokHam REST/WebSocket
Sahneler, varlıklarYokHam REST
Magic link, hesap bağını çözme, yerel platform girişleriYokHam REST (Kimlik doğrulama)

CrxClient’ta tipli işlem sayısı 45’tir (25 çekirdek + 20 klan) ve katalog ile ödül tablosu işlemleri bunlara eklenir. Tipli istemcisi olmayan bir modülü kullanmak sorun değildir; yalnızca tipleri elle yazarsınız.

Hatalar

Tüm hatalar CrxError olarak gelir: kind, status, attempts ve güvenli metadata (code, operation, deliveryState, reference, reason). HTTP gövdesi, istek URL’si ve anahtar hata nesnesine girmez.

ErrorKindAnlamı
HttpSunucu hata durumu döndürdü (status dolu)
NetworkBağlantı koptu; mutasyon uygulanmış olabilir
TimeoutToplam süre sınırı doldu
Cancelledsignal ile iptal
ContractGirdi ya da yanıt sözleşmeye uymuyor
ConfigurationGüvensiz ya da bozuk ayar
AuthOyuncu oturumu yeniden doğrulama istiyor
UnknownOutcomeİstek gönderildi ama sonuç bilinmiyor; kör tekrar yapmayın, sonucu sorgulayın
StorageGünlük, tarayıcı koordinasyonu ya da OAuth bağ deposu kullanılamıyor
Realtime · Backpressure · UpgradeRequired · ScopeChanged · DisposedGerçek zamanlı yaşam döngüsü, kuyruk dolu, eklenti sürüm uyuşmazlığı, oturum değişti, istemci kapatıldı

Banlı hesap error.code === ACCOUNT_BANNED_CODE ile ayırt edilir (Kimlik doğrulamaya genel bakış).

Sayılar ve JSON

  • i64/u64 alanlar bigint’tir; 32 bit alanlar sınır denetimli number’dır. 9007199254740993n kayıpsız taşınır.
  • SDK yükünü JSON.parse/JSON.stringify ile yeniden dolaştırmayın; parseJson, stringifyJson, encodeModel, decodeModel kullanın. Ondalık sayılar LosslessNumber olur.
  • Varsayılan sınırlar: JSON 2 MiB, derinlik 96, düğüm sayısı 200 000.
  • Eklenti protokolünde i64 değerler ondalık dizgedir (plugin-v1-decimal-int64). Bkz. Sayılar ve kodlama.

Kod üretimi (crx)

Eklenti şemasından tipli bağlama ve karakter tanımından tip üretmek için CLI kullanılır (yalnız Node; ağ erişimi yoktur, girdi dosyadır, çıktı yalnız yeni dosyaya yazılır, --check bayt karşılaştırır):

# Eklenti bağlaması: şema özeti (sha256) sabitlenir; değişirse üretim reddedilir
npx crx plugins generate --input plugin.schema.json --schema-sha256 "$SCHEMA_SHA256" --output binding.ts
npx crx plugins generate --input plugin.schema.json --schema-sha256 "$SCHEMA_SHA256" --output binding.ts --check

# Karakter tanımı tipleri
npx crx types generate --input definition.json --version 1 --schema-sha256 "$SCHEMA_SHA256" --output hero.ts

Eklenti şemasını nereden alacağınızı ve schemaSha256 sabitlemenin anlamını Eklenti bağlamaları sayfası anlatır.

Bilinen sınırlar

  • Yalnız çerez kipi vardır; oyuncu istemcisinde Bearer ya da sunucu anahtarı kipi yoktur.
  • Erişim JWT’si uygulamaya açılmaz (Sunucuda doğrulama).
  • Magic link, unlink ve yerel platform girişleri için yardımcı yoktur.
  • Görünen ad değiştirme (POST /v1/auth/session/display-name) için yöntem yoktur.
  • Sunucu CORS’u yalnız ortam alan adı listesindeki origin’lere verir; oturum çerezi SameSite=Lax olduğundan oyun ile Open Play aynı sitede olmalıdır, değilse ters vekil kullanın.

Unity SDK

com.openplay.sdk, Unity’den Open Play’e bağlanan C# istemcisidir: misafir ve e-posta/parola oturumu, oturumun yeniden başlatmalar arasında geri yüklenmesi, eklenti komutları ve sorguları ve gerçek zamanlı soket. Bu sayfa kurulumu, kullanımı ve neyin doğrulandığını dürüstçe anlatır.

Uyarı

Paket yalnız Unity 6000.6.7f1 ile doğrulanmıştır; başka editör sürümleri ve çoğu platform doğrulanmadı (Doğrulanan platformlar). Bu belge paket sürümü 0.1.0 içindir.

API başvurusu: C# tiplerinin üretilmiş başvurusu https://open-play.b4klabs.dev/docs/sdk/unity/ adresindedir.

Kurulum

Paket, https://npm.pkg.github.com/@ferdiunal adresindeki GitHub Packages npm kayıt defterinden gelir. Paket adı kapsamsızdır (com.openplay.sdk); kapsam kayıt defteri adresindedir.

  1. Kimlik. GitHub herkese açık paketlerde bile belirteç ister. Kullanıcı klasörünüzde ~/.upmconfig.toml oluşturun. Belirteç read:packages yetkili bir klasik belirteçtir (ince ayarlı belirteç denenmedi) ve bir sırdır: depoya koymayın, günlüğe yazmayın.

    [npmAuth."https://npm.pkg.github.com/@ferdiunal"]
    token = "<read:packages yetkili belirteç>"
    alwaysAuth = true
    
  2. Manifest. Oyun projenizin Packages/manifest.json dosyasına kapsamlı kayıt defterini ve bağımlılığı ekleyin:

    {
      "dependencies": { "com.openplay.sdk": "0.1.0" },
      "scopedRegistries": [
        { "name": "Open Play", "url": "https://npm.pkg.github.com/@ferdiunal", "scopes": ["com.openplay"] }
      ]
    }
    
  3. Unity’yi açın. İki bilinen davranış: Package Manager’ın arama ekranında paket görünmez (GitHub’ın arama uç noktası 405 döner; paketi manifest.json’a elle yazarsınız) ve günlükte Package 'com.openplay.sdk' has been installed without a signature uyarısı çıkar (zararsızdır).

com.unity.nuget.newtonsoft-json 3.2.1 ve com.unity.modules.unitywebrequest bağımlılıkları kendiliğinden çözülür. Yedek yol olarak yerel yol ("com.openplay.sdk": "file:<yol>/sdks/unity") kullanılabilir; bu yalnız kaynak deposuna erişimi olanlar içindir.

Paket bir Basic Login örneği içerir (Package Manager › Open Play SDK › Samples).

Temel kullanım

OpenPlayClient.Create(OpenPlayUnity.Options(baseUrl, environmentId)) istemciyi kurar; Options ana iş parçacığı pompasını, PlayerPrefs deposunu ve platforma uygun taşımayı seçer. Hızlı başlangıçtaki bileşen kayıtlı oturumu geri yükler, yoksa misafir açar.

OpenPlayOptions ayarları:

AyarAnlamı
BaseUrlAPI kökü. Mutlak https (geliştirmede loopback http); sorgu, parça ve kullanıcı bilgisi içeremez
EnvironmentIdOrtam kimliği (x-crx-env); boş olamaz
AllowInsecureTransportYalnız geliştirme: loopback adreslerinde (localhost, 127.0.0.1, ::1) düz http/ws. Varsayılan false
NativeOriginYerel derlemede bilet ve WebSocket isteklerinde gönderilen sabit Origin. Boşsa BaseUrl’den türetilir. Ortamın alan adlarında ya da genel izinli listede olmalıdır
PageOriginYalnız WebGL: sayfanın adresi; BaseUrl ile aynı origin olmalıdır (OpenPlayUnity.Options doldurur)
SecretStoreMisafir sırrı ve yenileme belirtecinin kalıcı deposu (aşağıya bakın)
TimeoutMsToplam istek süresi: 1–120 000, varsayılan 15 000
MaxResponseBytesEn büyük yanıt: 1 bayt–16 MiB, varsayılan 2 MiB
UserAgentBoşsa hiç gönderilmez
RealtimeGerçek zamanlı ayarları (AutoReconnect, ReconnectBaseMs, MaxReconnectAttempts, HeartbeatMs, AuthTimeoutMs, MaxPendingInputs, OnIdleClose)

Yanlış ayar OpenPlayException ile ErrorKind.Configuration ve kodlu bir neden verir (örneğin base_url_scheme_invalid, insecure_transport_not_loopback, environment_id_required).

Oturum

client.Session:

YöntemNe yapar?
RestoreAsync()Kayıtlı oturumu geri yükler; yoksa misafir yaratmaz
SignInGuestAsync()Misafir girişi (Misafir)
SignUpEmailAsync, SignInEmailAsync, UpgradeGuestAsyncE-posta kayıt/giriş/terfi (E-posta)
ChangePasswordAsync, RequestPasswordResetAsync, ResetPasswordAsyncParola işlemleri
RenameAsync(displayName)Görünen ad değiştirme (2–24 karakter, saatte 10)
RefreshAsync() · LogoutAsync()Yenileme ve çıkış
StateChanged olayı · StateSessionState: Status, PlayerId, SessionId, IsGuest, DisplayName, Email

SessionStatus: SignedOut, Authenticating, Authenticated, Refreshing, ReauthRequired, SigningOut, LogoutUncertain, Banned (terminal; yalnız LogoutAsync ile çıkılır), Disposed. Parola ve belirteçler SecretText ile taşınır; ToString() ve günlüklere girmez.

E-posta ve parola örnekleri için E-posta ve parola sayfasına bakın.

Sır depolama

Misafir sırrı ve yenileme belirteci ISecretStore ile saklanır. Varsayılan PlayerPrefs deposu düz metindir ve güvenli depo değildir (iOS Keychain, Android Keystore yok). Üretimde platformun güvenli deposuna yazan kendi ISecretStore uygulamanızı verin:

using System.Threading;
using System.Threading.Tasks;
using OpenPlay.Support;

/// <summary>Platformunuzun güvenli deposunu (iOS Keychain, Android Keystore, masaüstü kimlik deposu) temsil eden kendi arayüzünüz.</summary>
public interface IPlatformKeystore
{
    string Read(string key);
    void Write(string key, string value);
    void Delete(string key);
}

/// <summary>
/// ISecretStore sözleşmesi: misafir bootstrap sırrı ve yenileme belirteci burada saklanır.
/// Varsayılan PlayerPrefs deposu düz metindir ve güvenli depo DEĞİLDİR; üretimde bunun gibi kendi deponuzu verin:
///   options.SecretStore = new KeystoreSecretStore(myKeystore);
/// </summary>
public sealed class KeystoreSecretStore : ISecretStore
{
    private readonly IPlatformKeystore _keystore;

    public KeystoreSecretStore(IPlatformKeystore keystore) { _keystore = keystore; }

    public Task<string> GetAsync(string key, CancellationToken cancellationToken) => Task.FromResult(_keystore.Read(key));

    public Task SetAsync(string key, string value, CancellationToken cancellationToken)
    {
        _keystore.Write(key, value);
        return Task.CompletedTask;
    }

    public Task ClearAsync(string key, CancellationToken cancellationToken)
    {
        _keystore.Delete(key);
        return Task.CompletedTask;
    }
}

Sözleşme: değer günlüğe, istisna iletisine ve ToString()’e girmez; olmayan anahtar için GetAsync null döner; her işlem başarısız olabilir ya da geç dönebilir (SDK 3 sn sonra vazgeçer).

Eklenti komutları ve sorguları

client.Plugin(binding, pin) eklenti istemcisi verir (PrepareCommand, QueryAsync, PublicQueryAsync, VerifyServerSchemaAsync). Komut hazırlanırken tek bir Idempotency-Key üretilir ve gövde dondurulur; belirsiz sonuçta (ErrorKind.UnknownOutcome) aynı anahtarla LookupAsync ile sorgulanır, kaybolduysa ResumeAsync ile tam aynı komut yeniden gönderilir. Kör yeni komut üretilmez.

Not

Yayımlanan pakette yalnız package-ride eklentisinin üretilmiş tipli bağlaması vardır (OpenPlay.PackageRide). Başka bir eklenti için bugün üretilmiş Unity bağlaması yoktur; ham PluginBinding ile çalışırsınız. Bağlama, sunucudaki şema özetine (schemaSha256) sabitlenir: uyuşmazlıkta UpgradeRequired alırsınız.

using System.Threading.Tasks;
using OpenPlay;
using OpenPlay.PackageRide;
using OpenPlay.Plugins;

/// <summary>
/// Eklenti komutu: yayımlı pakette yalnız package-ride için üretilmiş tipli bağlama vardır.
/// Komut hazırlanırken tek Idempotency-Key üretilir; belirsiz sonuçta AYNI anahtarla sorgulanır.
/// </summary>
public static class PluginCall
{
    public static async Task ClaimLoginBonus(OpenPlayClient client, long expectedVersion)
    {
        var rides = PackageRideClient.Create(client); // bağlama sürümü ve şema özeti sunucuyla eşleşmezse UpgradeRequired
        var command = rides.Commands.ClaimLoginBonus.Prepare(new LoginBonusInput { ExpectedVersion = expectedVersion });
        try
        {
            var receipt = await command.SendAsync();
            LoginBonusOutput output = rides.Commands.ClaimLoginBonus.ParseOutput(receipt); // tipli çıktı
            UnityEngine.Debug.Log("giriş bonusu işlendi, HTTP durumu: " + receipt.Status + ", çıktı: " + output.GetType().Name);
        }
        catch (OpenPlayException ex) when (ex.Kind == ErrorKind.UnknownOutcome)
        {
            // Yanıt kayboldu; komut uygulanmış olabilir. Yeni komut üretmeyin, aynı anahtarla sorgulayın.
            var lookup = await command.LookupAsync();
            if (lookup.Status == CommandLookupStatus.NotFound) await command.ResumeAsync(); // tam aynı anahtar ve gövde
        }
    }
}

Gerçek zamanlı

client.Realtime.ConnectAsync() bileti alır, soketi açar ve kimlik doğrular; kopmada artan gecikmeyle yeniden bağlanır. Yerel derlemede bilet Authorization: Bearer + x-crx-env + sabit Origin ile alınır; çerez kullanılmaz. Abonelik açmak için RealtimeConnection.JoinAsync(modül, JoinTarget.PlayerSelf() / Room / Scene / InterestSet) kullanılır. Protokol ayrıntıları: Gerçek zamanlı.

using System;
using System.Threading.Tasks;
using OpenPlay;
using OpenPlay.Realtime;
using OpenPlay.Unity;

/// <summary>
/// Realtime soketi: bilet alma, bağlanma ve yeniden bağlanma SDK'dadır.
/// Sunucu etkinlik göstermeyen soketi 4100 koduyla kapatır; OnIdleClose verirseniz otomatik yeniden bağlanma olmaz.
/// </summary>
public static class RealtimeConnect
{
    public static OpenPlayClient Create(Uri baseUrl, Guid environmentId, Action showContinue)
    {
        var options = OpenPlayUnity.Options(baseUrl, environmentId);
        options.Realtime = new RealtimeOptions { OnIdleClose = _ => showContinue() }; // "Devam et" düğmesini gösterin
        return OpenPlayClient.Create(options);
    }

    /// <summary>Oturum açıldıktan sonra çağrılır; "Devam et" onayında da aynı çağrı yeni bilet alıp sürdürür.</summary>
    public static Task<RealtimeConnection> Connect(OpenPlayClient client) => client.Realtime.ConnectAsync();
}

WebGL

WebGL oynatıcısında SDK tarayıcı kipine geçer (OpenPlayUnity.Options bunu kendisi seçer): HTTP UnityWebRequest ile, gerçek zamanlı tarayıcının WebSocket’iyle (küçük bir jslib köprüsü) gider ve oturum çerezlerini tarayıcı yönetir. SDK Cookie, Origin, Authorization ya da User-Agent başlığı koymaz; her mutasyon x-csrf-token taşır.

  • Yalnız aynı origin (SDK kısıtı). Unity WebGL SDK’sı bugün oyun sayfası ile Open Play API’sinin aynı kökten sunulmasını şart koşar; sunucu artık ortam alan adı listesindeki origin’lere CORS verir ama Unity SDK’sı bunu henüz kullanmaz (Mimari ve barındırma). Farklı origin OpenPlayException ile Configuration ve webgl_cross_origin_unsupported kodunu verir; sessizce başarısız olmaz.
  • Gizli sekme. Kalp atışı tarayıcı zamanlayıcısıyla sürer, sekme arka plana alınınca bağlantı açık kalır. Gizliyken gelen mesajlar ve HTTP tamamlanmaları Unity karesini bekler ve sekme görünür olunca işlenir. Application.runInBackground kapalıyken oyun döngüsü gizli sekmede durur; oyununuz gizliyken de ilerlemeliyse Application.runInBackground = true yapın.
  • Çok sekme. Aynı origin’deki sekmeler tek çerez kavanozunu paylaşır; ikinci sekmedeki giriş birincinin oturum çerezini değiştirir.

Doğrulanan platformlar

Düzey: R = Unity’de ölçüldü, B = yalnız belge ya da dotnet göstergesi, — = yok.

HedefDüzeyNot
Unity 6000.6.7f1 Editör (Linux)RMono ve IL2CPP ölçüldü
WindowsBdoğrulanmadı
macOSBdoğrulanmadı
AndroidBdoğrulanmadı (telefon ölçümü yapılmadı)
iOS—doğrulanmadı
WebGL (aynı origin)RBasicLogin WebGL oynatıcısı gerçek API’ye karşı: misafir ve e-posta girişi, eklenti sorgusu, gerçek zamanlı Ready, katılım ve girdi onayı; 35 sn gizli sekmede kalp atışı sürdü. Yalnız Chrome; Firefox ve Safari doğrulanmadı

package.json içindeki unity: 2022.3 alanı yalnız alt sınırdır; Unity 2022.3 doğrulanmadı.

Hatalar

Tüm hatalar OpenPlayException olarak gelir: Kind (ErrorKind), Status, Attempts, Code, Operation, DeliveryState, Reference, Reason. ErrorKind değerleri: Http, Network, Timeout, Cancelled, Contract, Configuration, Pagination, Codegen, Auth, UnknownOutcome, Storage, Realtime, Backpressure, UpgradeRequired, CapabilityUnavailable, Disposed, ScopeChanged. Anlamları TypeScript SDK’sındakilerle aynıdır (TypeScript SDK › Hatalar).

Kapsam dışı ve bilinen sınırlar

  • Unity SDK’sında yoktur: OAuth (Google), katalog, mağaza, skor tablosu, bildirimler ve sunucu anahtarıyla yönetim işlemleri. Bunlar için ham REST kullanın; sunucu anahtarını oyun istemcisine koymayın.
  • Yanıt sıkıştırması (permessage-deflate) yoktur.
  • Erişim JWT’si uygulamaya açılmaz (Sunucuda doğrulama).
  • Android, iOS ve macOS oynatıcı koşuları yapılmadı; WebGL yalnız aynı origin’de çalışır.
  • PlayerPrefs deposu güvenli değildir; üretimde kendi deponuzu verin.

Karakterler

Bu sayfa, oyuncunun oynadığı karakterin Open Play’de nasıl tanımlandığını, oluşturulduğunu ve değiştirildiğini anlatır. Karakter durumunu (özellikler, kaynaklar, XP, seviye, yetenekler, bayraklar) sunucuda tutmak ve hile yapılamayan komutlarla değiştirmek istiyorsanız okuyun.

Ne işe yarar

Karakter modülü (characters), oyuncuya bağlı karakter durumunu tek bir güvenilir yerde tutar. Durum yalnız komutlarla değişir ve her değişiklik değiştirilemez bir olay (event) olarak kaydedilir. Böylece bir karakterin herhangi bir andaki durumunu geri okuyabilir, hatalı bir değişikliği yeni bir olayla geri alabilirsiniz.

Modül kapalıysa bu sayfadaki uçların hepsi 404 ve {"code":"module_disabled","module":"characters"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Karakter şablonu, özellik (trait) ve seviye eğrisi tanımlamakKarakter şablonları
Karakterleri listelemek, birini açıp durumunu ve geçmişini görmekKarakterler, Karakter profili
Oyuncunun hesabını ve karakterlerini birlikte görmekOyuncular, Oyuncu profili
Olay tetikli otomasyon yazmakOyun kuralları

Veri modeli (düz dille)

  • Özellik (trait): Yeniden kullanılabilir alan paketi. Örneğin “savaşçı” özelliği güç ve can alanlarını getirir. Bir şablon birden çok özelliği birleştirir.
  • Şablon (definition): Anahtarla (warrior) tanımlanan karakter türü. Hangi özellikleri kullandığını, alanların varsayılanlarını, karakterin nasıl yaratılacağını (defaults, point_buy, roll) ve hangi seviye eğrisini izlediğini söyler. Şablon her yayında yeni bir sürüm olur; var olan karakterler doğdukları sürüme bağlı kalır.
  • Seviye eğrisi (level curve): Seviyenin kaç XP’ye karşılık geldiğini tarif eder. Seviye saklanmaz, kümülatif XP’den türetilir; eğriyi değiştirmek karakter verisinde dönüşüm gerektirmez. Üç tür vardır: table (eşik listesi), piecewise (seviye aralığı başına XP) ve formula (kuadratik formül, max_level ile).
  • Karakter: Bir şablondan doğan örnek. external_id alanına oyunun kendi oyuncu kimliğini yazabilirsiniz; aynı ortamda iki karaktere aynı external_id verilemez.
  • Durum (state): attributes (özellik değerleri), resources (can/mana gibi tüketilen kaynaklar), progression (level, xp, highest_level), abilities, flags (evet/hayır bayrakları) ve serbest custom alanı.
  • Sürüm (version): Karakterin her komutla artan sayısı. Eşzamanlı yazmayı engellemek için kullanılır (aşağıda).

Sayılar tam sayıdır (i64); ondalık sayı yoktur. Yüzdeler baz puan (basis point) olarak yazılır, 10000 = %100. Ayrıntı: Sayılar ve kodlama.

Oyunun yaptığı çağrılar

Karakter uçları API anahtarıyla çağrılır; oyuncu oturumu (çerez ya da oturum belirteci) bu uçlarda geçerli değildir. İstek başlığı:

Authorization: Bearer crx_live_<önek>_<gizli>

Anahtarın türü önemlidir (bkz. Kavramlar): sunucu anahtarı her komutu çalıştırır, istemci anahtarı yalnız set_flag komutunu çalıştırabilir. Sunucu anahtarını oyuna gömmeyin.

Yöntem ve yolAnahtarNe yapar
POST /v1/definitionsherhangi (aşağıdaki not)Şablonu yeni sürüm olarak kaydeder
GET /v1/definitions/{key}herhangiGüncel şablonu ve çözülmüş şemayı okur
POST /v1/traitsherhangi (not)Özellik kaydeder
POST /v1/level-curvesherhangi (not)Seviye eğrisi kaydeder
GET /v1/level-curves/{key}herhangiEğriyi okur
POST /v1/charactersherhangiŞablondan karakter oluşturur
GET /v1/charactersherhangiKarakterleri listeler (definition, cursor, limit)
GET /v1/characters/{id}herhangiKarakteri durumuyla okur
DELETE /v1/characters/{id}herhangiKarakteri siler (yumuşak silme, 204)
POST /v1/characters/{id}/commandskomuta göreKomut çalıştırır
GET /v1/characters/{id}/statsherhangiEtkin değerleri ve hesap dökümünü verir
GET /v1/characters/{id}/eventsherhangiOlay günlüğü (cursor, limit)
GET /v1/characters/{id}/timelineherhangiOkunur zaman çizelgesi
GET /v1/characters/{id}/state?at=herhangiGeçmiş bir andaki durumu yeniden kurar

Kimlik ve modül kuralları ile hata gövdeleri: Hatalar. Sayfalama: Sayfalama (limit 1–200, varsayılan 50; next_cursor null ise son sayfadır).

Not

Hata gövdeleri bu uçlarda düz metindir (text/plain). Yalnız module_disabled ve account_banned JSON döner.

Uyarı

Bilinen kısıt: Şablon, özellik, seviye eğrisi ve kural yazan uçlar ile karakter okuma/silme uçları anahtar türüne bakmaz; geçerli bir istemci anahtarı da çağırabilir. İstemci anahtarı ortam kapsamlıdır, oyuncu kapsamlı değildir: oyuna gömülen anahtarla o ortamdaki herhangi bir karakter okunabilir ya da silinebilir. Bu yüzden yönetim çağrılarını ve karakter silmeyi yalnız kendi sunucunuzdan yapın; oyuncudan gelen karakter kimliğine sunucunuzda sahiplik denetimi uygulayın. Anahtar türüne göre kapı henüz yok.

Karakter oluşturmak

POST /v1/characters
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{ "definition": "warrior", "external_id": "oyuncu-1234" }
{
  "id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
  "definition": "warrior",
  "definition_version": 3,
  "external_id": "oyuncu-1234",
  "version": 1,
  "level": 1,
  "state": {
    "attributes": { "strength": 10 },
    "resources": { "hp": 100 },
    "progression": { "level": 1, "xp": 0, "highest_level": 1, "granted_rewards": [] },
    "abilities": [],
    "flags": {},
    "custom": {}
  },
  "created_at": "2026-11-03T09:00:00Z"
}

Gövdede ayrıca allocation (puan dağıtımlı yaratımda alan → harcanan puan), seed (zarlı yaratımda zorunlu, belirleyici tohum) ve custom (en çok 64 KB serbest JSON) bulunabilir. Mod şablonda tanımlıdır; istek yalnız modun istediği girdiyi taşır.

Uyarı

Oluşturma ve silme uçları idempotency anahtarı almaz ve otomatik tekrar denenmez. Aynı external_id ile ikinci bir oluşturma 409 döner; bu, kopya karakter oluşmasını da engeller. Ağ hatasında önce external_id ile aramadan körlemesine tekrar göndermeyin.

Komut çalıştırmak

POST /v1/characters/{id}/commands
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json
Idempotency-Key: gorev-17-oyuncu-1234
If-Match: 1

{ "type": "add_xp", "amount": 250 }
{
  "id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
  "version": 2,
  "state": { "...": "güncel durum" },
  "events": [
    { "id": "…", "seq": 2, "type": "xp_added", "payload": { "...": "…" }, "created_at": "2026-11-03T09:01:00Z" }
  ],
  "replayed": false
}

Komut türleri type alanıyla seçilir:

typeGövde alanlarıAnahtar
set_flagkey, value (bool)istemci veya sunucu
set_attributekey, value (tam sayı)sunucu
add_resourcekey, delta (tam sayı, eksi olabilir)sunucu
add_modifierstat, operation (flat_add, flat_sub, percent_add, percent_mul, clamp), value, priority, source_kind (item, buff, skill, curse, quest), source_id, expires_atsunucu
remove_modifiersource_kind, source_idsunucu
add_xpamountsunucu
rollbackto_versionsunucu

İstemci anahtarıyla set_flag dışında bir komut gönderirseniz 403 alırsınız ve idempotency anahtarınız yanmaz.

Bir komut birden çok olay üretebilir: örneğin add_xp bir seviye atlatırsa yanıtta xp_added ve level_up birlikte gelir; tanımlı kurallar da ek olay üretir.

Eşzamanlılık ve tekrar güvenliği

  • Idempotency-Key (isteğe bağlı, 1–200 karakter): Aynı anahtar ve aynı gövde ikinci kez gelirse komut tekrar çalışmaz, ilk yanıt replayed: true ile döner. Aynı anahtarla farklı gövde 422’dir. Anahtar yalnız komut başarılı olunca “yanar”; reddedilen komut anahtarı tüketmez. Aynı anda uçuşta olan aynı anahtar 409 alır.
  • If-Match (isteğe bağlı): Okuduğunuz version değeri, 3 ya da "3" biçiminde. Karakter bu sürümde değilse komut 409 ile reddedilir ve hiçbir şey değişmez. * ve zayıf W/ biçimleri kabul edilmez (400). rollback komutunda If-Match işlenmez.

Genel kurallar: Idempotency ve eşzamanlılık.

Geri alma

rollback komutu geçmişi silmez. Karakteri to_version sürümündeki duruma döndüren yeni bir olay (character_rolled_back) yazar; olay günlüğü yalnızca eklenir.

Olaylar ve webhook’lar

Karakter olayları (flag_set, attribute_set, resource_changed, modifier_added, modifiers_removed, xp_added, level_up, ability_unlocked, event_emitted, character_rolled_back) webhook olarak da gönderilebilir. Zarfta event_id, type, character_id, environment_id, seq ve data bulunur. Kayıt ve doğrulama: Webhook’lar.

Sınırlar

  • custom alanı en çok 64 KB; içinde ondalık sayı olamaz.
  • Liste uçlarında limit 1–200.
  • Seviye 1’den küçük olamaz; en çok 65535.
  • GET …/state?at= geleceğe ya da eksi bir zamana 400 döner. at, Unix milisaniyesi veya RFC 3339 zamanıdır.

Sık görülen hatalar

BelirtiNedenÇözüm
403 permission deniedİstemci anahtarıyla set_flag dışında komutKomutu sunucudan, sunucu anahtarıyla gönderin
409 version conflict: expected 3, actual 4If-Match bayatKarakteri yeniden okuyun, yeni sürümle tekrar gönderin
409 already existsAynı external_id varVar olan karakteri GET /v1/characters ile bulun
422 idempotency mismatchAynı anahtar, farklı gövdeYeni bir anahtar üretin; yalnız ağ tekrarında aynı anahtarı kullanın
400 If-Match tam sürüm numarası olmalı* ya da zayıf etiket gönderildiYalnız sayı gönderin
404 + module_disabledcharacters modülü kapalıModülü açın

Çalışan örnek

Aşağıdaki örnek bir karakter oluşturur ve güvenle XP verir. Şablonun (warrior) panelden ya da POST /v1/definitions ile önceden tanımlı olması gerekir.

/** Güvenilir backend örneği: karakter oluşturur, XP verir. Server anahtarı yalnız sunucu ortamından gelir. */
import { CrxClient, CrxError } from "@ferdiunal/open-play-ts";

/** Oyuncunun karakterini açar ve ilk XP'yi güvenle (idempotent) verir. */
export async function createAndReward(baseUrl: string, serverApiKey: string, playerId: string): Promise<bigint> {
  const crx = new CrxClient({ baseUrl, apiKey: serverApiKey });

  // 1) Karakteri tanımdan oluştur. `external_id` oyunun kendi oyuncu kimliğidir.
  const character = await crx.createCharacter({
    body: { definition: "warrior", external_id: playerId },
  });

  // 2) XP ver: aynı Idempotency-Key ile tekrar denemek ikinci XP vermez.
  //    If-Match, karakterin okuduğunuz sürümüdür; araya başka komut girdiyse 409 döner.
  try {
    const result = await crx.executeCommand({
      path: { id: character.id },
      body: { type: "add_xp", amount: 250n },
      headers: {
        "Idempotency-Key": `quest-1-${playerId}`,
        "If-Match": character.version.toString(),
      },
    });
    return result.version;
  } catch (error) {
    if (error instanceof CrxError && error.status === 409) {
      // Sürüm çakışması: karakteri yeniden okuyup komutu yeni sürümle gönderin.
      throw new Error("Karakter bu arada değişti; yeniden okuyup tekrar deneyin.");
    }
    throw error;
  }
}

İlgili sayfalar

Envanter ve eşyalar

Bu sayfa, oyunun eşyalarını (iksir, silah, kozmetik…) tanımlamayı, oyuncuya vermeyi, tüketmeyi, devretmeyi ve envanterde yerleştirmeyi anlatır. Envanter durumunun sunucuda güvenle tutulmasını istiyorsanız okuyun.

Ne işe yarar

Eşya modülü (items) iki ayrı şeyi yönetir:

  • Eşya tanımı (item definition): “Küçük can iksiri” gibi bir tür. Anahtarı, türü, yığılabilir (stackable) olup olmadığı, takas edilebilirliği ve NPC alış/satış fiyatı burada durur.
  • Eşya örneği (item instance): Bir oyuncunun elindeki gerçek eşya. Hangi tanım sürümünde doğduğunu, sahibini, adedini ve özelleştirme verisini (metadata) taşır.

Envanter, bir sahibin eşyalarının yerleşimidir. Envanter yalnız hangi eşyanın hangi yuvada olduğunu tutar; adet ve özelleştirme eşya örneğindedir.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"items"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Eşya tanımlarını ve oyun verilerini (yerleşik eşya grubu) düzenlemekİçerik
Eşyaya 3B model bağlamak3D modeller, Model profili
Bir oyuncunun envanterini ve cüzdanını görmekOyuncu profili
Eşyayı mağazada satmakMağaza

Veri modeli (düz dille)

  • Tür (kind): consumable, equipment, material, cosmetic, vip_card.
  • Yığılabilir (stackable) ve max_stack: Yığılabilir eşyada aynı tanım sürümünden ve boş metadata’lı örnekler tek satırda birleşir. stackable: true ise max_stack zorunludur (1 ile 2147483647 arası).
  • tradeable: false ise eşya başka sahibe devredilemez (422). Varsayılan true.
  • npc_buy_price, npc_sell_price: En küçük birimde tam sayı. null ise tüccarla alım/satım kapalıdır.
  • Sürüm sabitleme (pin): Her örnek, doğduğu tanım sürümünü (definition_version) taşır. Tanımı sonradan değiştirmek eski örneklerin kurallarını değiştirmez.
  • Sahip (owner): <tür>:<uuid> biçimi; tür player, character, clan ya da system olabilir. Örnek: player:0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f.
  • Envanter: İlk komutta kendiliğinden açılır (varsayılan kapasite 32). Kapasite, dolu yuva sayısının üst sınırıdır; yuva numarası 0 veya daha büyük herhangi bir sayı olabilir. Hiç açılmamış envanteri okumak 404 döner ve envanteri açmaz.
  • version: Hem eşya örneğinin hem envanterin kendi sürümü vardır; her değişiklikte artar ve If-Match ile kullanılır.

Oyunun yaptığı çağrılar

Eşya ve envanter uçları API anahtarıyla çağrılır:

Authorization: Bearer crx_live_<önek>_<gizli>

Oyuncu tarafı iki okuma ucu sunar; bunlar oyuncu oturumuyla çağrılır (çerez ya da oturum belirteci, x-crx-env: <ortam uuid> başlığıyla):

Yöntem ve yolKimlikNe yapar
POST /v1/item-definitionssunucu anahtarıTanımı yeni sürüm olarak kaydeder (content alanıyla oyun verisi de yazılabilir)
GET /v1/item-definitions/{key}API anahtarıGüncel tanım sürümünü okur
POST /v1/itemssunucu anahtarıEşya üretir (mint)
GET /v1/items?owner=player:<uuid>API anahtarıSahibin eşyalarını listeler (cursor, limit)
GET /v1/items/{id}API anahtarıTek örneği okur
POST /v1/items/{id}/consumeAPI anahtarıAdet tüketir
POST /v1/items/{id}/destroysunucu anahtarıEşyayı yok eder
POST /v1/items/{id}/transferAPI anahtarıSahipliği devreder
GET /v1/inventories/{owner}API anahtarıEnvanteri ve dolu yuvaları okur
POST /v1/inventories/{owner}/moveAPI anahtarıEşyayı yuvaya yerleştirir ya da taşır
POST /v1/inventories/{owner}/splitAPI anahtarıYığından adet ayırıp yeni yuvaya koyar
POST /v1/inventories/{owner}/stackAPI anahtarıİki yığını birleştirir
POST /v1/inventories/{owner}/equipAPI anahtarıEşyayı kuşanma yuvasına koyar
POST /v1/inventories/{owner}/unequipAPI anahtarıKuşanılmış eşyayı başka yuvaya indirir
GET /v1/player/inventoryoyuncu oturumuOyuncunun kendi envanteri (salt okunur)
GET /v1/player/itemsoyuncu oturumuOyuncunun kendi eşyaları (salt okunur)
GET /v1/catalog/items, GET /v1/catalog/items/{key}sunucu anahtarı ya da oyuncu oturumuEşya kataloğu (aşağıda)

Not

TypeScript SDK’sında eşya ve envanter için tipli istemci yoktur; bu uçlar ham REST ile çağrılır. Oyuncu oturumuyla okuma (/v1/player/*) için de SDK yardımcısı henüz yok. Kapsam tablosu: TypeScript SDK.

Uyarı

Bilinen kısıt: consume, transfer, move, split, stack, equip ve unequip uçları anahtar türüne bakmaz; geçerli bir istemci anahtarı da çağırabilir ve owner ya da eşya kimliği istekte serbestçe verilir. İstemci anahtarı ortam kapsamlıdır, oyuncu kapsamlı değildir. Oyuncunun yalnız kendi eşyasına dokunduğunu kendi sunucunuzda doğrulayın; hile riskine karşı bu çağrıları oyun sunucusundan yapın. mint ve destroy yalnız sunucu anahtarıyla çalışır.

Eşya üretmek (mint)

POST /v1/items
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json
Idempotency-Key: quest-17-potion-oyuncu-1234

{
  "definition_key": "health_potion",
  "owner": "player:0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
  "quantity": 3,
  "source": "quest"
}
{
  "instance": {
    "id": "0192f1aa-1111-7222-8333-944455556666",
    "definition_id": "0192f0aa-0000-7000-8000-000000000001",
    "definition_version": 2,
    "owner_type": "player",
    "owner_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
    "quantity": 3,
    "metadata": {},
    "version": 1,
    "created_event_id": "0192f1aa-2222-7333-8444-955566667777",
    "created_at": "2026-11-03T09:00:00Z",
    "updated_at": "2026-11-03T09:00:00Z"
  },
  "event": { "type": "item_instance_created" },
  "replayed": false
}
  • quantity 1 ile 2147483647 arasındadır.
  • source mint, craft, purchase, quest ya da grant olabilir; verilmezse mint.
  • metadata doluysa eşya yığılmaz, her zaman tek satır olur.
  • Yığılabilir tanımda, sahibin elinde aynı sürümde boş metadata’lı bir yığın varsa yeni satır açılmaz, adet o yığına eklenir. max_stack aşılırsa 422.

Tüketmek, yok etmek, devretmek

UçGövdeNot
POST /v1/items/{id}/consume{"amount": 1}amount 1 ile mevcut adet arasında; aşım 422. Adet sıfıra inerse satır silinir ve sonraki GET 404 döner. Eşya türüne bakılmaz.
POST /v1/items/{id}/destroy{"amount": 1}Yalnız sunucu anahtarı. Aynı kurala tabidir; geçmiş olaylar silinmez.
POST /v1/items/{id}/transfer{"to_owner": "player:<uuid>"}tradeable: false ise 422.

Bu uçların üçünde de Idempotency-Key zorunludur (yoksa 400). If-Match isteğe bağlıdır ve eşya örneğinin version değeridir.

Envanter yerleşimi

Yerleştirme komutları aynı şekli taşır:

POST /v1/inventories/player:0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f/move
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json
Idempotency-Key: move-1
If-Match: 4

{ "item_instance_id": "0192f1aa-1111-7222-8333-944455556666", "to_slot": 5 }
UçGövde
moveitem_instance_id, to_slot
equip, unequipitem_instance_id, to_slot
splitfrom_instance_id, amount, to_slot
stackfrom_instance_id, to_instance_id

Yanıt { "inventory": {…}, "slots": [{ "slot_idx": 5, "item_instance_id": "…", "created_at": "…" }], "event": {…}, "replayed": false } biçimindedir.

Kurallar:

  • Dolu yuvaya yerleştirme 422’dir. Yeni bir eşya yerleştirirken kapasite aşımı da 422’dir; yerleşik bir eşyayı taşımak kapasite tüketmez.
  • If-Match, envanterin sürümüdür. İlk açılışta (envanter henüz yokken) If-Match gönderirseniz 400 alırsınız.
  • Aynı eşya başka bir envanterde yerleşikse 409 (çift yerleşim kilidi).
  • split: amount 1 ile kaynak adedinin bir eksiği arasındadır; eşya yığılabilir olmalıdır. Yeni örneğin kimliği yanıttaki olayda (new_instance_id) döner.
  • stack: iki yığın da yerleşik olmalı; aynı tanım sürümünde ve boş metadata’lı olmalıdır.
  • equip: tanımın metadata’sında equip_condition (CEL ifadesi) varsa kuşanma bu koşula bağlıdır. Koşul ya da değerlendirmesi hata verirse kuşanma reddedilir (422). Koşulda character bağlamı şimdilik boştur, now ve seed sabit 0’dır.

Eşya kataloğu

Oyunun istemcisi eşya tanımını, tipli veri satırlarını ve model adresini katalogdan okur. İki kitle vardır: oyuncu yalnız player görünürlüklü satırları görür; sunucu anahtarı bütün satırları, her satırın görünürlüğüyle ve content_revision ile görür. Yanıtlar ETag taşır; If-None-Match ile değişmediyse 304 döner. Ayrıntı ve izleme örneği: İçerik grupları ve Scene, görsel ve 3B modeller.

Sürümleme ve tekrar güvenliği

  • Mint, consume, destroy, transfer ve bütün envanter komutlarında Idempotency-Key zorunludur. Aynı anahtar ve aynı gövde ikinci kez gelirse işlem tekrar çalışmaz; yanıt replayed: true ile döner. Aynı anahtar, farklı gövde 422’dir. Anahtar yalnız başarıda yanar.
  • If-Match eşzamanlı yazmayı engeller (uyuşmazlık 409).
  • Genel kurallar: Idempotency ve eşzamanlılık.

Olaylar

Eşya olayları (item_instance_created, item_ownership_changed, item_consumed, item_stacks_merged, item_stack_split) kural otomasyonunda ve görev hedeflerinde kullanılabilir. Webhook uç noktasına filtre olarak seçilebilen olay türleri arasında eşya olayı yoktur. Filtresiz (event_types boş) kaydedilmiş bir uç nokta bütün olayları aldığı için eşya olaylarını da alır; bu olayların zarf biçimi belgelenmiş bir sözleşme değildir, ona güvenmeyin. Eşya değişikliğini dışarıya bildirmeniz gerekiyorsa kendi sunucunuzdan okuyun. Seçilebilir türler: Webhook olayları.

Sınırlar

  • Liste uçlarında limit 1–200; sayfalama imleçlidir.
  • Varsayılan envanter kapasitesi 32 dolu yuvadır.
  • quantity, amount ve fiyatlar tam sayıdır; ondalık yoktur.

Sık görülen hatalar

BelirtiNedenÇözüm
400 (anahtar yok)Idempotency-Key başlığı eksikHer yazıda anahtar gönderin
403İstemci anahtarıyla mint/destroy/tanım kaydıSunucu anahtarı kullanın
404 (envanter)Envanter hiç açılmadıİlk envanter komutu (örn. move) envanteri açar; okuma açmaz
409If-Match bayat ya da eşya başka envanterdeYeniden okuyup tekrar deneyin
422 yetersiz adetamount mevcut adetten büyükAdedi düşürün
422 takastradeable: falseTanımda takası açın
422 yuva doluHedef yuva doluBoş bir yuva seçin

Çalışan örnek

Ham REST ile iksir verip tüketen örnek (SDK’da tipli istemci olmadığı için fetch kullanır):

/** Envanter ve eşya uçları için SDK'da tipli istemci yoktur; ham REST ile çağrılır (sunucu anahtarı). */

/** Küçük yardımcı: Bearer + Idempotency-Key ile JSON POST. */
async function post(baseUrl: string, key: string, path: string, idempotencyKey: string, body: unknown, ifMatch?: number): Promise<unknown> {
  const headers: Record<string, string> = {
    Authorization: `Bearer ${key}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  };
  if (ifMatch !== undefined) headers["If-Match"] = String(ifMatch);
  const response = await fetch(`${baseUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
  if (!response.ok) throw new Error(`${path}: HTTP ${response.status} ${await response.text()}`);
  return response.json();
}

/** Oyuncuya bir iksir verir (mint, yalnız server anahtarı) ve 1 adedini tüketir. */
export async function grantAndDrink(baseUrl: string, serverKey: string, playerId: string): Promise<void> {
  const minted = (await post(baseUrl, serverKey, "/v1/items", `mint-${playerId}-potion-1`, {
    definition_key: "health_potion",
    owner: `player:${playerId}`,
    quantity: 3,
  })) as { instance: { id: string; version: number } };

  await post(
    baseUrl,
    serverKey,
    `/v1/items/${minted.instance.id}/consume`,
    `drink-${minted.instance.id}-1`,
    { amount: 1 },
    minted.instance.version, // If-Match: okuduğunuz sürüm; uyuşmazlık 409
  );
}

İlgili sayfalar

Ekonomi

Bu sayfa oyun içi paranın Open Play’de nasıl tutulduğunu ve hareket ettiğini anlatır: para birimi tanımı, cüzdan, defter (ledger), oyuncular arası transfer, NPC tüccar ve oyuncu takası. Oyuncuya para vermenin, harcatmanın ve ekonomiyi izlemenin güvenli yolunu arıyorsanız okuyun.

Ne işe yarar

Ekonomi iki modüle yayılır:

  • economy: para birimleri, cüzdanlar, defter, transfer ve raporlar.
  • trading: NPC tüccar (merchants) ve oyuncular arası takas (trades).

Her para hareketi çift kayıtlıdır (double-entry): bir cüzdandan çıkan tutar başka bir cüzdana ya da bir sistem hesabına girer ve ikisi aynı işlem kimliğiyle (tx_id) defterde durur. Defter yalnızca eklenir; hiçbir satır güncellenmez ya da silinmez. Hata düzeltmek için ters kayıt yazılır.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"economy"} (ya da trading) döner.

Panelde nerede

Ne yapacaksınızPanel sayfası
Para birimi oluşturmak, ikon, kuruş hanesi ve arz tavanı ayarlamakPara birimleri, Para birimi profili
Günlük özet, bakiye sıralaması, takaslar ve cüzdan sorgusu görmekEkonomi raporları
Oyuncunun cüzdanını ve hareketlerini görmekOyuncu profili
Ödül tablosu kurmakÖdül tabloları
Ürünü paraya bağlayıp satmakMağaza

Veri modeli (düz dille)

  • Para birimi (currency): Anahtarı (gold), adı, transferable (oyuncular arası transfer serbest mi), max_supply (toplam arz tavanı; yoksa sınırsız) ve ikonu vardır. Aynı anahtarla yeniden kayıt yeni sürüm üretir; eski sürümler değişmez. Kuruş hanesi (display_decimals) ve ikon görseli panelden ayarlanır; REST kaydı onları 0/boş bırakır.
  • Cüzdan (wallet): Bir sahibin bir para birimindeki bakiyesi. Sahip <tür>:<uuid> biçimindedir (player, character, clan, system). Cüzdan yazma ucu yoktur; cüzdan ilk hareketle kendiliğinden doğar ve doğduğu para birimi sürümüne bağlanır.
  • Bakiye: En küçük birimde tam sayı (i64). Ondalık sayı yoktur: “12,50 altın” gösterimi için panelde ayarlanan kuruş hanesini istemcide uygulayın.
  • Defter satırı: direction (giriş/çıkış), amount, reason (transfer, grant, burn, merchant_purchase, merchant_sale, iap, iap_revoke, party_reward, clan_treasury, quest, login, npc_reward, reward_table, trade_lock, trade_refund, trade_settle …) ve reference (bağlam).
  • Tüccar (merchant): Anahtarı, adı ve eşya başına alış/satış fiyat geçersiz kılmaları (price_overrides) olan NPC. Eşyanın kendi npc_buy_price/npc_sell_price değeri varsayılandır.
  • Takas (trade): İki oyuncu arasında durum makinesi: draft → offered → locked → completed ya da cancelled/expired.

Oyuncuya para nasıl verilir

Doğrudan “cüzdana şu kadar ekle” REST ucu yoktur. Para şu yollarla oyuncuya girer:

YolNe zaman
Ödül tablosu verme (POST /v1/reward-tables/{key}/grant)Görev, turnuva, giriş ödülü gibi oyunun verdiği paralar — bkz. Ödüller
Günlük giriş ödülü, görev, NPC ödülüÖdüller
Uygulama içi satın alma (IAP)Gerçek parayla alınan oyun parası — bkz. Mağaza
Tüccara eşya satmak (POST /v1/merchants/{key}/sell)Oyuncu eşyayı NPC’ye satar
Parti ödül dağıtımı, klan hazinesiSosyal
Panelden elle vermeOperatör; işlem denetim izine yazılır

Her biri defterde kendi reason değeriyle görünür.

Oyunun yaptığı çağrılar

Uçlar Authorization: Bearer crx_live_<önek>_<gizli> ile çağrılır. Para birimi ve tüccar yönetimi sunucu anahtarı ister (403); okuma ve oyuncu işlemleri herhangi bir API anahtarıyla yapılabilir.

Yöntem ve yolAnahtarNe yapar
POST /v1/currenciessunucuPara birimini yeni sürüm olarak kaydeder
GET /v1/wallets?owner=player:<uuid>API anahtarıSahibin cüzdanları (para birimi başına bir satır)
POST /v1/transfersAPI anahtarıCüzdandan cüzdana transfer (Idempotency-Key zorunlu)
GET /v1/ledger?wallet_id=<uuid>API anahtarıCüzdanın defter akışı (cursor, limit)
POST /v1/merchantssunucuTüccarı yeni sürüm olarak kaydeder
GET /v1/merchants/{key}API anahtarıTüccarın güncel içeriği
POST /v1/merchants/{key}/buyAPI anahtarıOyuncu tüccardan eşya alır
POST /v1/merchants/{key}/sellAPI anahtarıOyuncu tüccara eşya satar
POST /v1/tradesAPI anahtarıTakas taslağı açar
GET /v1/trades/{id}?actor_id=API anahtarıTakası görüntüler (yalnız iki taraf)
POST /v1/trades/{id}/offerAPI anahtarıBacakları kilitler
POST /v1/trades/{id}/acceptAPI anahtarıSon onay; atomik takas
POST /v1/trades/{id}/cancelAPI anahtarıİptal; kilitler açılır, para iade edilir
GET /v1/economy/faucet-sink, /flags, /merchant-gap, /top-holderssunucuGünlük ekonomi raporları
GET /v1/auth/admin/player-wallet-movements?player_id=sunucuOyuncunun son cüzdan hareketleri (limit 1–100, varsayılan 20)

Not

TypeScript SDK’sında bu modüller için tipli istemci yoktur; ham REST ile çağrılır. Bkz. TypeScript SDK.

Uyarı

Bilinen kısıt: Transfer, tüccar alım/satımı ve takas uçları anahtar türüne bakmaz; geçerli bir istemci anahtarı da çağırabilir ve cüzdan/oyuncu kimliği istekte serbestçe verilir. İstemci anahtarı ortam kapsamlıdır, oyuncu kapsamlı değildir. Para hareketlerini yalnız kendi oyun sunucunuzdan yapın ve oyuncudan gelen kimliği orada doğrulayın.

Para birimi kaydı

POST /v1/currencies
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{ "key": "gold", "name": "Altın", "transferable": true, "max_supply": 1000000000 }
{ "id": "0192f0aa-1000-7000-8000-000000000001", "key": "gold", "version": 1, "name": "Altın", "transferable": true, "max_supply": 1000000000, "icon": null }

key ve name boş olamaz; max_supply verilirse 0’dan büyük olmalıdır (400). transferable: false olan para birimi oyuncular arası transferde 422 döner (oyun içi harcamalar yine çalışır).

Transfer

POST /v1/transfers
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json
Idempotency-Key: transfer-2026-11-03-0001

{ "from_wallet": "0192f1…", "to_wallet": "0192f2…", "amount": 500, "reference": { "neden": "hediye" } }
{ "tx_id": "0192f3…", "from": { "id": "0192f1…", "balance": 1500, "version": 7 }, "to": { "id": "0192f2…", "balance": 500, "version": 3 }, "replayed": false }

(Yanıttaki from ve to cüzdan kayıtlarının tam alanları yer alır; yukarıda kısaltılmıştır.) Reddedilen durumlar: tutar 0 ya da eksi 400; para birimleri farklı 400; cüzdan yok 404; yetersiz bakiye, transferable: false ya da aynı anahtar farklı gövde 422; aynı anahtar uçuşta 409.

Tüccar alım ve satımı

POST /v1/merchants/blacksmith/buy
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json
Idempotency-Key: buy-oyuncu-1234-17

{ "player_id": "0192f0f2-…", "item_key": "iron_sword", "currency_key": "gold", "quantity": 1 }

Yanıt: { "event": {…}, "wallet_balance": 900, "wallet_version": 8, "inventory_id": "…", "inventory_version": 5, "replayed": false }.

Alım tek işlemde cüzdandan düşer, eşyayı yeni örnek olarak üretir, ilk boş yuvaya yerleştirir ve stoğu azaltır. Fiyat tanımsızsa, bakiye yetersizse ya da envanter doluysa 422; stok yetersizse 409. Satışta (sell) gövde { "player_id", "item_instance_id", "currency_key" } taşır ve yığının tamamı satılır; kısmi satış için önce split uygulayın. Her ikisinde Idempotency-Key zorunludur.

Oyuncu takası

  1. POST /v1/trades — { "initiator_player_id", "counterparty_player_id", "ttl_ms" } ile taslak açın (ttl_ms > 0; süre dolunca takas expired olur).
  2. İki taraf da POST /v1/trades/{id}/offer ile bacaklarını kilitler: { "actor_id", "legs": [ {"type":"item","item_instance_id":"…"}, {"type":"currency","currency_id":"…","amount":100} ] }. Eşya yerleşik, takas edilebilir ve başka bir takasta kilitli olmamalıdır (kilitliyse 409); para escrow’ya (emanet hesabına) alınır.
  3. Her iki taraf kilitleyince takas locked olur. POST /v1/trades/{id}/accept ({ "actor_id" }) yalnız başlatan oyuncuyla çalışır ve tek işlemde bütün bacakları takas eder. Alıcının envanterinde yer yoksa 422 döner ve takas kilitli kalır.
  4. POST /v1/trades/{id}/cancel iki taraftan biriyle çağrılabilir; kilitler açılır ve para iade edilir.

Taraf olmayan biri GET /v1/trades/{id} çağırırsa 404 alır (takasın varlığı gizlenir); actor_id sorgu parametresi zorunludur.

Sürümleme ve tekrar güvenliği

  • transfers, merchants/{key}/buy|sell, trades komutlarının hepsinde Idempotency-Key zorunludur (yoksa 400). Aynı anahtar ve aynı gövde işlemi tekrarlamaz, replayed: true döner; farklı gövde 422. Anahtar yalnız başarıda yanar.
  • Para birimi ve tüccar kaydı her seferinde yeni sürüm yazar; eski sürümler dokunulmazdır.
  • Defter okuması imleçlidir (limit 1–200, varsayılan 50).

Raporlar

Rapor uçları (faucet-sink, flags, merchant-gap, top-holders) UTC’de kapanmış günler üzerinden hesaplanır: from ve to dahildir ve en çok 366 gün aralık verilebilir. Eksik günler yanıtta missing_dates olarak listelenir; sıfır sayılmaz. iap_net gerçek para değil oyun parasıdır. flags bir inceleme sinyalidir; otomatik yaptırım uygulanmaz.

Olaylar

Para hareketleri outbox’a olay yazar. Webhook filtresi olarak seçilebilen türler arasında ekonomiye özel bir tür yoktur; filtresiz kayıtlı uç noktalar bütün olayları aldığı için para olaylarını da alır, ancak bu olayların zarf biçimi belgelenmiş bir sözleşme değildir. Mağaza ve ödül olayları seçilebilir türlerdir: Webhook’lar.

Sınırlar

  • Bakiye ve tutarlar i64 en küçük birimdir; tutar 0’dan büyük olmalıdır.
  • Para birimi max_supply tavanını aşan üretim (ödül, IAP, satış) 422 döner.
  • Raporlarda en çok 366 gün.

Sık görülen hatalar

BelirtiNedenÇözüm
400 (Idempotency-Key)Başlık eksikHer yazıda anahtar gönderin
403İstemci anahtarıyla para birimi/tüccar kaydıSunucu anahtarı kullanın
404 cüzdanTransferde verilen cüzdan kimliği yokKimliği GET /v1/wallets ile okuyun; sahibin o para biriminde hiç hareketi yoksa cüzdan henüz doğmamıştır ve liste boş döner
422 yetersiz bakiyeBakiye tutardan azBakiyeyi GET /v1/wallets ile okuyun
422 transferable=falsePara birimi transfer edilemezPara biriminde transferi açın ya da başka yol kullanın
409 stokTüccar stoğu yetmiyorAdedi düşürün

Çalışan örnek: para ver, sonra ürün satın al

Sunucu oyuncuya ödül tablosundan para verir; oyuncu kendi oturumuyla mağazadan ürün alır. İlk adım sunucuda, ikinci adım oyun istemcisinde çalışır. welcome_pack tablosu panelden (Ödül tabloları) ya da POST /v1/reward-tables ile önceden kurulmuş olmalıdır.

/** Ekonomi akışı: sunucu ödül tablosundan para verir, oyuncu kendi oturumuyla mağazadan ürün alır. */
import { CrxClient, StoreBuyErrorCode, storeBuyErrorCode, type PlayerClient } from "@ferdiunal/open-play-ts";

/** 1. adım (güvenilir sunucu): oyuncuya tablodan ödül verir. Aynı `requestKey` ile tekrar ödeme yapmaz. */
export async function grantWelcomeReward(baseUrl: string, serverApiKey: string, playerId: string, requestKey: string): Promise<void> {
  const crx = new CrxClient({ baseUrl, apiKey: serverApiKey });
  const grant = await crx.grantRewardTable("welcome_pack", {
    playerId,
    source: { type: "manual", operatorId: "game-server" },
    idempotencyKey: requestKey,
  });
  // `rewards` verilen birleşik ödüllerdir; `replayed: true` ikinci çağrının aynı sonucu geri verdiği anlamına gelir.
  console.log(grant.rewards, grant.replayed);
}

/** 2. adım (oyun istemcisi): oyuncunun gördüğü ilk ürünü satın alır. */
export async function buyFirstListing(player: PlayerClient, pageKey: string, idempotencyKey: string): Promise<string> {
  const catalog = await player.store.catalog({ page: pageKey });
  const listing = catalog.pages[0]?.listings[0];
  if (listing === undefined || listing.version === null) return "Vitrinde ürün yok.";
  try {
    // Gördüğünüz sürümü geri gönderin: fiyat arada değiştiyse sunucu satmaz (409 listing_changed).
    const result = await player.store.buy(listing.id, { expectedVersion: listing.version, quantity: 1, idempotencyKey });
    return `Satın alındı. Kalan bakiye: ${result.balanceAfter}`;
  } catch (error: unknown) {
    switch (storeBuyErrorCode(error)) {
      case StoreBuyErrorCode.InsufficientFunds: return "Bakiye yetmiyor.";
      case StoreBuyErrorCode.OutOfStock: return "Stok tükendi.";
      case StoreBuyErrorCode.ListingChanged: return "Ürün değişti; mağazayı yenileyin.";
      default: throw error;
    }
  }
}

İlgili sayfalar

Mağaza ve uygulama içi satın alma

Bu sayfa oyuncunun gördüğü mağaza vitrinini, oyun parasıyla satın alma akışını ve gerçek parayla yapılan uygulama içi satın almanın (IAP, in-app purchase) doğrulanmasını anlatır. Vitrin oluşturmak, ürün satmak ve ödemeyi sunucuda güvenle tamamlamak istiyorsanız okuyun.

Ne işe yarar

Mağaza modülü (store) üç şeyi birbirine bağlar:

  • Vitrin (page) ve ürün (listing): Operatörün panelden kurduğu, oyuncuya gösterilen katalog.
  • Ödeme: Ürün ya oyun parasıyla (currency: bir tüccar üzerinden, eşya karşılığı para) ya da gerçek parayla (iap: App Store ya da Google Play makbuzu) satılır.
  • Rezervasyon: Satın alma önce bir hak ayırır (stok ve oyuncu limiti), sonra ödemeyi tek işlemde tamamlar. Ödeme ve teslimat birlikte ya olur ya hiç olmaz.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"store"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Vitrin ve ürün oluşturmak, fiyat, stok, görünürlük penceresi ve oyuncu limiti ayarlamakMağaza, Ürün
Fiyat için para birimi tanımlamakPara birimleri
Satılacak eşyayı tanımlamakİçerik

Panelde IAP ürünleri ve makbuzları yalnız listelenir. Mağazaya IAP ürünü bağlamak ve platform kimlik bilgileri (App Store / Google Play) için bkz. Sınırlar.

Veri modeli (düz dille)

  • Vitrin (page): key (küçük harf, rakam, _, -; en çok 64), title, sort_order ve görünürlük. Açıklama, afiş görseli, ürün başlığı, rozet ve görsel gibi vitrin alanları panelden girilir; sunucu anahtarlı REST taslağı bunları taşımaz.
  • Ürün (listing): Bir vitrine bağlıdır. Ödeme biçimi currency (tüccar + eşya tanımı + para birimi + price_amount) ya da iap (IAP ürün kimliği) olur. limit_per_player (en az 1) ömür boyu oyuncu başına adet sınırıdır.
  • Görünürlük: active, tarih penceresi (starts_at/ends_at, Unix ms) ve isteğe bağlı bir CEL visibility_rule (oyuncuya özgü koşul; kayıtta derlenir, en çok 8192 bayt). Oyuncu vitrinini her okumada yeniden hesaplanır: koşul, saat ve kalan limit o anki duruma göre değerlendirilir.
  • Katalog sürümü (version): Vitrinde yapılan her değişiklik katalog sürümünü artırır; yönetim komutlarında If-Match bu sürümdür.
  • Ürün sürümü: Oyuncunun gördüğü ürünün version alanı; satın almada expected_version olarak geri gönderilir.
  • Rezervasyon: Durumu reserved, processing, completed, cancelled. reserved ve processing ödeme sayılmaz.
  • Fiyat: En küçük birimde tam sayı; vitrinde display_decimals (0–4) ile birlikte gelir. Gösterimi tamsayı/dize işlemiyle kurun, ondalık sayıya çevirmeyin.

Oyunun yaptığı çağrılar

İki kimlik türü vardır:

  • Oyuncu oturumu (çerez ya da oturum belirteci + x-crx-env: <ortam uuid>): vitrini okur ve kendi adına satın alır. Çerezli oturumda mutasyonlar CSRF çift gönderimi (x-csrf-token) ve Origin denetimi ister; bkz. Oturumlar ve CSRF.
  • Sunucu anahtarı (Authorization: Bearer crx_live_<önek>_<gizli>): katalog yönetimi, oyuncu önizlemesi, rezervasyon ve ödeme tamamlama.
Yöntem ve yolKimlikNe yapar
GET /v1/store/pages[?page=<key>&at=now]oyuncu oturumuOyuncunun görebildiği vitrinler; page tek vitrine daraltır
POST /v1/player/store/listings/{listing_id}/buyoyuncu oturumuOyuncu kendi adına satın alır
GET /v1/store/catalogsunucu anahtarıHam yönetim kataloğu (görünürlük süzülmez)
GET /v1/store/preview/{player_id}[?at=now]sunucu anahtarıBir oyuncunun göreceği vitrini önizler
POST /v1/store/pages, PUT/DELETE /v1/store/pages/{id}, POST /v1/store/pages/reordersunucu anahtarıVitrin yönetimi
POST /v1/store/pages/{page_id}/listings, PUT/DELETE /v1/store/listings/{id}, POST …/listings/reordersunucu anahtarıÜrün yönetimi
POST /v1/store/listings/{listing_id}/reservesunucu anahtarıOyuncu için hak ayırır (ödeme değildir)
POST /v1/store/reservations/{id}/checkout/currencysunucu anahtarıOyun parasıyla ödemeyi tamamlar
POST /v1/store/reservations/{id}/checkout/iapsunucu anahtarıDoğrulanmış IAP ile ödemeyi tamamlar
POST /v1/store/reservations/{id}/cancelsunucu anahtarıreserved rezervasyonu iptal eder (iade değildir)
POST /v1/iap/verifyAPI anahtarıIAP makbuzunu doğrular ve ödülü verir
POST /v1/webhooks/iap/{platform}/{environment_id}anahtarsız (imza)Platformun iade bildirimini alır

Hata zarfları: yönetim ve rezervasyon uçlarının hataları text/plain’dir; oyuncu satın alma ucu tipli code taşıyan JSON döner (aşağıda). Genel liste: Hatalar.

Vitrini okumak

GET /v1/store/pages?page=garaj
x-crx-env: 0192f000-0000-7000-8000-00000000e001
{
  "version": 12,
  "pages": [
    {
      "id": "…", "key": "garaj", "title": "Garaj",
      "listings": [
        {
          "id": "0192f111-…", "version": 3,
          "title": "Hızlı Araba", "badge": "sale",
          "price": { "currency_key": "gold", "currency_name": "Altın", "amount": 1250, "display_decimals": 0, "icon_url": null },
          "item": { "key": "fast_car", "name": "Hızlı Araba" },
          "remaining": 40
        }
      ]
    }
  ]
}

Yanıt Cache-Control: private, no-store taşır. Görünmeyen ya da bilinmeyen page anahtarı 404 döner. Oyuncu kimliği, saat ve CEL sonucu istemciden alınmaz; sorguya player_id ya da başka bir alan eklemek 400 döner.

Oyuncunun satın alması

POST /v1/player/store/listings/0192f111-…/buy
x-crx-env: 0192f000-0000-7000-8000-00000000e001
x-csrf-token: <csrf>
Idempotency-Key: buy-oyuncu-1234-0001
Content-Type: application/json

{ "quantity": 1, "expected_version": 3 }
{
  "reservation_id": "…",
  "payment_reference": "…",
  "item_instance_ids": ["…"],
  "balance_after": 750,
  "replayed": false
}

Gövdede yalnız quantity (pozitif) ve expected_version bulunur; oyuncu kimliği, fiyat ya da ödeme iddiası kabul edilmez ve bilinmeyen alan 400 döner. Kimlik oturumdan gelir. Rezervasyon ve tahsilat tek işlemdedir. Idempotency-Key zorunludur: aynı anahtar ve aynı gövde kalıcı sonucu replayed: true ile tekrar eder ve ikinci tahsilat yapmaz; aynı anahtarla farklı gövde 422’dir. IAP ürünü bu uçta alınamaz (422).

Reddedilen satın almalar tipli JSON kod taşır, {"code": "...", "message": "..."}:

codeHTTPAnlamı
not_visible404Ürün bu oyuncuya görünmüyor ya da yok
listing_changed409Ürün siz bakarken değişti (expected_version bayat); vitrini yenileyin
out_of_stock422Stok bitti
limit_reached422Oyuncu başına limit doldu
insufficient_funds422Bakiye yetersiz
inventory_full422Envanter dolu; işlem tamamen geri alınır

Diğer hatalar (401, 403 CSRF/Origin, 400, 429 bütçe, 500) genel kurallara tabidir.

Sunucu tarafı akış: rezervasyon ve ödeme

Ödemeyi oyun sunucusunun yönettiği durumlarda (örneğin gerçek para makbuzunu sizin sunucunuz alıyorsa) iki adım kullanın:

POST /v1/store/listings/{listing_id}/reserve
Authorization: Bearer crx_live_<önek>_<gizli>
Idempotency-Key: reserve-oyuncu-1234-0001
If-Match: 12
Content-Type: application/json

{ "player_id": "0192f0f2-…", "quantity": 1 }

Yanıt { catalog_version, entity_id, event_id, event, reservation, replayed } biçimindedir; reservation.status reserved’dır. Ardından:

POST /v1/store/reservations/{id}/checkout/currency
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{ "player_id": "0192f0f2-…" }
{ "reservation_id": "…", "payment_reference": "…", "kind": "currency", "replayed": false }

checkout uçlarında Idempotency-Key ve If-Match gönderilmez (gönderilirse 400); kalıcı rezervasyon kimliği tekrar güvenliğinin çapasıdır. Para birimiyle ödemede hata olursa işlem geri alınır ve rezervasyon reserved kalır; yeniden deneyebilir ya da iptal edebilirsiniz. IAP ödemesinde sonuç belirsizse rezervasyon processing olur ve oyuncu limiti korunur; aynı credential ile yeniden deneyin. IAP ödemesinde quantity 1’dir.

reserve ve katalog yönetim komutlarında Idempotency-Key zorunludur; If-Match katalog sürümüdür ve isteğe bağlıdır (uyuşmazlık 409). Başarısız komut anahtarı yakmaz.

IAP doğrulaması

POST /v1/iap/verify
Authorization: Bearer crx_live_<önek>_<gizli>
Idempotency-Key: iap-oyuncu-1234-tx-abc
Content-Type: application/json

{ "platform": "app_store", "product_sku": "gems_500", "credential": "<transactionId>", "player_id": "0192f0f2-…" }
{ "receipt_id": "…", "status": "granted", "product_sku": "gems_500", "transaction_id": "…", "grants": [ … ], "replayed": false }
  • platform: app_store ya da google_play. credential: App Store için transactionId, Google Play için purchaseToken. Sunucu platformu kendisi sorgular; istemciye güvenilmez.
  • Aynı işlemi iki kez doğrulamak ikinci ödül vermez (200, replayed: true).
  • status verified, granted, revoked ya da failed olabilir; mutlu yol granted’dır.
  • Hatalar: sahte makbuz 403; ürün katalogda yok ya da işlem platformda tanınmıyor 404; product_sku uyuşmazlığı, platform yapılandırılmamış ya da arz tavanı aşımı 422; aynı anahtarla uçuşta başka istek 409.
  • İade bildirimi: App Store (ASN V2) ve Google Play (RTDN) bildirimleri POST /v1/webhooks/iap/{platform}/{environment_id} adresine gelir; uç anahtarsızdır, kimlik doğrulaması imzadır. Bilinen bir makbuzun iadesinde ödül ters kayıtlarla geri alınır ve makbuz revoked olur; kısmen geri alınabilen durumlar flagged olarak işaretlenir (varsayılan politika, tüketilmiş kısma dokunmaz). İade dışı bildirimler ve bilinmeyen işlemler 200 alır, yok sayılır.

Sürümleme ve tekrar güvenliği

  • Oyuncu satın alma: expected_version (ürün sürümü) + Idempotency-Key.
  • Katalog yönetimi ve rezervasyon: Idempotency-Key zorunlu, If-Match = katalog sürümü.
  • Ödeme tamamlama: kalıcı rezervasyon kimliği; başlık yok.
  • Genel kurallar: Idempotency ve eşzamanlılık.

Olaylar ve webhook’lar

Mağaza olayları webhook olarak gönderilebilir: store_page_created, store_page_updated, store_page_deleted, store_pages_reordered, store_listing_created, store_listing_updated, store_listing_deleted, store_listings_reordered, store_purchase_initiated, store_payment_started, store_purchase_completed, store_reservation_cancelled. Zarfta event_id, environment_id, type, seq, now_ms, actor_api_key_id ve data bulunur. Kayıt ve doğrulama: Webhook’lar.

Sınırlar ve bilinen kısıtlar

  • Vitrin anahtarı ^[a-z0-9_-]{1,64}$; sıralama isteğinde en çok 2000 giriş.
  • Oyuncu satın alma ucunda IAP ürünü desteklenmez; IAP ürünleri checkout/iap ya da iap/verify ile alınır.
  • IAP ürün kaydı: Panelde IAP ürünleri ve makbuzları salt okunurdur ve ürün kaydetmek için herkese açık bir HTTP ucu henüz yoktur. IAP ürünü eklenmesi şimdilik Open Play operatörü üzerinden yapılır.
  • Platform kimlik bilgileri: App Store ve Google Play doğrulama kimlik bilgileri dağıtımda operatör tarafından tanımlanır. Tanımlı değilse iap/verify ve checkout/iap 422 döner.
  • Vitrini oyuncu açtığında ya da yenilediğinde okuyun; değişiklikler okuma anında yeniden hesaplanır.

Sık görülen hatalar

BelirtiNedenÇözüm
409 listing_changedVitrinde okuduğunuz ürün sürümü eskidiVitrini yeniden okuyun, yeni version ile deneyin
404 not_visibleÜrün oyuncuya görünmüyor (pencere, koşul, pasif)Vitrini yenileyin; koşulu panelde kontrol edin
422 insufficient_fundsBakiye yetersizBakiye gösterin; ödül ya da IAP ile para verin
400 (bilinmeyen alan)Satın alma gövdesine fazladan alan eklendiYalnız quantity ve expected_version gönderin
400 (checkout)Idempotency-Key/If-Match gönderildiCheckout uçlarında bu başlıkları kaldırın
403 (satın alma)CSRF çifti ya da Origin reddiCSRF belirtecini yenileyin; bkz. Oturumlar ve CSRF
403 (iap/verify)Sahte ya da imzası geçersiz makbuzMakbuzu platformdan yeniden alın
422 (iap)Platform yapılandırılmamış ya da product_sku uyuşmuyorOperatörle iletişime geçin; sku’yu kontrol edin

Çalışan örnek

Vitrini okuyup ilk ürünü satın alma örneği Ekonomi sayfasında; hata kodlarına göre ayrı mesaj gösterir (storeBuyErrorCode). TypeScript SDK’sı player.store.catalog() ve player.store.buy() yardımcılarını sağlar; Idempotency-Key verilmezse çağrı başına üretir, ağ kesintisinden sonra aynı anahtarı kendiniz vermelisiniz.

İlgili sayfalar

Ödüller

Bu sayfa oyunun oyuncuya verdiği ödüllerin Open Play’de nasıl tanımlandığını ve verildiğini anlatır: şansa bağlı ödül tabloları, günlük giriş ödülü, görevler, NPC ödülleri ve VIP kartları. Ödülün sunucuda, hile yapılamadan ve tek seferlik verilmesini istiyorsanız okuyun.

Ne işe yarar

Ödül modülü (rewards) “oyuncuya para ya da eşya ver” işini tek bir güvenilir hat üzerinden yapar. Her ödül verme işlemi tek bir veritabanı işlemidir: para cüzdana yazılır, eşya envantere konur ve olay kaydedilir. Envanter doluysa ya da para birimi arz tavanını aşarsa hiçbir şey verilmez (422).

Tablolu ödüller belirlenimcidir (deterministic): aynı tohum aynı sonucu verir. Tohum sunucuda tutulur; yanıtta, webhook olayında ve defter kaydında görünmez.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"rewards"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Ödül tablosu kurmak, satır şanslarını ve garanti ödülleri düzenlemek, sürümleri görmekÖdül tabloları, Ödül tablosu profili
Ödülde kullanılacak para birimini tanımlamakPara birimleri
Ödülde kullanılacak eşyayı tanımlamakİçerik
Olay tetikli otomasyonla ödül vermekOyun kuralları

Veri modeli (düz dille)

  • Ödül tablosu: key, name, rolls (kaç kez çekiliş yapılacağı, 1–10), entries (satırlar) ve guaranteed (her seferinde verilen ödüller). Her kayıt yeni bir sürüm olur; grant sırasında güncel sürüm kullanılır ve sonuçta sürüm numarası döner.
  • Satır (entry): chance_bp (baz puan, 1–10000) ve drop. Bir çekilişin satır şansları toplamı tam 10000 (%100) olmalıdır; “ödül yok” bir satır olabilir.
  • Ödül (drop): currency (key, min, max), item (key, min, max) ya da nothing. Adet her zaman min ile max arasından eşit dağılımla seçilir; adetler 1 ile 1.000.000 arasındadır. Tabloda en çok 50 satır, 10 garanti ödül bulunur.
  • Garanti ödül: Çekilişten bağımsız verilir; nothing olamaz.
  • Kaynak (source): Ödülün neden verildiğini tipli olarak yazar: content_entry, tournament, leaderboard_season, quest, npc_reward, rule ya da manual.

Oyunun yaptığı çağrılar

Tüm ödül uçları sunucu anahtarıyla (ya da yönetici türünde bir anahtarla) çağrılır; istemci anahtarı ve oyuncu oturumu reddedilir. Oyuncu kimliği güvenilir sunucu bileşeninin gövdesinde taşınır:

Authorization: Bearer crx_live_<önek>_<gizli>
Yöntem ve yolNe yapar
POST /v1/reward-tablesÖdül tablosunu yeni sürüm olarak kaydeder
GET /v1/reward-tables/{key}Güncel sürümü okur
POST /v1/reward-tables/{key}/grantTablodan idempotent ödül verir
GET /v1/login-rewards/{player_id}Bugünün giriş ödülü durumunu okur
POST /v1/login-rewards/{player_id}/claimGünlük giriş ödülünü verir
POST /v1/questsGörev tanımı kaydeder
GET /v1/quests, GET /v1/quests/{key}Görevleri listeler / okur
POST /v1/quests/{key}/claimGörev dönemi ödülünü verir
GET /v1/npc-rewards/{key}/eligibilityNPC ödülü uygunluğunu okur (yazmaz)
POST /v1/npc-rewards/{key}/claimNPC ödülünü verir
GET /v1/vip/{player_id}VIP katmanını okur
POST /v1/vip/{player_id}/cards/{item_instance_id}/useVIP kartı kullanır

Not

TypeScript SDK’sında yalnız ödül tablosu için tipli istemci vardır (CrxClient.createRewardTable, getRewardTable, grantRewardTable). Giriş ödülü, görev, NPC ve VIP uçları ham REST ile çağrılır. Bkz. TypeScript SDK.

Ödül tablosu kaydetmek

POST /v1/reward-tables
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "key": "welcome_pack",
  "name": "Hoş geldin paketi",
  "rolls": 1,
  "entries": [
    { "chance_bp": 7000, "drop": { "type": "currency", "key": "gold", "min": 100, "max": 300 } },
    { "chance_bp": 2500, "drop": { "type": "item", "key": "health_potion", "min": 1, "max": 3 } },
    { "chance_bp": 500,  "drop": { "type": "nothing" } }
  ],
  "guaranteed": [ { "type": "currency", "key": "gold", "min": 50, "max": 50 } ]
}

Yanıt 201 ve { id, version, key, name, rolls, entries, guaranteed, created_at_ms } döner; çekiliş tohumu hiçbir yanıtta bulunmaz. Şans toplamı 10000 değilse, tablo boşsa ya da sınırlar aşılıyorsa kayıt 422 ve düz metin iletiyle reddedilir. validate anahtarı ayrılmıştır, tablo anahtarı olarak kullanılamaz.

Tablodan ödül vermek

POST /v1/reward-tables/welcome_pack/grant
Authorization: Bearer crx_live_<önek>_<gizli>
Idempotency-Key: welcome-oyuncu-1234
Content-Type: application/json

{ "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "source": { "type": "manual", "operator_id": "game-server" } }
{
  "grant_id": "…", "tx_id": "…", "event_id": "…",
  "table_key": "welcome_pack", "table_version": 1,
  "rewards": [ { "drop": "currency", "key": "gold", "quantity": 250 }, { "drop": "item", "key": "health_potion", "quantity": 2 } ],
  "replayed": false
}

Idempotency-Key zorunludur. Aynı anahtar ve aynı gövde ödülü ikinci kez vermez; ilk sonuç replayed: true ile döner. Aynı anahtar farklı gövdeyle 422’dir. source değişkenleri:

typeEk alanlar
manualoperator_id
content_entrygroup, entry
questquest_key
npc_rewardnpc_key
tournamenttournament_id, placement
leaderboard_seasonleaderboard_key, season
rulerule_key, event_id

Para ve eşya tek işlemde verilir: envanter doluysa ya da para birimi arz tavanı aşılırsa bütün verme geri alınır ve 422 döner. Oyuncuya bağlı olmayan bir karakter için NPC ödülü verilemez.

Günlük giriş ödülü

GET /v1/login-rewards/{player_id} bugünün durumunu verir: period_date, claimed, streak_day, current_streak, config_version. POST /v1/login-rewards/{player_id}/claim ödülü verir ve { period_date, streak_day, rewards, event_id, tx_id, replayed } döner; aynı gün ikinci çağrı aynı makbuzu replayed: true ile geri verir, ikinci ödül vermez.

Gün, ortamın saat dilimine (timezone, Ortamlar ve alan adları) göre hesaplanır. Ödül çizelgesi (gün sayısı, her günün para ödülü) ve kaçırılan gün politikası (reset_on_miss: seri 1’e döner; freeze: seri korunur) sürümlü bir yapılandırmadır.

Not

Giriş ödülü yapılandırmasını (çizelge ve seri politikası) yazan herkese açık bir uç ya da panel ekranı henüz yoktur; yapılandırma şimdilik Open Play operatörü tarafından kurulur.

Görevler

POST /v1/quests görev tanımını yeni sürüm olarak kaydeder:

{
  "key": "kill_10_wolves",
  "name": "On kurt avla",
  "objectives": [ { "key": "wolves", "event_type": "event_emitted", "count": 10, "filter_cel": "event.event_key == \"wolf_killed\"" } ],
  "rewards": [ { "type": "currency", "key": "gold", "amount": 500 }, { "type": "table", "key": "welcome_pack" } ],
  "recurrence": "daily"
}
  • objectives[].event_type: görevi ilerleten olay türü (karakter olayları, score_submitted, login, eşya ve para olayları gibi); count kaç kez gerektiği; filter_cel isteğe bağlı koşul.
  • rewards[].type: currency (key, amount) ya da table (ödül tablosu anahtarı).
  • recurrence: none, daily, weekly. window ve prerequisite_cel (kayıt anında derlenir) isteğe bağlıdır.
  • POST /v1/quests/{key}/claim gövdesi { "player_id": "…", "period_key": "…" }’dir (period_key en çok 160 karakter). Yanıt { status: "claimed" | "already_claimed", reward_count, event_id } — aynı dönemi ikinci kez almak already_claimed döner, ödül tekrarlanmaz.
  • GET /v1/quests search, limit, offset ile listeler (sayfa içi total döner).

Not

Görev ilerlemesini (oyuncunun hangi görevde kaçta olduğunu) okuyan herkese açık bir REST ucu henüz yoktur. İlerleme olaylardan sunucuda hesaplanır; period_key değerini bu nedenle oyun sunucunuzun kendisi bilmelidir.

NPC ödülleri

GET /v1/npc-rewards/{key}/eligibility?character_id=…&period_key=… yazmadan uygunluğu söyler: { eligible, reason } (reason: cooldown, daily_limit, condition). POST /v1/npc-rewards/{key}/claim (Idempotency-Key zorunlu, gövde { "character_id", "period_key" }) ya ödülü verir ya da rejected döner: { status: "claimed" | "rejected", reward_count, claim_count, tx_id, reason }. NPC kuralını (bekleme, günlük sınır, CEL koşulu, ödül) kaydeden herkese açık bir uç henüz yoktur.

VIP

VIP katmanı kümülatif puandan türetilir. GET /v1/vip/{player_id} { player_id, cumulative_points, tier, config_version } verir. POST /v1/vip/{player_id}/cards/{item_instance_id}/use (Idempotency-Key zorunlu) bir vip_card eşyasını tüketir ve puan ekler; kart bir üst katman koşulunu (mevcut katman + 1) karşılamıyorsa 422 döner. İstemci ve sunucu anahtarı aynı VIP yüzeyini kullanabilir.

Sürümleme ve tekrar güvenliği

  • Tablo grant, NPC claim ve VIP kart kullanımı: Idempotency-Key zorunlu.
  • Giriş ödülü: aynı gün ikinci claim aynı makbuzu döner; başlık gerekmez.
  • Görev claim: dönem anahtarı doğal tekrar güvenliğidir (already_claimed).
  • Tablo ve görev kayıtları her seferinde yeni sürümdür; eski sürümler değişmez.
  • Genel kurallar: Idempotency ve eşzamanlılık.

Olaylar ve webhook’lar

Tablodan veya sabit para ile verilen her ödül reward_granted olayı üretir ve webhook olarak gönderilebilir. Zarfta event_id, environment_id, player_id, type ve data (source_kind, table_key, table_version ve verilenler) bulunur; tohum yoktur. Kayıt ve doğrulama: Webhook’lar.

Sınırlar

  • Tabloda en çok 50 satır, 10 garanti ödül, 10 çekiliş (rolls); adetler 1–1.000.000.
  • chance_bp toplamı tam 10000.
  • Görev period_key ve claim alanları en çok 160 karakter.

Sık görülen hatalar

BelirtiNedenÇözüm
403İstemci anahtarı ya da oyuncu oturumuyla çağrıÇağrıyı sunucu anahtarıyla, sunucunuzdan yapın
422 (tablo kaydı)Şanslar 10000’e tamamlanmıyor ya da sınır aşıldıSatırları düzeltin; nothing satırıyla tamamlayın
422 envanter doluVerilecek eşya için boş yuva yokOyuncuya yer açtırın; işlem hiç yazılmadı, tekrar denenebilir
422 arz tavanıPara birimi max_supply sınırıTavanı panelden yükseltin
422 idempotency mismatchAynı anahtar, farklı gövdeYeni anahtar kullanın
404 tablo yokkey yanlış ya da tablo henüz kaydedilmediGET /v1/reward-tables/{key} ile doğrulayın

Çalışan örnek

Hoş geldin tablosundan para verme ve ardından mağazadan alma akışı Ekonomi sayfasındadır. TypeScript’te crx.grantRewardTable("welcome_pack", { playerId, source, idempotencyKey }) çağrısı aynı uca gider; idempotencyKey verilmezse SDK çağrı başına üretir, ağ tekrarında aynı anahtarı kendiniz vermelisiniz.

İlgili sayfalar

Klanlar ve partiler

Bu sayfa kalıcı oyuncu topluluklarını (klan) ve geçici takımları (parti) Open Play’de nasıl kuracağınızı ve yöneteceğinizi anlatır. Üyelik, davet, klan hazinesi, klan XP’si ve parti ödül dağıtımını güvenilir bir sunucudan yönetmek istiyorsanız okuyun.

Ne işe yarar

Sosyal modül (social) iki yapıyı yönetir:

  • Klan (clan): Kalıcı topluluk. Lider, subay ve üye rolleri, davet ve başvuru akışı, ortak hazine (para birimi) ve XP/seviye vardır.
  • Parti (party): Geçici takım. Kapasitesi, ganimet kuralı ve ödül bölüştürmesi vardır; dağılınca sona erer.

Önemli: Bu uçların hepsi sunucu anahtarıyla çağrılır. Oyuncu istemcisi doğrudan klan ya da parti ucunu çağıramaz; oyuncunun kimliği isteğin gövdesinde (actor_player_id gibi) sizin güvenilir sunucunuz tarafından yazılır. Oyuncunun “kendi klanını yönetebildiğini” doğrulamak sizin sunucunuzun işidir; Open Play klan içindeki rol kurallarını (kimin kimi atabileceği, hazineden kimin çekebileceği) uygular.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"social"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Klanları listelemek, üyelik, davet, başvuru, kasa ve komut geçmişini görmekKlanlar
Partileri, üyelerini, ganimet kuralını ve ödül dağıtımlarını görmekPartiler
Klan ve parti sohbetiSohbet, Şehir sohbeti
Klan hazinesinde kullanılan para birimiPara birimleri

Veri modeli (düz dille)

Klan

  • name (3–64 karakter), tag (2–8 karakter), leader_player_id, xp, highest_level_reached, state (active ya da disbanded) ve members.
  • Roller: leader, officer, member.
  • Ortam ayarları (/v1/clan-settings): max_members (üye sınırı), request_ttl_seconds (davet ve başvuru ömrü), officer_can_withdraw (subay hazineden çekebilir mi) ve level_curve_key (klan seviyesi için seviye eğrisi).
  • Davet (invite): Klanın oyuncuya gönderdiği çağrı. Durumları pending, accepted, declined, revoked, expired.
  • Başvuru (application): Oyuncunun klana başvurusu. Durumları pending, accepted, rejected, cancelled, expired.
  • Hazine: Klanın cüzdanı; deposit ve withdraw ile para birimi hareketi yapılır ve defterde clan_treasury olarak görünür.

Parti

  • leader_player_id, capacity (pozitif), loot_rule (equal: eşit pay, weighted: baz puan payları), state (active, disbanded) ve members.
  • Davet: share_bp (üyenin baz puan payı; weighted kuralda payların toplamı tam 10000 olmalıdır) taşır ve 24 saat geçerlidir. Durumları pending, accepted, expired.
  • Ödül dağıtımı: Tek bir para birimi tutarını üyelere böler. Eşit dağıtımda artık kuruşlar player_id sırasına göre dağıtılır; sonuç her zaman belirlenimcidir. Dağıtım kaydı üye anlık görüntüsünü, bacakları, delivery_mode (inline ya da outbox) ve durumu (processing, queued, completed) taşır.

Oyunun yaptığı çağrılar

Hepsi sunucu (ya da yönetici) anahtarıyla:

Authorization: Bearer crx_live_<önek>_<gizli>

İstemci anahtarı ve oyuncu oturumu reddedilir (403).

Klan

Yöntem ve yolGövdeNe yapar
GET /v1/clan-settings, PATCH /v1/clan-settingsmax_members, request_ttl_seconds, officer_can_withdraw, level_curve_keyOrtam ayarlarını okur / günceller
GET /v1/clans—Klanları listeler
POST /v1/clansleader_player_id, name, tagKlan kurar
GET /v1/clans/{clan_id}—Klanı üyeleriyle okur
PATCH /v1/clans/{clan_id}actor_player_id, name, tagAd ve etiketi günceller
POST /v1/clans/{clan_id}/invitesinviter_player_id, invitee_player_idDavet gönderir
GET /v1/clan-invites/{invite_id}, POST …/respondactor_player_id, decision (accept, decline, revoke, expire)Daveti okur / yanıtlar
POST /v1/clans/{clan_id}/applicationsapplicant_player_idBaşvuru açar
GET /v1/clan-applications/{application_id}, POST …/resolveactor_player_id, decision (accept, reject, cancel, expire)Başvuruyu okur / çözer
POST /v1/clans/{clan_id}/leaveactor_player_idÜye ayrılır
POST /v1/clans/{clan_id}/members/kickactor_player_id, target_player_idÜyeyi çıkarır
POST /v1/clans/{clan_id}/members/roleactor_player_id, target_player_id, roleRol değiştirir
POST /v1/clans/{clan_id}/leadershipactor_player_id, target_player_idLiderliği devreder
POST /v1/clans/{clan_id}/disbandactor_player_idKlanı dağıtır
POST /v1/clans/{clan_id}/treasury/deposit, …/withdrawactor_player_id, currency_id, amountHazineye yatırır / çeker
POST /v1/clans/{clan_id}/xpamountKlana XP ekler

Klan yazma komutlarının hepsinde Idempotency-Key zorunludur. Yanıt hep aynı zarftadır: { "type": "clan" | "invite" | "application" | "settings" | "treasury" | "xp", "<tür>": { … }, "replayed": false }. Örneğin klan kurma:

POST /v1/clans
Authorization: Bearer crx_live_<önek>_<gizli>
Idempotency-Key: clan-kur-oyuncu-1234
Content-Type: application/json

{ "leader_player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "name": "Kurt Sürüsü", "tag": "KRT" }

201 döner ve type: "clan" zarfı içinde klan kaydı gelir. TypeScript SDK’sı bu uçları tipli olarak sunar (createClan, getClan, createClanInvite, respondClanInvite, depositClanTreasury, addClanXp …); bkz. TypeScript SDK.

Parti

Yöntem ve yolGövdeNe yapar
POST /v1/partiesleader_player_id, capacity, loot_ruleParti kurar
GET /v1/parties?player_id=—Oyuncunun partilerini listeler
GET /v1/parties/{party_id}—Partiyi üyeleriyle okur
PATCH /v1/parties/{party_id}actor_player_id, capacityKapasiteyi günceller
POST /v1/parties/{party_id}/invitesinviter_player_id, invitee_player_id, share_bpDavet gönderir
POST /v1/party-invites/{invite_id}/acceptactor_player_idDaveti kabul eder
POST /v1/parties/{party_id}/leaveactor_player_idÜye ayrılır
POST /v1/parties/{party_id}/disbandactor_player_idPartiyi dağıtır
POST /v1/parties/{party_id}/rewardscurrency_key, amountÖdülü üyelere böler

Parti ödül dağıtımında Idempotency-Key zorunludur; aynı anahtarla tekrar aynı dağıtımı replayed: true ile döndürür, ikinci dağıtım yapmaz. Diğer parti komutları Idempotency-Key istemez.

Not

TypeScript SDK’sında parti uçları için tipli istemci yoktur; ham REST ile çağrılır.

Sürümleme ve tekrar güvenliği

  • Klan komutları ve parti ödül dağıtımı Idempotency-Key ister: aynı anahtar ve aynı gövde tekrar çalışmaz (replayed: true); aynı anahtar farklı gövde 422; uçuşta aynı anahtar 409.
  • If-Match bu modülde kullanılmaz.
  • Genel kurallar: Idempotency ve eşzamanlılık.

Olaylar

Klan ve parti olayları (ClanMemberJoined, ClanXpAdded, PartyMemberJoined, RewardDistributed …) kural otomasyonunda ve görev hedeflerinde kullanılabilir. Webhook filtresi olarak seçilebilen türler arasında klan ya da parti olayı yoktur; filtresiz kayıtlı uç noktalar bütün olayları alır ve bunlara klan/parti olayları dahildir, ancak zarf biçimi belgelenmiş bir sözleşme değildir. Seçilebilir türler: Webhook olayları.

Sınırlar

  • Klan adı 3–64, etiketi 2–8 karakter.
  • Parti kapasitesi pozitif olmalı; kapasite aşımı reddedilir. Parti daveti 24 saat geçerlidir.
  • Klan üye sınırı ve davet/başvuru ömrü ortam ayarlarındadır.
  • Parti ödülünde weighted payların toplamı 10000 olmalıdır; amount pozitif olmalıdır.

Sık görülen hatalar

BelirtiNedenÇözüm
403Oyuncu oturumu ya da istemci anahtarıyla çağrı; ya da actor_player_id’nin klan içinde bu işlem için yetkisi yok (örn. üye başka üyeyi atamaz)Çağrıyı sunucu anahtarıyla yapın; actor_player_id rolünü kontrol edin
400Idempotency-Key eksik; klan adı/etiketi uzunluk ya da karakter kuralına uymuyor; XP ya da tutar pozitif değil; parti kapasitesi pozitif değilGirdiyi düzeltin
409Aynı ad ya da etiketle klan var; oyuncu zaten başka bir aktif klanda/partide; bekleyen davet ya da başvuru zaten var; aynı anahtar uçuştaÇakışan kaydı okuyup kararı oyuna bırakın
422Parti kapasitesi/pay kuralları (kapasite aşımı, weighted pay toplamı 10000 değil, yinelenen oyuncu) ya da aynı anahtar farklı gövdeGirdiyi düzeltin; yeni anahtar kullanın
404Klan, davet ya da parti yokKimlikleri GET ile doğrulayın

Çalışan örnek

TypeScript’te klan kurup davet gönderme (SDK’nın tipli klan istemcisiyle):

/** Güvenilir backend örneği: klan kurar ve bir oyuncuya davet gönderir. Server anahtarı yalnız sunucuda durur. */
import { CrxClient } from "@ferdiunal/open-play-ts";

/** Lider için klan kurar, ardından davet gönderir; her yazma kendi `Idempotency-Key`'iyle tekrar güvenlidir. */
export async function foundClanAndInvite(baseUrl: string, serverApiKey: string, leaderId: string, friendId: string): Promise<string> {
  const crx = new CrxClient({ baseUrl, apiKey: serverApiKey });
  const created = await crx.createClan({
    body: { leader_player_id: leaderId, name: "Kurt Sürüsü", tag: "KRT" },
    headers: { "Idempotency-Key": `clan-kur-${leaderId}` },
  });
  if (created.type !== "clan") throw new Error("Beklenmeyen yanıt zarfı");
  await crx.createClanInvite({
    path: { clan_id: created.clan.id },
    body: { inviter_player_id: leaderId, invitee_player_id: friendId },
    headers: { "Idempotency-Key": `davet-${created.clan.id}-${friendId}` },
  });
  return created.clan.id;
}

İlgili sayfalar

Sohbet

Bu sayfa oyuncuların birbirine yazdığı kanal sohbetini (global, dünya, klan, parti ve birebir) anlatır. Oyuncu oturumuyla mesaj göndermek, geçmişi okumak ve canlı mesaj almak istiyorsanız okuyun.

Ne işe yarar

Sohbet modülü (chat) kanal tabanlı metin sohbeti sağlar. Mesajlar kalıcı arşive yazılır; canlı dağıtım ayrıca WebSocket ile yapılır. Aynı mesaj hem REST hem WebSocket üzerinden gönderilebilir ve ikisi de aynı kuralları (susturma, hız sınırı, küfür süzgeci, tekrar koruması) uygular.

Oyuncunun kendi oturumuyla çalışır. API anahtarıyla sohbet uçları çağrılamaz.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"chat"} döner; sohbet soketi ise önce bir error çerçevesi (code: "module_disabled") gönderip kapanır. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Modülü açmak ya da kapatmakOyun ayarları › Modüller
Şehir (sahne) eklentisinin sohbetini okumak, mesajı silmek, oyuncuyu susturmakŞehir sohbeti

Not

Panelin “Şehir sohbeti” ekranı sahne eklentisinin sohbetini yönetir; bu sayfadaki kanal sohbeti için panelde mesaj okuma, silme ya da susturma ekranı henüz yoktur.

Veri modeli (düz dille)

  • Kanal (channel): kind ile ayrılır:

    kindreference_idKim girebilir
    globalyokOrtamdaki herkes
    worldgerekli (dünya/harita UUID’si)Referansı bilen oyuncular
    clangerekli (klan UUID’si)Klanın üyeleri
    partygerekli (parti UUID’si)Partinin üyeleri
    directyok; direct_player_id gerekliYalnız iki oyuncu

    Aynı türde ve aynı referansta kanal bir kez kurulur; yeniden istemek var olan kanalı döndürür. direct kanalda diğer oyuncu kendiniz olamaz.

  • Mesaj: id, channel_id, sender_player_id, client_message_id, body, sent_at_ms.

  • client_message_id: İstemcinin ürettiği tekrar anahtarı (boş olamaz, en çok 200 karakter). Aynı kanalda aynı değerle ikinci gönderim yeni mesaj üretmez; var olan mesajı duplicate: true ile geri döner.

  • Susturma: Oyuncunun all, global, world, clan, party ya da direct kapsamında susturulması. Susturulmuş oyuncunun gönderimi 403 döner.

Oyunun yaptığı çağrılar

Bütün sohbet uçları oyuncu oturumuyla çağrılır: oturum çerezi ya da opak oturum belirteci (Authorization: Bearer <oturum belirteci>) ve ortam başlığı:

x-crx-env: <ortam uuid>

Bu uçlarda x-csrf-token çift gönderimi ve Origin denetimi uygulanmaz; oturum çerezi ya da oturum belirteci yeterlidir (mağaza satın alma ucundan farklı olarak). Oturum kuralları: Oturumlar ve CSRF.

Yöntem ve yolNe yapar
POST /v1/chat/channelsKanal kurar ya da var olanı döndürür
GET /v1/chat/channelsOyuncunun yetkili olduğu kanalları listeler
POST /v1/chat/channels/{channel_id}/messagesMesaj gönderir
GET /v1/chat/channels/{channel_id}/messagesArşivi geriye doğru okur (before_ms, limit 1–100, varsayılan 50)
GET /v1/chat/wsCanlı sohbet WebSocket’i

Not

TypeScript ve Unity SDK’larında sohbet için hazır istemci yoktur; uçlar ham REST ve WebSocket ile çağrılır. Bkz. TypeScript SDK.

Kanal kurmak

POST /v1/chat/channels
x-crx-env: <ortam uuid>
Content-Type: application/json

{ "kind": "clan", "reference_id": "0192f0aa-…" }
{ "channel": { "id": "0192f2bb-…", "environment_id": "…", "kind": "clan", "reference_id": "0192f0aa-…", "participant_a": null, "participant_b": null, "created_at_ms": 1790000000000 } }

Klan ya da parti üyesi olmayan oyuncunun o kanalı kurması ya da okuması reddedilir.

Mesaj göndermek

POST /v1/chat/channels/0192f2bb-…/messages
x-crx-env: <ortam uuid>
Content-Type: application/json

{ "client_message_id": "c-0001", "body": "Merhaba!" }
{ "message": { "id": "…", "channel_id": "0192f2bb-…", "sender_player_id": "…", "client_message_id": "c-0001", "body": "Merhaba!", "sent_at_ms": 1790000000123 }, "duplicate": false }

Kurallar, sırasıyla:

  1. client_message_id boş ya da 200 karakterden uzunsa 400.
  2. Gövde 1–2000 Unicode karakter olmalı ve en az bir boşluk dışı karakter içermelidir; aksi 400.
  3. Oyuncu o kanal için susturulmuşsa 403.
  4. Hız sınırı: oyuncu başına 10 saniyede en çok 20 mesaj; aşımı 429 döner.
  5. Küfür süzgeci: mesajda yasaklı bir sözcük (boşlukla ayrılmış tam sözcük, büyük/küçük harf duyarsız) varsa 422 döner.
  6. Mesaj arşive yazılır, ardından canlı abonelere dağıtılır. Dağıtım başarısız olsa bile arşivlenmiş mesaj silinmez.

Arşivi okumak

GET /v1/chat/channels/{channel_id}/messages?before_ms=1790000000000&limit=50 yanıtı { "messages": [ … ] } döner. before_ms, bu Unix milisaniyesinden eski mesajları ister; bir sonraki sayfa için en eski mesajın sent_at_ms değerini verin.

Canlı sohbet (WebSocket)

GET /v1/chat/ws adresine bağlanırken x-crx-env ve oturum kimliği (çerez ya da Authorization: Bearer) başlık olarak gönderilir. Kimlik doğrulanmadan yükseltme yapılmaz (401); modül kapalıysa 404.

Uyarı

Tarayıcıdaki WebSocket API’si özel başlık (x-crx-env) gönderemez; bu nedenle chat soketine tarayıcıdan doğrudan bağlanılamaz. Tarayıcı oyunları REST ile yoklayabilir; native istemciler (örneğin Unity masaüstü/mobil) başlık ekleyerek bağlanabilir. Başlık yerine sorgu parametresi ya da alt protokol henüz desteklenmez.

İstemci çerçeveleri (JSON; type alanı küçük camelCase, diğer alanlar snake_case):

typeAlanlarAnlamı
subscribechannel_idKanalın canlı akışına abone ol
unsubscribe—Aboneliği bırak
sendchannel_id, client_message_id, bodyREST’teki gibi mesaj gönder
pingnonceCanlılık yoklaması

Sunucu çerçeveleri:

typeAlanlar
subscribedchannel_id
unsubscribed—
messagemessage, duplicate
pongnonce
errorcode (invalid_frame, invalid_payload, forbidden_channel, unavailable, message_rejected, module_disabled)

Bir soket aynı anda tek kanala abonedir. Tanınmayan alan içeren çerçeve invalid_frame alır. Modül sonradan kapanırsa açık soket de kapatılır. Genel gerçek zamanlı yüzey için bkz. Gerçek zamanlı: genel bakış.

Sürümleme ve tekrar güvenliği

  • Mesaj gönderiminde Idempotency-Key başlığı yoktur; tekrar güvenliği client_message_id ile sağlanır. Ağ hatasında aynı değerle yeniden gönderin: mesaj bir kez görünür (duplicate: true).
  • If-Match kullanılmaz.

Olaylar

Sohbet olayı webhook olarak gönderilmez; webhook olay türleri arasında sohbet yoktur. Canlı akış için WebSocket kullanın.

Sınırlar

  • Mesaj gövdesi 1–2000 karakter; client_message_id en çok 200 karakter.
  • Gönderim hızı: oyuncu başına 10 saniyede 20 mesaj.
  • Arşiv sayfası 1–100 mesaj.
  • Bir WebSocket aynı anda tek kanala abone olabilir.

Sık görülen hatalar

BelirtiNedenÇözüm
401Oturum yok, süresi dolmuş ya da x-crx-env eksikOturumu yenileyin; başlığı ekleyin
403 (gönderim)Oyuncu susturulmuş ya da kanalda yetkisi yokKullanıcıya bilgi verin
400Boş ya da çok uzun gövde / client_message_idGirdiyi düzeltin
422Küfür süzgeci mesajı reddettiMesajı değiştirin
42910 saniyede 20 mesaj aşıldıBekleyip yeniden deneyin
error: forbidden_channel (WS)Kanal için yetki yokKanalı önce REST ile kurun / yetkiyi doğrulayın

Çalışan örnek

Oturum çerezi (cookies.txt) ile REST üzerinden bir kanala yazıp geçmişi okuma:

curl -X POST "$API/v1/chat/channels/$CHANNEL/messages" \
  -H "x-crx-env: $ENV" \
  -H "Content-Type: application/json" -b cookies.txt \
  -d '{"client_message_id":"c-0001","body":"Merhaba!"}'

curl "$API/v1/chat/channels/$CHANNEL/messages?limit=20" \
  -H "x-crx-env: $ENV" -b cookies.txt

İlgili sayfalar

Skor tabloları ve turnuvalar

Bu sayfa oyuncuların puanla yarıştığı skor tablolarını (leaderboard) ve eleme usulü turnuvaları anlatır. Puanı güvenilir sunucudan yazıp oyun içinde sıralamayı göstermek istiyorsanız okuyun.

Ne işe yarar

Rekabet modülü (competition) iki şey sağlar:

  • Skor tablosu: Oyuncu başına en iyi puanı tutan, sezonlara bölünebilen sıralama.
  • Turnuva: Oyuncu ya da klan katılımcılarıyla tek eleme, çift eleme ya da lig usulü bracket; final ödülü ile.

Puan yazma ve tablo yönetimi sunucu anahtarıyla yapılır. Oyun istemcisi tabloyu oyuncu oturumuyla okur ve sunucu anahtarı gerekmez.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"competition"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Tablo tanımlamak, sezon, birim ve görünürlük ayarlamakLeaderboard
Bir tablonun en yüksek kayıtlarını ve sezon geçmişini görmekLeaderboard ayrıntısı
Turnuva için ödül para birimiPara birimleri

Turnuvalar için panel ekranı yoktur; yalnız REST ile yönetilir.

Veri modeli (düz dille)

  • Tablo tanımı: key, name, metric_expr, order (asc: küçük puan iyi, desc: büyük puan iyi), season, tie_policy, player_visible, unit ve unit_suffix. Tanım sürümlüdür.
  • Sezon (season): none (hiç sıfırlanmaz), daily, weekly, monthly (UTC takvimine hizalı: gün 00:00 UTC’de, hafta Pazartesi 00:00 UTC’de, ay ayın birinde döner) ya da custom (custom_period_ms uzunluğunda dönem). Sezon kapanınca sıralaması değişmez bir kayda alınır.
  • Puan: Tam sayı (i64). Bir oyuncunun sezondaki en iyi puanı sayılır (desc’te en yüksek, asc’te en düşük). Daha kötü bir puan göndermek sıralamayı değiştirmez.
  • Eşitlik (tie_policy): Şimdilik tek seçenek score_achieved_player_id: önce puan, sonra o puana ilk ulaşan, sonra oyuncu kimliği.
  • Sıra (rank): 1’den başlar.
  • Birim (unit): points, money (en küçük para birimi) ya da duration_ms; unit_suffix 1–8 karakterlik ektir ($, ms …). Birim yalnız gösterim içindir.
  • player_visible: true ise oyuncu oturumu tabloyu okuyabilir; varsayılan false.
  • metric_expr: Kayıt anında derlenen CEL ifadesi (zorunlu, boş olamaz). Eklentilerin kendiliğinden kurduğu tablolar event.score kullanır; REST ile kurarken de aynı ifadeyi yazın.

Oyunun yaptığı çağrılar

Yöntem ve yolKimlikNe yapar
POST /v1/leaderboardssunucu anahtarıTablo tanımı oluşturur
GET /v1/leaderboards, GET /v1/leaderboards/{key}sunucu anahtarıTanımları listeler / okur
POST /v1/leaderboards/{key}/scoressunucu anahtarıOyuncu puanı yazar (Idempotency-Key zorunlu, 202)
GET /v1/leaderboards/{key}/entries?limit=&cursor=sunucu anahtarıİlk sıralar (sayfalı)
GET /v1/leaderboards/{key}/around-me?player_id=&radius=sunucu anahtarıOyuncunun çevresindeki sıralar
GET /v1/player/leaderboards/{key}?limit=oyuncu oturumuTablonun ilk sayfası ve oyuncunun kendi satırı
POST /v1/tournamentssunucu anahtarıTurnuva oluşturur
GET /v1/tournaments/{id}sunucu anahtarıTurnuva durumunu okur
POST /v1/tournaments/{id}/participantssunucu anahtarıKatılımcı kaydeder (Idempotency-Key)
POST /v1/tournaments/{id}/startsunucu anahtarıBracket üretip başlatır
POST /v1/tournaments/{id}/matches/{match_id}/resultsunucu anahtarıMaç sonucu bildirir ya da itirazı çözer (Idempotency-Key)

Sunucu uçları Authorization: Bearer crx_live_<önek>_<gizli> ister; istemci anahtarı ve oyuncu oturumu reddedilir. Oyuncu ucu çerez ya da oturum belirteci ve x-crx-env: <ortam uuid> ile çalışır; geliştirici anahtarı kabul edilmez.

Not

TypeScript SDK’sında oyuncu okuması için player.leaderboard(key, { limit }) vardır; sunucu uçları (puan yazma, tanım, turnuva) için tipli istemci yoktur ve ham REST ile çağrılır. Bkz. TypeScript SDK.

Tablo tanımı

POST /v1/leaderboards
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "key": "daily-earnings", "name": "Günlük kazanç",
  "metric_expr": "event.score", "order": "desc",
  "season": "daily", "tie_policy": "score_achieved_player_id",
  "player_visible": true, "unit": "money", "unit_suffix": "$"
}

201 döner ve { id, key, name, metric_expr, order, season, custom_period_ms, tie_policy, season_key } gelir. season: "custom" için custom_period_ms pozitif olmalıdır.

Puan yazmak

POST /v1/leaderboards/daily-earnings/scores
Authorization: Bearer crx_live_<önek>_<gizli>
Idempotency-Key: score-oyuncu-1234-2026-11-03
Content-Type: application/json

{ "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "score": 4200 }
{ "entry": { "rank": 7, "player_id": "0192f0f2-…", "score": 4200, "achieved_at_ms": 1790000000000 }, "replayed": false }

Yanıt 202’dir: puan kalıcı olarak kabul edilmiştir; sıralama projeksiyonu kısa bir gecikmeyle güncellenir, bu yüzden hemen ardından okunan tablo birkaç saniye geride kalabilir. Idempotency-Key zorunludur: aynı anahtar ve aynı gövde aynı sonucu replayed: true ile döndürür.

Tabloyu oyuncu oturumuyla okumak

GET /v1/player/leaderboards/daily-earnings?limit=10
x-crx-env: 0192f000-0000-7000-8000-00000000e001
{
  "leaderboard": { "key": "daily-earnings", "name": "Günlük kazanç", "order": "desc", "season": "daily", "seasonKey": "2026-11-03", "seasonEndsAtMs": 1790121600000, "unit": "money", "unitSuffix": "$" },
  "entries": [ { "rank": "1", "playerId": "…", "displayName": "Ayşe", "score": "9800", "achievedAtMs": 1790000000000 } ],
  "me": { "rank": "7", "playerId": "…", "displayName": "Siz", "score": "4200", "achievedAtMs": 1790000000000 }
}
  • Yalnız player_visible tablolar okunur; aksi 404. Modül kapalıysa da 404.
  • limit 1–50 (varsayılan 20).
  • rank ve score JSON’da ondalık metindir (kayıpsız i64); SDK bunları bigint yapar. Alan adları bu uçta camelCase’tir.
  • Yasaklı ve silinmiş oyuncular süzülür ve sıra sayfa içinde yoğun numaralanır; me, görünür oyuncular arasındaki sıranızdır. Bu sezonda puanı yoksa me null’dır.
  • Birinci sayfa kısa süre (en çok birkaç saniye) önbelleğe alınır.

Turnuvalar

POST /v1/tournaments
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "name": "Kış Kupası", "format": "single_elimination", "participant_type": "player",
  "capacity": 16,
  "registration_opens_at_ms": 1790000000000, "registration_closes_at_ms": 1790086400000,
  "seeding_source": "registration_order",
  "prize_table": { "currency_key": "gold", "amount": 5000 }
}
  • format: single_elimination, double_elimination, round_robin. participant_type: player ya da clan.
  • capacity 2–1024; kayıt kapanışı açılıştan sonra olmalı; name 1–200 karakter.
  • seeding_source: registration_order (kayıt sırası; leaderboard_id verilmez) ya da leaderboard (yalnız oyuncu turnuvasında; leaderboard_id zorunlu).
  • prize_table: currency_key (boşluksuz, en çok 200 karakter) ve pozitif amount içeren nesne.
  • Akış: oluştur → participants ile kaydet ({ "participant_id" }) → start ile bracket’i üret → her maç için result bildir (reporter_id, reported_side: home away admin, outcome: home_win away_win draw, score). admin tarafı itirazı çözmek içindir.
  • Yanıtlar turnuvanın güncel görünümünü ({ "tournament": { … } }) taşır.

Sürümleme ve tekrar güvenliği

  • Puan yazma, katılımcı kaydı ve maç sonucu Idempotency-Key ister (aynı anahtar + aynı gövde: tekrar çalışmaz; farklı gövde: 422).
  • Tablo tanımı sürümlüdür; liste ve sıralama okumaları sürüm başlığı istemez.
  • Genel kurallar: Idempotency ve eşzamanlılık.

Olaylar

Puan kabulü score_submitted, sezon geçişi season_closed/season_opened olaylarını üretir; bunlar görev hedeflerinde ve kural otomasyonunda kullanılabilir. Webhook filtresi olarak seçilebilen türler arasında skor tablosu ya da turnuva olayı yoktur; filtresiz kayıtlı uç noktalar bütün olayları alır ve bunlara skor olayları da dahildir, ancak zarf biçimi belgelenmiş bir sözleşme değildir. Final ödülü için Ödüller sayfasındaki ödül verme akışını kullanın.

Sınırlar

  • Oyuncu okumasında limit 1–50; sunucu entries uçlarında sayfalama imleçlidir.
  • Turnuva capacity 2–1024.
  • season: custom için pozitif custom_period_ms gerekir.

Sık görülen hatalar

BelirtiNedenÇözüm
404 (oyuncu okuması)Tablo player_visible değil, yok ya da modül kapalıTanımı kontrol edin
403İstemci anahtarı ya da oyuncu oturumuyla sunucu ucuSunucu anahtarını sunucudan kullanın
400 (Idempotency-Key)Puan yazarken başlık yokAnahtar gönderin
Puan yazıldı ama sıralama değişmediOyuncunun sezondaki en iyi puanı daha iyiYalnız daha iyi puan sıralamayı değiştirir
Puan yazıldı ama tablo hemen güncellenmediProjeksiyon gecikmesiBirkaç saniye bekleyip okuyun
400 (turnuva)capacity, kayıt penceresi, prize_table ya da seed kuralı hatalıGövdeyi düzeltin

Çalışan örnek: puan yaz, tabloyu oku

/** Skor tablosu: sunucu puan yazar (ham REST), oyun istemcisi tabloyu oyuncu oturumuyla okur. */
import type { PlayerClient } from "@ferdiunal/open-play-ts";

/** Sunucu: `POST /v1/leaderboards/{key}/scores`. Sunucu anahtarı gerekir; Idempotency-Key zorunludur. */
export async function submitScore(baseUrl: string, serverApiKey: string, board: string, playerId: string, score: number, requestKey: string): Promise<number> {
  const response = await fetch(`${baseUrl}/v1/leaderboards/${encodeURIComponent(board)}/scores`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${serverApiKey}`,
      "Content-Type": "application/json",
      "Idempotency-Key": requestKey,
    },
    body: JSON.stringify({ player_id: playerId, score }),
  });
  if (response.status !== 202) throw new Error(`Puan yazılamadı: HTTP ${response.status} ${await response.text()}`);
  const body = (await response.json()) as { entry: { rank: number }; replayed: boolean };
  return body.entry.rank;
}

/** Oyun istemcisi: tablonun ilk sayfası ve oyuncunun kendi satırı (`player_visible` tablolar). */
export async function showBoard(player: PlayerClient, board: string): Promise<void> {
  const view = await player.leaderboard(board, { limit: 10 });
  for (const row of view.entries) console.log(`${row.rank}. ${row.displayName} — ${row.score}`);
  if (view.me !== null) console.log(`Siz: ${view.me.rank}. sıra, ${view.me.score} puan`);
}

İlgili sayfalar

Bildirimler

Bu sayfa oyuncuya oyun içi (gelen kutusu) ve push bildirimi göndermenin yolunu anlatır: panelde sağlayıcı, şablon ve kural kurulumu; oyunun cihaz kaydı, gelen kutusu okuması ve sunucudan doğrudan gönderim. Bildirim altyapısını kendiniz yazmadan kullanmak istiyorsanız okuyun.

Ne işe yarar

Bildirim modülü (notifications) bir bildirimi önce kuyruğa yazar, sonra arka plandaki işçi sağlayıcıya (FCM, APNs ya da OneSignal) gönderir. API’de senkron push gönderimi yoktur: isteğiniz kuyruğa yazıldığında 200 alırsınız; teslimat sonradan olur.

Bildirimin metni şablondan gelir. Şablon panelde yazılır; oyun yalnız şablon anahtarını ve doldurma verisini gönderir. Bildirimi iki yolla üretebilirsiniz:

  • Sunucudan gönderim: Oyun sunucunuz POST /v1/notifications çağırır.
  • Kural: Panelde kurduğunuz kural, bir oyun olayı olunca (ör. seviye atlama, görev tamamlama, mağaza satın alma) otomatik bildirim üretir.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"notifications"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Push sağlayıcısı (FCM, APNs, OneSignal) kimlik bilgilerini girmekBildirimler › Sağlayıcılar
Şablon yazmak (dil başına başlık ve gövde, yer tutucular)Bildirimler › Şablonlar
Olay tetikli kural kurmak, kuralı canlı önizlemekBildirimler › Kurallar
Oyuncuya test bildirimi göndermek, gönderim geçmişini ve cihazları görmekBildirimler › Geçmiş, Cihazlar

Sağlayıcı kimlik bilgileri panelde yalnız yetkili rolle girilir ve sunucuda mühürlü saklanır; panel yalnız parmak izini gösterir.

Veri modeli (düz dille)

  • Sağlayıcı (provider): fcm, apns, one_signal. Her ortam için ayrı açılıp kapatılır.
  • Cihaz (device): Oyuncunun bir sağlayıcıdaki kayıtlı belirteci (token) ve platformu (ios, android, web). Aynı (ortam, sağlayıcı, belirteç) tekrar kaydedilebilir; yanıt hangi dalın işlediğini söyler (created, revived, refreshed).
  • Şablon (template): Anahtar, dil başına başlık ve gövde, {{yer_tutucu}} alanları. İstenen dil yoksa İngilizce (en) kullanılır. Her bildirim, gönderildiği andaki şablon sürümüne bağlanır.
  • Kanal: in_game (yalnız gelen kutusu), push (yalnız push), both.
  • Bildirim (inbox satırı): id, template_key, title, body, data, channel, status, created_at, sent_at, read_at. Durum: queued, sent, delivered, failed, read.
  • Tekrar anahtarı (dedup_key): Aynı (ortam, oyuncu, anahtar) ikinci kez yazılmaz.

Oyunun yaptığı çağrılar

İki kimlik türü vardır: oyuncu oturumu (çerez ya da oturum belirteci + x-crx-env: <ortam uuid>) ve sunucu anahtarı (Authorization: Bearer crx_live_<önek>_<gizli>).

Yöntem ve yolKimlikNe yapar
POST /v1/notificationssunucu anahtarıBir oyuncuya bildirim kuyruğa yazar
GET /v1/notificationsoyuncu oturumuGelen kutusu (status, cursor, limit)
POST /v1/notifications/{id}/readoyuncu oturumuOkundu işaretler
POST /v1/devicesoyuncu oturumuPush cihazını kaydeder
DELETE /v1/devices/{token}?provider=oyuncu oturumuCihaz kaydını iptal eder (204)
GET /v1/push/configkimliksiz (x-crx-env zorunlu)Web push için istemciye açık ayarlar
GET /v1/wsoyuncu oturumuCanlı bildirim itme soketi (yalnız sunucudan oyuncuya; ortam x-crx-env başlığıyla verilir, bu yüzden tarayıcı WebSocket API’siyle bağlanılamaz; çerçeveler en çok bir kez gelir, kalıcı yüzey gelen kutusudur)

Oyuncu uçlarında x-csrf-token ve Origin denetimi uygulanmaz; oturum çerezi ya da oturum belirteci yeterlidir.

TypeScript SDK’sında oyuncu tarafı hazırdır: player.notifications altında getPushConfig, registerDevice, unregisterDevice, enableWebPush, disableWebPush, inbox ve markRead bulunur. Sunucudan gönderim için tipli istemci yoktur; ham REST kullanılır. Bkz. TypeScript SDK.

Sunucudan bildirim göndermek

POST /v1/notifications
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
  "template_key": "daily_reward_ready",
  "locale": "tr",
  "data": { "amount": 250 },
  "channel": "both",
  "dedup_key": "daily-2026-11-03"
}
{ "id": "0192f400-…", "duplicate": false }
  • locale: kısa dil kodu (tr, en …). İstenen dil şablonda yoksa en kullanılır.
  • dedup_key (isteğe bağlı, 1–256 karakter): aynı anahtar ikinci kez gelirse yeni satır yazılmaz; 200 ve duplicate: true ile ilk bildirimin kimliği döner.
  • 403: istemci anahtarı ya da oyuncu oturumu. 404: şablon hiçbir dilde yok. 400: template_key boş ya da 128 karakterden uzun, locale biçimi geçersiz ya da şablondaki bir yer tutucu data içinde eksik.

Gelen kutusunu okumak

GET /v1/notifications?status=queued&limit=20
x-crx-env: 0192f000-0000-7000-8000-00000000e001
{
  "items": [
    { "id": "0192f400-…", "template_key": "daily_reward_ready", "template_version": 2, "title": "Günlük ödülün hazır", "body": "250 altın seni bekliyor.", "data": { "amount": 250 }, "channel": "both", "status": "sent", "created_at": "2026-11-03T09:00:00Z", "sent_at": "2026-11-03T09:00:02Z", "read_at": null }
  ],
  "next_cursor": null
}

Liste yeniden eskiye sıralıdır (created_at, id). limit 1–100; next_cursor null ise son sayfadır. Teslimi başarısız olan satırlar da listelenir. POST /v1/notifications/{id}/read satırı read yapar; ikinci çağrı aynı satırı döndürür ve yeni olay üretmez.

Push cihazı kaydetmek

POST /v1/devices
x-crx-env: 0192f000-0000-7000-8000-00000000e001
Content-Type: application/json

{ "provider": "fcm", "platform": "android", "token": "<cihaz belirteci>", "device_meta": { "model": "Pixel 8" } }
{ "id": "…", "registration": "created", "provider": "fcm", "platform": "android", "created_at": "2026-11-03T09:00:00Z" }

provider apns, fcm ya da one_signal; platform ios, android ya da web. Belirteç boş olamaz. Oyuncu kimliği gövdeden değil oturumdan gelir. app_key çok uygulamalı ortamlarda uygulamayı ayırır.

Not

Üretilmiş OpenAPI belgesi cihaz sağlayıcısını onesignal olarak yazar; sunucu ve SDK one_signal kullanır. Bilinen bir belge sapmasıdır; one_signal gönderin.

Web push için player.notifications.enableWebPush tarayıcı izin akışını, GET /v1/push/config ile aldığı ayarlarla yürütür; çıkışta session.logout() bu sayfadan kaydedilmiş web cihazını kendiliğinden iptal eder.

Kurallar: olay olunca otomatik bildirim

Panelde bir kural; tetikleyici (kapalı bir listeden bir oyun olayı; örneğin level_up, quest_completed, store_purchase_completed, vip_tier_up), isteğe bağlı koşul, şablon, dil, kanal ve alıcı oyuncu yolundan oluşur. Kural sürümleri panelin geçmiş sekmesinde görünür. Bildirimin kendisi yeni bir kural tetiklemez. Kural editörü bir “canlı önizleme” ve test gönderimi sunar. Ayrıntılı alanlar için panel sayfasına bakın.

Sürümleme ve tekrar güvenliği

  • Gönderim: dedup_key ile tekrar güvenliği; Idempotency-Key başlığı yoktur.
  • Cihaz kaydı ve okundu işareti doğal olarak idempotenttir.
  • Şablon sürümü bildirime bağlanır; şablonu sonradan değiştirmek eski bildirimi etkilemez.

Olaylar

Bildirim kuyruğa yazıldığında bir notification_queued olayı üretilir ve gönderim işçisi onu tüketir. Webhook filtresi olarak seçilebilen türler arasında bildirim yoktur; filtresiz kayıtlı uç noktalar bu iç olayı da alır, ancak zarf biçimi belgelenmiş bir sözleşme değildir.

Sınırlar

  • dedup_key 1–256 karakter; gelen kutusu limit 1–100.
  • Push teslimatı en-iyi-çabadır: sağlayıcı reddederse satır failed olur. Cihaz kalıcı geçersizse (örn. silinmiş uygulama) cihaz otomatik iptal edilir.
  • Sağlayıcı kimlik bilgileri tanımlı değilse bildirim kuyrukta bekler ve panelde sağlayıcı durumu uyarı gösterir.

Sık görülen hatalar

BelirtiNedenÇözüm
403 (gönderim)Sunucu anahtarı yerine istemci anahtarıSunucu anahtarını kullanın
404 (gönderim)template_key hiçbir dilde yokŞablonu panelde oluşturup yayınlayın
duplicate: trueAynı dedup_key zaten yazıldıBeklenen davranış; yeni bildirim için yeni anahtar
Bildirim queued kalıyorSağlayıcı kapalı ya da kimlik bilgisi hatalıPanelde sağlayıcı durumunu kontrol edin
400 (cihaz)Boş belirteç ya da bilinmeyen sağlayıcı/platformGirdiyi düzeltin

Çalışan örnek

Oyuncu tarafında gelen kutusunu boşaltma:

/** Oyuncu tarafı: gelen kutusunu okur ve okunmamış bildirimleri okundu işaretler. */
import type { PlayerClient } from "@ferdiunal/open-play-ts";

/** İlk sayfadaki okunmamış bildirimleri yazdırır ve okundu yapar. */
export async function drainInbox(player: PlayerClient): Promise<number> {
  const page = await player.notifications.inbox({ limit: 20 });
  let marked = 0;
  for (const item of page.items) {
    if (item.readAt !== null) continue;
    console.log(item.title, item.body, item.data);
    await player.notifications.markRead(item.id);
    marked += 1;
  }
  return marked;
}

Sunucudan gönderim için curl örneği yukarıdadır.

İlgili sayfalar

Webhook’lar

Bu sayfa Open Play’in oyun olaylarını kendi sunucunuza HTTP POST ile nasıl ilettiğini anlatır: uç nokta kaydı, olay türleri, imza doğrulaması, yeniden deneme ve tekrar eden teslimatla başa çıkma. Karakter, mağaza ya da ödül olayında kendi arka uç sisteminizi tetiklemek istiyorsanız okuyun.

Ne işe yarar

Open Play bir olay oluştuğunda (ör. level_up, store_purchase_completed, reward_granted) kayıtlı uç noktalarınıza imzalı bir POST gönderir. Teslimat en az bir kez (at-least-once) yapılır: aynı olay ağ hataları yüzünden birden çok gelebilir, bu yüzden alıcınız tekrarı X-Crx-Event-Id ile elemelidir.

Modül kapalıysa uçlar 404 ve {"code":"module_disabled","module":"webhooks"} döner. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
Kayıtlı uç noktaları, teslimat denemelerini, başarısız (DLQ) teslimatları ve olay kuyruğunu görmekWebhook ve olay kuyruğu
Modülü açmakOyun ayarları › Modüller

Panel bu sayfada salt okunurdur: uç nokta kaydı REST ile yapılır; panelde yeniden gönderme ya da başarısız teslimatı kurtarma düğmesi yoktur. Panel sırları, ham gövdeyi ve ham hata metnini göstermez.

Veri modeli (düz dille)

  • Uç nokta (endpoint): url, isteğe bağlı event_types süzgeci, enabled ve created_at.
  • Sır (secret): Sunucu üretir (whsec_ + 64 küçük harf hex), yalnız kayıt yanıtında bir kez döner ve şifreli saklanır. Sonra hiçbir uç onu vermez. Sır değiştirme ucu yoktur: yenilemek için uç noktayı silip yeniden kaydedin.
  • Olay zarfı: Gönderilen JSON gövdesi. Her zarfta event_id, type, environment_id ve data bulunur.
  • Teslimat (delivery): Bir olayın bir uç noktaya gönderimi. Durumları bekleyen, teslim edilen ve başarısız (DLQ).

Seçilebilir olay türleri

event_types boşsa uç nokta bütün olayları alır (aşağıdaki not). Doluysa yalnız şu türlerden seçebilirsiniz:

AlanTürler
Karakterflag_set, attribute_set, resource_changed, modifier_added, modifiers_removed, xp_added, level_up, ability_unlocked, event_emitted, character_rolled_back
Mağazastore_page_created, store_page_updated, store_page_deleted, store_pages_reordered, store_listing_created, store_listing_updated, store_listing_deleted, store_listings_reordered, store_purchase_initiated, store_payment_started, store_purchase_completed, store_reservation_cancelled
Ödülreward_granted

Bilinmeyen bir tür 400 döner. Tam liste ve veri alanları: Webhook olayları.

Uyarı

Süzgeci boş bırakırsanız uç nokta yukarıdaki listeyle sınırlı kalmaz: outbox’a yazılan bütün olayları (eşya, cüzdan, klan, parti, skor, bildirim kuyruğu gibi) alır. Bu ek olayların zarf biçimi belgelenmiş bir sözleşme değildir ve haber verilmeden değişebilir. Üretimde event_types süzgecini her zaman açıkça verin.

Zarf biçimi

Karakter olayları:

{
  "event_id": "0192f500-1111-7222-8333-944455556666",
  "type": "level_up",
  "character_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f",
  "environment_id": "0192f000-0000-7000-8000-00000000e001",
  "seq": 12,
  "data": { "...": "olaya özgü alanlar" }
}

Mağaza olaylarında character_id yoktur; seq ile birlikte now_ms (olayın işlendiği an) ve actor_api_key_id bulunur. reward_granted olayında player_id ve data içinde source_kind, table_key, table_version ile verilenler yer alır; çekiliş tohumu hiçbir olayda yoktur.

Oyunun yaptığı çağrılar

Bütün yönetim uçları sunucu anahtarıyla çağrılır; istemci anahtarı 403 alır:

Authorization: Bearer crx_live_<önek>_<gizli>
Yöntem ve yolNe yapar
POST /v1/webhooksUç nokta kaydeder (201; sır yalnız burada)
GET /v1/webhooksUç noktaları listeler (cursor, limit 1–200, varsayılan 50)
GET /v1/webhooks/{id}Tek uç noktayı okur (sırsız)
DELETE /v1/webhooks/{id}Uç noktayı siler (yumuşak silme, 204)

TypeScript SDK’sı bu dört ucu tipli sunar: createWebhook, listWebhooks, getWebhook, deleteWebhook (bkz. TypeScript SDK).

Uç nokta kaydetmek

POST /v1/webhooks
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "url": "https://hooks.ornek-oyun.com/open-play",
  "event_types": ["level_up", "store_purchase_completed"]
}
{
  "id": "0192f600-…",
  "url": "https://hooks.ornek-oyun.com/open-play",
  "event_types": ["level_up", "store_purchase_completed"],
  "enabled": true,
  "created_at": "2026-11-03T09:00:00Z",
  "secret": "whsec_<64 hex karakter>"
}

secret değerini hemen güvenli bir yere (gizli yönetim servisine) yazın; bir daha okunamaz.

Kayıt kuralları:

  • URL https olmalı; kullanıcı bilgisi (user:parola@) taşımamalı; harf dışı kontrol karakteri ve boşluk içermemeli. Loopback, özel ve link-local IP adresleri kayıtta reddedilir (400).
  • Ayrıca her gönderimde adın çözümlendiği IP yeniden denetlenir; özel ya da loopback adrese çözümlenen ad gönderilemez. Yönlendirme (redirect) takip edilmez.
  • Aynı URL aktifken ikinci kayıt 409.

Teslimat

Her olay için uç noktanıza şu istek gelir:

POST /open-play
Content-Type: application/json
X-Crx-Signature: t=1790000000,v1=9c1f…e2
X-Crx-Event-Id: 0192f500-1111-7222-8333-944455556666
X-Crx-Event-Type: level_up

{ …zarf… }
BaşlıkAnlamı
X-Crx-Signaturet=<unix saniye>,v1=<hex HMAC-SHA256>
X-Crx-Event-IdOlay kimliği; tekrar eleme anahtarınız
X-Crx-Event-TypeZarftaki type değeri

İmzayı doğrulamak

İmza, "{t}.{ham gövde}" metninin, kayıtta dönen sırrın tamamı (whsec_… önekiyle) anahtar alınarak hesaplanan HMAC-SHA256 özetidir (küçük harf hex). Doğrulama adımları:

  1. X-Crx-Signature başlığından t ve v1 değerlerini ayırın.
  2. Ham gövde baytlarını alın. JSON’u ayrıştırıp yeniden serileştirmeyin; imza tel üzerindeki birebir baytlar içindir.
  3. HMAC-SHA256(secret, t + "." + ham_gövde) hesaplayın ve v1 ile sabit zamanlı karşılaştırın.
  4. t değerini kendi saatinizle karşılaştırın; çok eski (örneğin 5 dakikadan eski) istekleri reddedin. Her yeniden denemede t yenilenir, bu yüzden gerçek bir yeniden deneme bu pencerede kalır.
  5. X-Crx-Event-Id daha önce işlendiyse işlemi tekrarlamadan 2xx dönün.

Yanıt ve yeniden deneme kuralları

  • Yalnız 2xx başarıdır. Başka her yanıt, zaman aşımı ve bağlantı hatası yeniden denemeyi tetikler.
  • Bağlantı için 5 saniye, DNS dahil toplam 10 saniye beklenir.
  • Gövde 64 KB’ı aşarsa gönderilmez ve hata sayılır.
  • Gecikme min(1 sn × 2^deneme, 5 dk)’dır ve ±%20 rastgelelik içerir. En çok 10 deneme yapılır; 10. başarısız denemeden sonra teslimat başarısız (DLQ) durumuna alınır ve otomatik tekrar edilmez.
  • Uç noktayı sildiğinizde bekleyen teslimatlar da başarısız duruma alınır.
  • Sıra garantisi yoktur: olaylar karakter için seq ile sıralanabilir, ama teslimat sırası değişebilir.
  • Uç nokta yokken oluşan olaylar sonradan gönderilmez; geriye dönük teslimat yoktur.

Sürümleme ve tekrar güvenliği

  • Alıcı tarafı: X-Crx-Event-Id ile tekrar eleme zorunludur. Aynı olay yeniden denemede aynı event_id ile gelir; imza zamanı (t) yenilenir.
  • Kayıt ucunda Idempotency-Key yoktur; aynı URL’yi ikinci kez kaydetmek 409 döner.
  • Zarfta bir olay sürümü alanı yoktur; tanımadığınız alanları yok sayın ve gövdeyi sıkı bir şemayla reddetmeyin.

Sınırlar

  • Gövde 64 KB; toplam bekleme 10 saniye; 10 deneme.
  • Liste ucunda limit 1–200.
  • Redirect takip edilmez; yalnız https.

Sık görülen hatalar

BelirtiNedenÇözüm
Kayıtta 400URL https değil, IP adresi, kullanıcı bilgisi ya da bilinmeyen olay türüURL ve event_types değerlerini düzeltin
Kayıtta 403İstemci anahtarıSunucu anahtarı kullanın
Kayıtta 409Aynı URL zaten aktifVar olanı silin ya da kullanın
İmza doğrulanmıyorGövde yeniden serileştirildi; sırrın whsec_ öneki atıldı; saat farkıHam baytlarla, sırrın tamamıyla hesaplayın
Aynı olay iki kez geliyorAt-least-once teslimatX-Crx-Event-Id ile eleyin
Teslimatlar durdu10 deneme doldu (DLQ) ya da uç nokta silindiAlıcıyı düzeltin; kaçan olaylar için kendi kaynağınızdan okuyun
Teslimat hiç gelmiyorUç adı özel/loopback IP’ye çözümleniyor ya da yanıt 10 saniyeyi aşıyorGenel bir adres kullanın; yanıtı hızlı verin

Çalışan örnek: Node ile alıcı

Aşağıdaki sunucu imzayı doğrular, tekrar eden olayı eler ve 2xx döner. Sır ortam değişkeninden gelir; üretimde kalıcı bir depoyla tekrar elemesi yapın.

/** Webhook alıcısı (Node 22): imzayı doğrular, tekrar eden teslimatı `X-Crx-Event-Id` ile eler. */
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const SECRET = process.env["OPEN_PLAY_WEBHOOK_SECRET"] ?? ""; // uç noktayı kaydederken bir kez dönen sır
const TOLERANCE_SECONDS = 300; // eski teslimatı (replay) reddetme penceresi: alıcının kendi politikası
const seen = new Set<string>(); // örnek amaçlı bellek; üretimde kalıcı depo kullanın

/** `X-Crx-Signature: t=<unix-sn>,v1=<hex>` değerini ham gövdeye karşı doğrular. */
export function verifySignature(secret: string, header: string, rawBody: Buffer, nowSeconds: number): boolean {
  const parts = new Map(header.split(",").map((part) => part.split("=", 2) as [string, string]));
  const t = parts.get("t");
  const v1 = parts.get("v1");
  if (t === undefined || v1 === undefined) return false;
  if (Math.abs(nowSeconds - Number(t)) > TOLERANCE_SECONDS) return false;
  const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest("hex");
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(v1, "utf8");
  return a.length === b.length && timingSafeEqual(a, b);
}

createServer((req, res) => {
  const chunks: Buffer[] = [];
  req.on("data", (chunk: Buffer) => chunks.push(chunk));
  req.on("end", () => {
    const rawBody = Buffer.concat(chunks); // imza ham bayt üzerindendir: JSON'u yeniden serileştirmeyin
    const signature = req.headers["x-crx-signature"];
    if (typeof signature !== "string" || !verifySignature(SECRET, signature, rawBody, Math.floor(Date.now() / 1000))) {
      res.writeHead(401).end();
      return;
    }
    const eventId = String(req.headers["x-crx-event-id"] ?? "");
    if (!seen.has(eventId)) {
      seen.add(eventId);
      const event = JSON.parse(rawBody.toString("utf8")) as { type: string; data: unknown };
      console.log("olay", event.type, event.data);
    }
    res.writeHead(200).end(); // yalnız 2xx başarıdır; başka kod yeniden denemeyi tetikler
  });
}).listen(8080);

İlgili sayfalar

Scene, görsel ve 3B modeller

Bu sayfa oyunun dünyasını oluşturan üç parçayı anlatır: gerçek zamanlı scene (sahne/oda) kaydı, panelden ve sunucudan yüklenen görseller ve operatörün yüklediği 3B modeller. Şehir odalarını sunucudan açmak, ikon ve afiş görselini yönetmek ya da modelleri oyuna sunmak istiyorsanız okuyun.

Ne işe yarar

  • Scene: Gerçek zamanlı eklentinin oyuncuları topladığı oda. Oyun sunucusu açılışta ihtiyaç duyduğu scene’leri kaydeder; kayıt idempotenttir.
  • Görsel (asset): PNG, JPEG ya da WebP. İçerik adreslidir: adres dosyanın sha256 özetidir, aynı dosya bir kez saklanır. Mağaza afişi, para birimi ikonu ve içerik grubu image alanları bu depoyu kullanır.
  • 3B model: Operatörün panelde yüklediği glb (ya da dönüştürülen kaynak dosya). Yayınlanınca eşyaya ya da karaktere bağlanır ve oyun onu katalogdan alır.

Scene ve görsel yazma uçları scenes modülüne bağlıdır; modül kapalıysa 404 ve {"code":"module_disabled","module":"scenes"} döner. Görsel ve model okuma uçları çekirdektir ve modül kapalıyken de çalışır. Modülü Oyun ayarları › Modüller sayfasından açın.

Panelde nerede

Ne yapacaksınızPanel sayfası
3B model yüklemek, önizlemek, yayınlamak, arşivlemek, klip eklemek3D modeller, Model profili
Modeli eşyaya ya da karaktere bağlamakİçerik (yerleşik eşya/karakter grupları)
Mağaza afişi ve ürün görseli, para birimi ikonu yüklemekMağaza, Para birimleri
Şehir sohbetini denetlemek (sahne eklentisi)Şehir sohbeti
Scene eklentisinin kurulu olduğunu ve yetkisini görmekEklentiler

Veri modeli (düz dille)

Scene

  • Kimlik üç parçadan türetilir: plugin_id (eklenti), module_id (gerçek zamanlı modül) ve key (en çok 96 karakter). Aynı üçlü her zaman aynı scene’e çıkar.
  • capacity: Odanın oyuncu kapasitesi; en çok 10 000.
  • state: open, draining (boşalıyor) ya da closed.
  • partition_id: Scene’in dağıtık çalıştığı bölüm kimliği (sunucu üretir).

Görsel

  • Tür dosyanın ilk baytlarından çıkarılır; Content-Type başlığı yalnız tavsiyedir. Kabul edilen türler image/png, image/jpeg, image/webp.
  • En çok 2 MiB; genişlik ve yükseklik her biri en çok 4096 piksel.
  • Yanıt sha256, contentType, byteSize ve created (bu çağrıda yazıldı mı) verir.

3B model

  • Yükleme glb olabilir ya da fbx, obj, usdz, stl, blend, 3mf, dxf gibi kaynak biçimlerinden dönüştürülür. Dönüştürme durumu queued, converting, ready, failed’dır; başarısızlığın nedeni panelde Türkçe cümleyle görünür (ör. harici kaynağa başvuru, bütçe aşımı, 2B DXF).
  • Her model sürümlüdür; yayınlanan sürüm katalogda görünür. Yaşam döngüsü active, archived, purged.
  • Varsayılan sınırlar: yüklenen dosya 50 MiB, ortam kotası 1 GiB, ortamda 200 model, model başına 20 sürüm, operatör başına 10 dakikada 10 yükleme.
  • Dönüştürücü yalnız amd64 mimarisinde çalışır; başka mimaride yalnız glb yüklenebilir.

Oyunun yaptığı çağrılar

Yöntem ve yolKimlikNe yapar
PUT /v1/scenessunucu anahtarıScene’i idempotent sağlar
PUT /v1/assetssunucu anahtarıGörseli ham gövdeyle yükler
GET /v1/assetssunucu anahtarıOrtamın görsellerini listeler (cursor, limit)
DELETE /v1/assets/{sha256}sunucu anahtarıGörsel satırını siler (204)
GET /v1/assets/{sha256}kimliksizGörsel baytlarını okur
GET /v1/model-files/{sha256}kimliksizYayınlanmış modelin glb dosyasını okur

Yönetim uçları Authorization: Bearer crx_live_<önek>_<gizli> ister; istemci anahtarı 403 alır.

Scene sağlamak

PUT /v1/scenes
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{ "plugin_id": "package-ride", "module_id": "package-ride.presence", "key": "istanbul-1", "capacity": 200 }
{ "scene_id": "…", "plugin_id": "package-ride", "module_id": "package-ride.presence", "key": "istanbul-1", "partition_id": "…", "capacity": 200, "state": "open", "created": true }
  • Scene yoksa yazılır (201, created: true); varsa değiştirilmeden döner (200, created: false). Idempotency-Key gerekmez.
  • 400: kimlik, anahtar ya da kapasite geçersiz (gövde hataları da 400’dür). 403: istemci anahtarı. 409: aynı anahtarda farklı kimlikli scene. 422: eklenti bu ortamda etkin değil.

Bir eklentinin gerçek zamanlı modüllerini ve oyuncu tarafı akışı için bkz. Gerçek zamanlı: genel bakış ve Eklentiler.

Görsel yüklemek

PUT /v1/assets
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: image/png

<ham PNG baytları>
{ "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "contentType": "image/png", "byteSize": 18234, "created": true }

201 ve created: true ilk yazımdır; aynı baytlar zaten varsa 200 ve created: false döner ve satır değişmez, yani yükleme idempotenttir. Boş gövde, tanınmayan tür, 2 MiB’ı aşan boyut ya da 4096 pikseli aşan en/boy 400 ile reddedilir (hata kodları asset_empty, asset_unsupported_type, asset_too_large, asset_dimensions_too_large).

Görseli oyunda göstermek için <img src="…/v1/assets/<sha256>?env=<ortam uuid>"> kullanın: uç kimliksizdir, ortamı x-crx-env başlığından ya da (başlık gönderemeyen <img>/doku yükleyicileri için) env sorgu parametresinden alır. Yanıt ETag olarak sha256 taşır ve süresiz önbelleğe alınabilir (immutable); If-None-Match eşleşirse 304 döner. Yeni görsel yeni sha256, yani yeni adres demektir.

Not

Görsel ve model okuma uçları, tarayıcının doku yükleyicisinin kullanabilmesi için çapraz kökenden okunabilir yanıt başlıkları gönderir. Diğer kamusal uçlar CORS’u yalnız ortamın alan adı listesindeki origin’lere verir; bkz. Mimari ve barındırma.

Uyarı

DELETE /v1/assets/{sha256} yalnız satırı siler; depo, başka kayıtların (ürün, ikon, içerik) bu görsele başvurup başvurmadığını bilmez. Silmeden önce görselin kullanılmadığından emin olun.

3B modeli oyuna almak

Modeli panelde yükleyip yayınlayın ve eşyaya ya da karaktere bağlayın. Oyun modelin adresini ve hesaplanmış gerçeklerini (üçgen sayısı, kemik, klipler, sınır kutusu) katalogdan okur; model.glb.url /v1/model-files/<sha256>?env=<ortam uuid> biçimindedir. Ayrıntı: İçerik grupları › Katalog ve Oyun katalog entegrasyonu (docs/oyun-katalog-entegrasyonu.md).

GET /v1/model-files/{sha256} kimliksizdir ve içerik adreslidir:

  • Ortam x-crx-env başlığıyla ya da env sorgusuyla verilir; ikisi de verilip farklıysa ya da ikisi de yoksa 400.
  • Yalnız hazır, dosyaları tutulan ve silinmemiş modelin glb dosyası döner; silinmiş modelde 404.
  • Eşleşen If-None-Match dosyayı açmadan 304 verir. 200 yanıtı Content-Length, model/gltf-binary türü ve süresiz önbellek başlıklarını taşır. Range desteklenmez.
  • Aynı sha256 her zaman aynı baytlardır: dosyayı sha256 ile önbelleğe alın, yeniden indirmeyin.

Not

Model yükleme ve yayınlama yalnız panelden yapılır; modeller için herkese açık bir yazma ucu yoktur. Unity SDK’sında katalog ve model okuma yardımcıları yoktur; ham REST kullanılır.

Sürümleme ve tekrar güvenliği

  • PUT /v1/scenes ve PUT /v1/assets doğal olarak idempotenttir (türetilmiş kimlik, içerik adresi); Idempotency-Key istemezler.
  • Model dosyası ve görsel içerik adreslidir: aynı adres hiçbir zaman başka bayt vermez.
  • Model sürümleri panelde izlenir; oyun her zaman yayınlanmış güncel sürümü katalogdan görür.

Olaylar

Bu yüzeyler webhook olayı üretmez. Model değişikliği katalogda yeni sha256 olarak yansır; ETag ile yoklayın.

Sınırlar

  • Görsel en çok 2 MiB ve 4096×4096 piksel; yalnız PNG, JPEG, WebP.
  • Scene key en çok 96 karakter, capacity en çok 10 000.
  • Model varsayılan sınırları yukarıdadır (50 MiB, 1 GiB kota, 200 model, 20 sürüm).
  • GET /v1/assets sayfa boyu en çok 200 (varsayılan 50).

Sık görülen hatalar

BelirtiNedenÇözüm
422 (scene)Eklenti bu ortamda etkin değilEklentiyi etkinleştirin (Eklentiler)
409 (scene)Aynı anahtarda farklı kimlikli sceneAnahtarı değiştirin
400 (görsel)Tür tanınmadı, 2 MiB ya da 4096 piksel aşıldıPNG/JPEG/WebP, 2 MiB ve 4096 piksel altında yükleyin
<img> yüklenmiyorenv ya da x-crx-env eksikAdrese ?env=<ortam uuid> ekleyin
404 (model dosyası)Model silinmiş ya da henüz hazır değilKatalogdaki model alanını kontrol edin
Panelde model failedDönüştürme hatasıPanelin gösterdiği nedene göre dosyayı düzeltin

Çalışan örnek

Görsel yükleme ve okuma:

curl -X PUT "$API/v1/assets" \
  -H "Authorization: Bearer $SERVER_KEY" -H "Content-Type: image/png" \
  --data-binary @afis.png

curl "$API/v1/assets/<sha256>?env=$ENV" -o afis.png

İlgili sayfalar

İçerik grupları

Bu sayfa araçlar, silahlar, NPC’ler ya da polis birimleri gibi oyun verisini kod yayımlamadan değiştirmenizi sağlayan içerik gruplarını anlatır. Verinin panelde nasıl tanımlanıp yayınlandığını ve oyunun onu nasıl okuduğunu öğrenmek istiyorsanız okuyun.

Ne işe yarar

İçerik grubu, sizin tanımladığınız tipli bir tablodur: grubun alanları (ör. hiz, hasar, renk, model) vardır ve her kayıt (ör. sedan) bu alanlar için değer taşır. Operatör değeri panelde düzenler ve yayınlar; oyun yalnızca yayınlanmış güncel hâli okur. Böylece bir değeri değiştirmek için oyunu yeniden dağıtmanız gerekmez.

İçerik grupları çekirdektir: ayrı bir modül anahtarı yoktur ve her ortamda çalışır.

Panelde nerede

Ne yapacaksınızPanel sayfası
Grup oluşturmak, alan eklemek, şablondan başlamakİçerik, Grup düzenle
Kayıt eklemek, değerleri düzenlemek, kaydı yayına almak ya da arşivlemekKayıt düzenle
Yayın geçmişini ve farkı görmek, değişiklikleri oyuna göndermekİçerik › Yayınla
Model alanına 3B model bağlamak3D modeller

Veri modeli (düz dille)

  • Grup: key (^[a-z][a-z0-9_]{1,47}$, yani 2–48 karakter), ad ve alanlar. Anahtar oluşturulduktan sonra değişmez.

  • Alan: key (aynı kural) ve bir tür. On tür vardır:

    Tür (kind)Ne tutarAyarlar
    integerTam sayı (isteğe bağlı ondalık gösterim ölçeği)min, max, scale (0–3), unit (en çok 12 karakter), default
    percentBaz puan yüzde (10000 = %100)min_bp, max_bp, default_bp
    textMetinmax_chars (en çok 512), multiline
    boolEvet/hayırdefault
    selectSeçenek(ler)options (en çok 64), multiple
    colorRenk#rrggbb
    model3B modelmodel kütüphanesi kimliği
    imageGörselvarlığın sha256 değeri
    referenceBaşka kayda bağlantıhedef: başka bir grup, yerleşik eşya ya da yerleşik karakter
    tableTekrarlanan satırlı matriscolumns (1–8), min_rows, max_rows (en çok 100)
  • Görünürlük (audience): Her alan player (oyuncuya gider) ya da server (yalnız sunucu anahtarı görür) olabilir.

  • Kayıt (entry): key (^[a-z0-9][a-z0-9_-]{0,63}$) ve alan değerleri. Durumu: Yayında, Taslak değişikliği var, Arşivli, Henüz yayınlanmadı.

  • Yayın: Panelde “Değişiklikleri oyuna gönder” ile yapılır; en çok 280 karakterlik bir not alır ve sıralı bir yayın sayacı (seq) üretir. Bir sonraki yayın öncekini değiştirmez, yenisini ekler; geçmiş ve fark panelde görünür. Siz bakarken biri yayınladıysa panel farkı yeniler ve yeniden onay ister.

  • Sayılar: Tamsayıdır; kesirli ya da üslü gösterim (40.0, 1e2) reddedilir. Ölçekli tamsayıyı oyunda formatScaled ile gösterin.

Not

Eşya ve karakter şablonları da panelde yerleşik gruplar olarak görünür; onlar “kaydedince hemen oyunda” davranır ve yayın adımı istemez. Kullanıcı tanımlı gruplar yayın ister.

Sınırlar: ortamda en çok 64 grup, grupta 48 alan ve 1000 kayıt; bir kaydın tablo verisi 128 KiB’ı, sunucu kitlesi için yayın görüntüsü 1 MiB’ı aşamaz.

Katalog: eşya ve karakter

Oyun, eşya ve karakter tanımlarını (anahtar, ad, tipli veri satırları ve 3B model adresi) katalog uçlarından okur:

Yöntem ve yolNe döner
GET /v1/catalog/items, GET /v1/catalog/items/{key}Eşya manifestleri
GET /v1/catalog/characters, GET /v1/catalog/characters/{key}Karakter manifestleri

Bunlar items ve characters modüllerine bağlıdır; modül kapalıysa 404. Liste sayfalıdır (limit 1–100, varsayılan 50; cursor). Her manifest kind, key, name, definition_version, data (tipli satırlar), model (yoksa null) ve content_sha256 taşır. Ayrıntı, ETag ile izleme ve three ile model gösterme: Oyun katalog entegrasyonu (docs/oyun-katalog-entegrasyonu.md) ve Scene, görsel ve 3B modeller.

Oyunun yaptığı çağrılar (içerik grupları)

İçerik grupları salt okunur bir yüzeyle sunulur; oyun taslağa ulaşamaz. İki kimlik türü vardır:

  • Oyuncu oturumu (çerez ya da oturum belirteci + x-crx-env: <ortam uuid>): yalnız visibility = player alanları ve değerlerini görür.
  • Sunucu anahtarı (Authorization: Bearer crx_live_<önek>_<gizli>): bütün alanları görünürlükleriyle ve seq ile görür. Geçersiz bir Bearer crx_… anahtarı geçerli bir çerezle bile 401 alır; çereze düşülmez.
Yöntem ve yolNe döner
GET /v1/catalog/groupsYayınlanmış grupların anahtarı, adı ve kitleye göre content_sha256 değeri
GET /v1/catalog/groups/{group}Grubun güncel yayını: group, fields, entries, seq
GET /v1/catalog/groups/{group}/entries/{key}Aynı şekil, yalnız o kayıt

Başlıklar:

BaşlıkAnlamı
x-crx-envOyuncu oturumunda zorunlu; sunucu anahtarında ortam anahtardan gelir
If-None-MatchElinizdeki ETag; eşleşirse 304 ve gövde yoktur

Yanıtlar kitleye özgü ETag taşır. Oyuncu ETag’i ve content_sha256 değeri süzülmüş gövdeden hesaplanır; bu yüzden yalnız-sunucu bir alanı değiştirmek oyuncu tarafında hiçbir değişiklik göstermez. Hiç yayınlanmamış ya da olmayan grup 404 döner.

Not

Yalnız-sunucu satırlar bir sır deposu değildir: panelde operatörler ve sunucu anahtarı tutanlar görür. Parola, anahtar ya da kişisel veri koymayın.

Tablo alanı

table alanı bir kayıtta tekrarlanan satırları tutar (silahın seviye → hasar tablosu, NPC ganimet listesi). Sütun türleri integer, percent, text, bool, select ve reference’tır. Değer, satır sırasını koruyan nesne dizisidir; sırayı değiştirmek yeni bir yayın özeti demektir. Yalnız-sunucu tablo oyuncu gövdesinde hiç yoktur.

Oyundan okumak

TypeScript SDK’sı iki kitle için de tipli okuyucu sağlar (player.catalog.groups, sunucuda CrxClient.catalog.groups; list, get, entry). İzleme için watchCatalogGroup ve ölçekli sayıyı göstermek için formatScaled vardır. Unity SDK’sında katalog yoktur; ham REST kullanılır.

Sürümleme ve tekrar güvenliği

  • Okuma uçlarında Idempotency-Key ya da If-Match yoktur; If-None-Match ile koşullu okuma yaparsınız.
  • Yayın sayacı (seq) her yayında artar; seq değişmediyse içerik değişmemiştir.
  • Grup, alan ve kayıt anahtarları oluşturulduktan sonra değişmez; alan türü değişimi denetlenir ve panel sorunları yayın öncesinde listeler.

Olaylar

İçerik grubu yayını outbox’a olay yazmaz (yalnız denetim kaydı düşer), dolayısıyla webhook üretmez. Değişikliği oyunda yakalamak için ETag ile yoklayın: değişim oyuncuya en geç bir yoklama aralığı sonra ulaşır; bir dakikalık aralık yeterlidir.

Sınırlar

  • 64 grup / 48 alan / 1000 kayıt; tablo en çok 8 sütun ve 100 satır; kayıt tablo verisi 128 KiB; sunucu yayın görüntüsü 1 MiB.
  • Metin alanı en çok 512 karakter; seçim alanı en çok 64 seçenek.
  • Yayın notu en çok 280 karakter.

Sık görülen hatalar

BelirtiNedenÇözüm
404 (grup)Grup hiç yayınlanmadı ya da yokPanelde grubu yayınlayın
401Kimlik yok ya da geçersiz Bearer crx_…; oyuncu oturumunda x-crx-env eksikKimliği ve başlığı düzeltin
Panelde yayın reddedildiBaşvuru (reference) hedefi yok; tablo ya da yayın boyutu aşıldıPanelin listelediği sorunları giderin
Değeri değiştirdim, oyun görmüyorTaslak yayınlanmadı ya da alan server görünürlüklüYayınlayın; görünürlüğü kontrol edin
304ETag eşleştiBeklenen davranış; elinizdeki kopya güncel

Çalışan örnek

weapons grubunun güncel yayınını ETag ile okuma:

/** İçerik grubunu (panelde yayınlanan oyun verisi) oyuncu oturumuyla okur; ETag ile değişmediyse 304 döner. */
import type { PlayerClient } from "@ferdiunal/open-play-ts";

/** `weapons` grubunun güncel yayınındaki kayıt anahtarlarını döndürür; değişmediyse boş liste. */
export async function readWeapons(player: PlayerClient, previousEtag?: string): Promise<readonly string[]> {
  const result = await player.catalog.groups.get("weapons", previousEtag === undefined ? {} : { ifNoneMatch: previousEtag });
  if (result.status === 304) return [];
  return result.value.entries.map((entry) => entry.key);
}

İlgili sayfalar

Oyun kuralları ve otomasyonlar

Bu sayfa panelin “Kurallar” ekranının iki parçasını anlatır: oyunun eklentisinin bildirdiği oyun kuralı değerleri (örneğin polis eşikleri) ve olay olunca otomatik iş yapan otomasyonlar. Oyunun davranışını kod yayımlamadan ayarlamak ya da bir olaya otomatik tepki vermek istiyorsanız okuyun.

Ne işe yarar

  • Oyun kuralı değerleri: Oyunun eklentisi hangi sayıların ayarlanabilir olduğunu bildirir (ad, birim, alt ve üst sınır, adım, varsayılan). Operatör bu değerleri panelde değiştirir ve yayınlar; yeni değerler oyunda kısa süre içinde (en geç yaklaşık 6 saniye) geçerli olur. Her yayın yeni bir sürüm olur ve tek tıkla eski değerlere dönülebilir.
  • Otomasyonlar: “Şu olay olunca, şu koşul doğruysa, şunu yap” biçiminde kurallar. Kural, karakter olaylarına (level_up, xp_added …) bağlanır ve karaktere stat, kaynak, bayrak, değiştirici ya da ödül verir. Koşullar CEL ifadesiyle yazılır; panelde yapısal bir oluşturucu CEL’i sizin için üretir.

Oyun kuralı değerleri panel yüzeyidir: oyun sunucunuz bunları bir REST ucuyla yazmaz; eklenti değerleri sunucuda okur. Otomasyonlar ise hem panelden hem REST ile yönetilir (characters modülünün parçasıdır).

Panelde nerede

Ne yapacaksınızPanel sayfası
Kurallar ekranına genel bakış, konu ve kartlarKurallar
Bir oyun kuralı kartını düzenleyip yayınlamak, önizlemekKural kartı
Yayın geçmişini ve farkı görmek, geri almakKural geçmişi
Otomasyon oluşturmak, düzenlemek, açıp kapatmakOtomasyon düzenleyici

Oyun kuralı değerleri

Model

  • Katalog: Oyunun eklentisi bildirir. Konular, kartlar ve her kartın parametreleri vardır; her parametre bir birimle (örn. saniye, metre, yüzde) ve sınırlarla gelir. Katalog yoksa ya da eklenti bildirmiyorsa panelde yalnız Otomasyonlar görünür.
  • Sürüm: Etkin değerler en son sürümdür. Hiç yayın yoksa eklentinin kod varsayılanları kullanılır (“Kod varsayılanı”). Yayın ve geri alma yeni sürüm yazar; geçmiş silinmez.
  • Uygulama zamanı: Parametre canlı ise yayından sonra kısa sürede uygulanır; yeniden başlatmada ise oyun sunucusu yeniden başlayana dek eski değer kalır ve panel “Yayında / Bekliyor” olarak ikisini birlikte gösterir.
  • Doğrulama: Değerler katalog sınırı, adım ve alanlar arası kısıtlarla sunucuda doğrulanır; hatalı alan alan başına bildirilir. Eklenti ayrıca aynı sınırlarla yeniden doğrular; geçersiz bir değer oyunu bozmaz, eklenti kod varsayılanlarına döner.
  • Yetki: Okuma dört panel rolünde; yayın ve geri alma yalnız super_admin ve game_manager rollerinde. Yayın ve geri alma 8–500 karakterlik bir gerekçe ister ve denetim kaydına yazılır.
  • Eşzamanlılık: Yayın, gördüğünüz sürümü (expectedVersion) taşır; siz bakarken başka biri yayınladıysa 409 döner ve sekmeyi yenilemeniz gerekir.
  • Önizleme: Panel, bir senaryoyu (örn. sürekli hız ihlali) eklentinin gerçek mantığıyla bugünkü ve önerilen değerlerle çalıştırıp olay zaman çizelgesini gösterir; hiçbir şey kaydetmez.
  • İstemci uyarısı: Bir parametre clientVisible ise oyun istemcisi onu eklentinin sorgusundan okur. İstemci o değeri henüz okumuyorsa panel yayın sırasında sizi uyarır.

Oyuna etkisi

Oyun kuralı değerlerini oyun kodunuz REST ile okumaz. Eklenti değerleri sunucuda her adımda tek bir görüntüden alır; istemciye gereken değerler eklentinin kendi sorgusuyla gelir. Bu davranış eklentiye özgüdür; bugün yalnız package-ride eklentisi bir katalog bildirir. Üçüncü taraf eklentilerin katalog bildirmesi ve yüklenmesi henüz desteklenmez; bkz. Eklentiler: genel bakış.

Otomasyonlar

Model

  • Kural (rule): key (tekil anahtar), trigger_type (tetikleyici olay), condition (CEL ifadesi), actions (eylem listesi) ve priority. Kural sürümlüdür; her yayın yeni sürümdür.
  • Tetikleyici: flag_set, attribute_set, resource_changed, modifier_added, modifiers_removed, xp_added, level_up, ability_unlocked, event_emitted, character_rolled_back. Bilinmeyen tür reddedilir. Panel oluşturucusu character_rolled_back seçeneğini sunmaz.
  • Eylemler: add_stat (key, delta), add_resource (key, delta), set_flag (key, value), add_modifier, remove_modifier ve grant_reward (what: sabit para { "type": "currency", "key", "amount" } ya da ödül tablosu { "type": "table", "key" }).
  • CEL bağlamı: Koşulda character, event, now ve seed değişkenleri vardır; has_ability, base_stat ve clamp işlevleri kullanılabilir. Zamanı ve tohumu bağlam taşır, kural kendi saatini okuyamaz; now() ve random() yoktur. Kurallar kayıt anında derlenir, çalışma anında değil.
  • Bütçe (değişmez): Bir komut zincirinde en çok 50 kural değerlendirilir, 200 eylem üretilir, zincir derinliği 5’i, koşul ifadesi 500 düğümü aşamaz. Aşım fail-closed’dur: komut 429 ile reddedilir ve hangi sınırın aşıldığı söylenir. Birbirini tetikleyen iki kural bu sınırla kesilir.

Panel oluşturucusu

Oluşturucu “Ne zaman → Koşullar → Ne yap” yapısından CEL üretir: koşul satırları (alan, işleç, değer) hepsi (&&) ya da herhangi biri (||) ile birleşir; en çok 20 koşul ve 10 eylem eklenebilir. Metin değerleri kaçışlanır; satır sonu ve kontrol karakterleri reddedilir. Oluşturucu varken sunucu kuralı yeniden derler (istemcinin gönderdiği CEL metni oluşturucu kuralında okunmaz). Yeni kural kapalı başlar; “hemen aç” seçeneği işaretsizdir. Yayın öncesi örnek bir olayla dry-run (deneme koşusu) yapabilirsiniz; zorunlu değildir.

Oyunun yaptığı çağrılar

Kural uçları API anahtarıyla çağrılır:

Authorization: Bearer crx_live_<önek>_<gizli>
Yöntem ve yolNe yapar
POST /v1/rulesKuralı kaydeder (201)
GET /v1/rulesKuralları listeler (cursor, limit)
GET /v1/rules/{key}Kuralın güncel sürümünü okur
POST /v1/rules/{key}/enable, POST /v1/rules/{key}/disableKuralı açar / kapatır (204)
DELETE /v1/rules/{key}Kuralı siler (204)
POST /v1/rules/dry-runKuralı bir karaktere karşı yazmadan dener

Uyarı

Bilinen kısıt: Kural yazan uçlar anahtar türüne bakmaz; geçerli bir istemci anahtarı da kural kaydedebilir ya da silebilir. İstemci anahtarını oyuna gömüyorsanız kural yönetimini kendi sunucunuzdan yapın ve bu uçları istemciden çağırmayın.

TypeScript SDK’sı bu uçları tipli sunar: createRule, listRules, getRule, deleteRule, enableRule, disableRule, dryRunRule (bkz. TypeScript SDK). Bu uçlar ham CEL metni alır; panel oluşturucusunun yapısal modeli yalnız panel yüzeyindedir.

Kural kaydetmek

POST /v1/rules
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{
  "key": "lvl10_reward",
  "trigger_type": "level_up",
  "condition": "event.to == 10",
  "actions": [
    { "type": "grant_reward", "what": { "type": "currency", "key": "gold", "amount": 1000 } },
    { "type": "set_flag", "key": "reached_level_10", "value": true }
  ],
  "priority": 0
}
{ "key": "lvl10_reward", "current_version": 1, "enabled": true, "created_at": "2026-11-03T09:00:00Z" }

REST ile kaydedilen kural hemen açık başlar (enabled: true); panel oluşturucusundan farklı olarak kapalı başlatmak için kaydın ardından POST /v1/rules/{key}/disable çağırın. event.to seviye atlama olayının yeni seviyesidir; olay alanları tetikleyiciye göre değişir. Koşul derlenemezse, tetikleyici bilinmiyorsa ya da bütçe sınırı aşılıyorsa kayıt reddedilir.

Deneme koşusu

POST /v1/rules/dry-run
Authorization: Bearer crx_live_<önek>_<gizli>
Content-Type: application/json

{ "condition": "character.progression.level >= 10", "actions": [ { "type": "set_flag", "key": "veteran", "value": true } ], "character_id": "0192f0f2-…" }
{ "condition_result": true, "node_cost": 7, "would_produce_events": [ { "...": "…" } ], "would_grant": [], "warnings": [] }

Dry-run hiçbir olay yazmaz ve ödül vermez.

Sürümleme ve tekrar güvenliği

  • Oyun kuralı yayını ve geri alma: Idempotency-Key ve expectedVersion ile panel tarafından yapılır; aynı anahtarla tekrar aynı sürümü döndürür, tek denetim kaydı yazar.
  • Otomasyon yayını: her yayın yeni sürümdür; eski sürümler silinmez ve “geri al” eski sürümü yeni sürüm olarak yeniden yazar.
  • REST kural uçlarında Idempotency-Key ve If-Match yoktur.

Olaylar

Kuralın çalıştırdığı eylemler normal karakter olaylarını üretir (flag_set, xp_added …); bunlar webhook’larla iletilebilir. Kuralın ürettiği ödül reward_granted olayı da üretir.

Sınırlar

  • Komut zincirinde 50 kural, 200 eylem, derinlik 5, koşul başına 500 düğüm.
  • Panel oluşturucusunda en çok 20 koşul ve 10 eylem.
  • Oyun kuralı gerekçesi 8–500 karakter.
  • Oyun kuralı tam sayı değerleri panelde ±(2^53−1) ile sınırlıdır.

Sık görülen hatalar

BelirtiNedenÇözüm
429 (komut)Kural zinciri bütçeyi aştıBirbirini tetikleyen kuralları sadeleştirin
Kayıtta 400/422CEL derlenemedi, bilinmeyen tetikleyici ya da alanKoşulu önce dry-run ile deneyin
Panelde 409 (yayın)Siz bakarken başka biri yayınladıSekmeyi yenileyip tekrar yayınlayın
Panelde alan hatası (422)Değer sınır, adım ya da kısıt dışındaAlanın gösterdiği sınıra uyun
Değeri yayınladım, oyun eski davranıyorParametre yeniden başlatmada uygulanıyor ya da 6 saniye dolmadıPanelin “Bekliyor” göstergesine bakın

Çalışan örnek

Yukarıdaki iki istek (lvl10_reward kaydı ve dry-run) birlikte tam bir akıştır: önce dry-run ile koşulu deneyin, sonra POST /v1/rules ile kaydedin, sonra bir karaktere add_xp komutuyla seviye atlatarak kuralın çalıştığını Karakterler sayfasındaki akışla doğrulayın.

İlgili sayfalar

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ızPanel sayfası
Bir oyuncunun anahtarlarını, boyutlarını ve değerini görmek; gerektiğinde silmekOyuncular › 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ırDeğ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 yolKimlikNe yapar
GET /v1/player/dataoyuncu oturumuKendi anahtarlarının özetini listeler
GET /v1/player/data/{key}oyuncu oturumuBir 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 oturumuIf-Match ile siler (204)
GET /v1/players/{player_id}/datasunucu 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": "…" }.

DurumcodeNe zaman
404not_foundAnahtar yok (sunucu uçlarında oyuncu bu ortamda yoksa da)
409version_conflictSürüm tutmadı ya da var olan anahtara If-None-Match: *; currentVersion güncel sürümü (anahtar yoksa null) taşır
413value_too_largeDeğer 64 KiB’ı aştı; limit alanı sınırı verir
422invalid_keyAnahtar kalıba uymuyor
422invalid_valueDeğer nesne değil, gövde { "value": … } değil ya da JSON değil
422too_many_keysOyuncu 16 anahtarda; limit alanı sınırı verir
428precondition_requiredIf-Match / If-None-Match: * yok, çift ya da biçimsiz
429rate_limitedDakikada 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-Match ile 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önen version değ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-After kadar 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).

İlgili sayfalar

Realtime genel bakış

Bu sayfa, Open Play’in sunduğu üç WebSocket’i ve hangisini ne zaman kullanacağınızı anlatır. Oyun içi durum (hareket, oda, sahne) için eklenti realtime soketini, bildirim ve sohbet için ise ayrı iki soketi kullanırsınız; hepsinin kimlik ve kapanış kuralları farklıdır.

Üç soket

SoketYolNe içinKim konuşurKimlik
Realtime (eklenti)/v1/realtime/wsOyun durumu: anlık girdi, durum anlık görüntüsü (snapshot), kalıcı komut ve olayİki yönlüTek kullanımlık bilet (ticket)
Bildirim/v1/wsBildirimin oyuncuya canlı itilmesiYalnız sunucu → oyuncuOyuncu oturumu (çerez ya da Bearer)
Sohbet/v1/chat/wsKanal sohbeti (abone ol, mesaj gönder)İki yönlüOyuncu oturumu (çerez ya da Bearer)

Kural: üçü de oyuncu soketidir. Sunucu anahtarı (server key) ile soket açılmaz; sunucudan sunucuya iş için REST uçlarını kullanın.

Hangisini kullanmalıyım

İhtiyaçSeçim
Aracın, oyuncunun ya da odanın anlık durumunu yayınlamak ve oyuncu girdisini sıralı almakRealtime soketi, protokol
Bir komutun sonucunu kalıcı ve tekrar-güvenli almak (ödül, teslimat)Realtime Command çerçevesi ya da HTTP komut ucu (eklentileri çağırma)
Oyuncuya “yeni bildirim” anlık göstermek/v1/ws; kalıcı kaynak her zaman GET /v1/notifications gelen kutusudur (bildirimler)
Kanal sohbeti/v1/chat/ws ya da REST (sohbet)
Skor tablosu, mağaza, envanterREST; bunlar için soket yoktur

Not

TypeScript SDK’sı realtime soketini sizin yerinize yönetir: bilet alma, kimlik doğrulama, yeniden bağlanma ve anlık görüntü kurtarma dahildir. Elle protokol yazmanız yalnız SDK’sı olmayan bir istemci (ör. Unity yerel soketi) için gerekir. SDK kullanımı için TypeScript SDK sayfasına bakın.

Bağlantı koşulları (ortak)

  1. HTTPS ve izinli origin. Tarayıcı oyununuz API’yi ters vekille aynı kökenden ya da alan adı bir ortamda kayıtlıysa doğrudan Open Play adresinden (CORS) çağırır. Soketler aynı adrese açılır: wss://<oyun-alan-adınız>/<yol> ya da wss://<open-play-adresi>/<yol>; el sıkışmasında Origin ortam başına denetlenir (CORS bunu değiştirmez). Ayrıntı: mimari ve barındırma.
  2. Ortam başlığı. Oyuncu kimliği her zaman bir ortama (environment) bağlıdır ve x-crx-env: <ortam uuid> başlığı gerekir. Realtime soketinde başlık HTTP bilet isteğiyle verilir; WebSocket yükseltmesi başlık taşımaz, bilet ortamı bilir. /v1/ws ve /v1/chat/ws ise başlığı yükseltme isteğinin kendisinde ister. Tarayıcının WebSocket API’si özel başlık ekleyemez; bu yüzden bu iki soket tarayıcıdan doğrudan açılamaz (bilinen kısıt). Tarayıcı oyunlarında bildirim için gelen kutusunu REST ile yoklayın, sohbet için REST uçlarını kullanın; tarayıcı dışı istemciler başlığı kendileri gönderebilir.
  3. Sorgu dizesinde jeton yok. Hiçbir soket oturum jetonunu URL’de kabul etmez; erişim günlüklerine sızmasın diye ?token= desteklenmez. Realtime yükseltmesi sorgu dizesi taşıyorsa reddedilir.
  4. Origin başlığı zorunludur (realtime): hem bilet isteği hem yükseltme Origin taşımalıdır ve ortamın izinli kökenlerinden biri olmalıdır. Tarayıcı bunu kendisi ekler; tarayıcı dışı bir istemciyse izinli bir Origin değerini kendiniz göndermelisiniz.
  5. Modül kapısı. Realtime soketi plugins, /v1/ws notifications, /v1/chat/ws chat modülüne bağlıdır. Modül kapalıysa yükseltme 404 ve module_disabled döner; açık soket, modül sonradan kapanırsa 1008 (neden module_disabled) ile kapatılır, kapı durumu doğrulanamıyorsa 1013 ile. Modülleri panelden yönetirsiniz: Oyun ayarları: modüller.

Bildirim soketi (/v1/ws)

  • Yalnız sunucudan oyuncuya akar. İstemciden gelen çerçeveler yok sayılır.
  • Gelen çerçeve bir JSON nesnesidir: {"type":"notification","notification_id":"…","title":"…","body":"…","data":{…},"channel":"in_game","sent_at_ms":1760000000000}. Alan adları snake_case’dir.
  • Canlı çerçeve en çok bir kez (at-most-once) teslim edilir. Kalıcı kaynak gelen kutusudur; aynı bildirim hem çerçeveyle hem gelen kutusu sorgusuyla gelebilir, notification_id ile tekilleştirin.
  • Bağlantı presence (çevrimiçi) sinyali olarak sayılır.

Sohbet soketi (/v1/chat/ws)

Çerçeveler type alanı (camelCase değerli) ile ayrılır; diğer alan adları snake_case’dir ve bilinmeyen alan reddedilir. Örnek: {"type":"send","channel_id":"…","client_message_id":"retry-1","body":"merhaba"}.

YöntypeAlanlarAnlam
İstemci → sunucusubscribechannel_idKanal aboneliği (aynı anda tek kanal)
İstemci → sunucuunsubscribe—Aboneliği bırakır
İstemci → sunucusendchannel_id, client_message_id, bodyREST ile aynı tekrar-güvenli mesaj gönderme
İstemci → sunucupingnonce (sayı)Canlılık yoklaması
Sunucu → istemcisubscribed / unsubscribedchannel_id (yalnız subscribed)Kabul
Sunucu → istemcimessagemessage, duplicateKanal mesajı; duplicate: true ise client_message_id daha önce işlenmişti
Sunucu → istemcipongnonceping yanıtı
Sunucu → istemcierrorcodeinvalid_frame, invalid_payload, forbidden_channel, unavailable, message_rejected, modül kapanırken module_disabled

Mesaj gönderme sınırı: oyuncu başına 10 saniyede 20 mesaj; aşımı message_rejected ya da REST’te 429 olarak görürsünüz (hız sınırları). Ayrıntılı kullanım için sohbet rehberi.

Sıradaki adım

Realtime protokolü

Bu sayfa, /v1/realtime/ws soketinin tel (wire) protokolünü anlatır: bilet alma, bağlanma, kimlik doğrulama ve her istemci ve sunucu çerçevesinin alanları. TypeScript SDK’sını kullanıyorsanız bu protokolü elle yazmazsınız; ama davranışı anlamak, başka bir istemci yazmak ya da sorun gidermek için buradan okuyun.

Not

Protokol sürümü 1’dir. Her çerçevede protocolVersion: 1 bulunur; başka değer UnsupportedProtocol hatası alır (sürümleme).

Genel kurallar

  • Her çerçeve tek bir WebSocket metin mesajı olarak giden bir JSON nesnesidir. İkili (binary) mesaj kabul edilmez.
  • type alanı çerçeve türünü belirler ve PascalCase’dir (Authenticate, Join, …). Diğer alan adları camelCase’dir. Bilinmeyen alan içeren çerçeve reddedilir.
  • Büyük tam sayılar (sıra numaraları, epoch, revizyon, sürüm) JSON sayısı değil ondalık metindir: "sessionEpoch": "7". Kurallı biçim: boş değil, baştaki sıfır yok, yalnız rakam, negatif değil, i64 aralığında. Ayrıntı: sayılar ve kodlama. protocolVersion ve schemaVersion küçük tam sayıdır ve JSON sayısı olarak gider.
  • requestId (UUID) istemcinin her deneme için ürettiği ilişki kimliğidir; sunucu yanıtı ve hatayı aynı kimlikle döndürür.
  • Kimlik doğrulamadan önce yalnız Authenticate, sonra Authenticate dışındaki çerçeveler geçerlidir; yanlış fazdaki çerçeve UnexpectedFrame alır.

Bağlanma akışı

sequenceDiagram
    participant C as "İstemci"
    participant H as "HTTP (REST)"
    participant S as "WebSocket /v1/realtime/ws"
    C->>H: "POST /v1/realtime/tickets"
    H-->>C: "200 ticket, expiresAt"
    C->>S: "Upgrade (Sec-WebSocket-Protocol openplay.realtime.v1)"
    C->>S: "Authenticate(protocolVersion 1, ticket)"
    S-->>C: "Ready(connectionId, playerId, sessionEpoch, authorityGeneration)"
    C->>S: "Join(pluginId, moduleId, target)"
    S-->>C: "Joined(subscriptionId)"
    S-->>C: "Snapshot veya DurableRecovery"
    C->>S: "Input veya Command"
    S-->>C: "Ack veya CommandResult"

1. Bilet al

POST /v1/realtime/tickets HTTP/1.1
Host: oyun.example.com
Origin: https://oyun.example.com
x-crx-env: 0192a3b4-0000-7000-8000-000000000001
Cookie: __Secure-crx.session=<oturum>; __Secure-crx.csrf=<csrf-çerezi>
x-csrf-token: <csrf-jetonu>
HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json

{"ticket":"tkt_AbC123-xyz","expiresAt":"2026-11-03T10:15:30+00:00","protocol":"openplay.realtime.v1"}
BaşlıkZorunluNot
x-crx-envevetOyuncunun ortamı. Oturumun ortamıyla uyuşmalıdır.
OriginevetOrtamın izinli kökenlerinden biri olmalı; bilet bu kökene bağlanır.
Cookie ya da Authorization: Bearer <oturum jetonu>evetÇerez varsa her zaman çerez kipi seçilir; bozuk çerezde Bearer’a düşülmez.
x-csrf-token + __Secure-crx.csrf çereziyalnız çerez kipindeÇift gönderim (double submit). Başarılı her istekte yeni CSRF değeri döner; yanıttaki x-csrf-token başlığını ve Set-Cookie’yi saklayın (oturumlar ve CSRF).
  • Bilet tek kullanımlıktır, 30 saniye geçerlidir ve Origin’e bağlıdır. Soket açmadan önce alın; bekletmeyin.
  • Bu uçta kimlik, CSRF ve Origin hataları aynı yanıtı alır: 403 ve düz metin permission denied. Modül kapalıysa (plugins) 404 ve {"code":"module_disabled",…}.

2. Soketi aç

  • Adres: wss://<oyun-alan-adınız>/v1/realtime/ws. Sorgu dizesi olmamalıdır; olursa yükseltme reddedilir.
  • Sec-WebSocket-Protocol: openplay.realtime.v1 istenmelidir; sunucu bunu seçer. İstenmezse yükseltme reddedilir.
  • Origin zorunludur ve ortamın izinli kökenlerinden biri ya da genel izinli listede olmalıdır. Tarayıcı bunu kendisi gönderir.
  • Yükseltme reddi 401 (düz metin unauthorized); modül kapalıysa 404.
  • Adres başına ve oyuncu başına bağlantı sınırları vardır (limitler).

3. Authenticate gönder

Soket açılınca 5 saniye içinde ilk çerçeve gelmelidir; gelmezse sunucu AuthenticationFailed hatası yazıp kapatır. Kimlik doğrulama fazında çerçeve en çok 4 KiB olabilir.

{"type":"Authenticate","protocolVersion":1,"ticket":"tkt_AbC123-xyz"}

Sunucu bileti tüketir (kökeni ve alt protokolü doğrular), oturumu yeniden doğrular ve cevap verir:

{"type":"Ready","protocolVersion":1,"connectionId":"0192a3b4-0000-7000-8000-0000000000a1","playerId":"0192a3b4-0000-7000-8000-0000000000b2","sessionEpoch":"7","authorityGeneration":"42"}
AlanAnlam
connectionIdBu soketin sunucu kimliği.
playerIdDoğrulanmış oyuncu.
sessionEpochBu soketin yazma dönemi (epoch). Input çerçevelerinde aynen geri verilir. Aynı oyuncunun daha yeni bir soketi aynı kaynağa yazmaya başlarsa eski soketin girdileri Stale alır.
authorityGenerationOrtamın yetki nesli; sunucu tarafı sınırlama içindir, istemci yalnız saklar.

Geçersiz, kullanılmış, süresi dolmuş ya da başka kökene ait bilet AuthenticationFailed ile reddedilir; sunucu soketi kapatır. Yeni bilet alıp yeniden bağlanın.

İstemci çerçeveleri

Ready’den sonra gönderilebilir. Çerçeve boyutu en çok 16 KiB’dir; aşan çerçeve PayloadTooLarge alır.

Join: aboneliğe katıl

{"type":"Join","protocolVersion":1,"requestId":"0192a3b4-0000-7000-8000-000000000010","pluginId":"package-ride","moduleId":"package-ride.street","target":{"kind":"Scene","sceneId":"0192a3b4-0000-7000-8000-000000000031"}}
AlanTürAnlam
requestIdUUIDDeneme kimliği.
pluginIdmetinModülün sahibi eklenti (eklentiler).
moduleIdmetinEklentinin tanımladığı realtime modülü; eklentinin şema belgesindeki modules bölümünde listelenir.
target.kindmetinPlayerSelf, Room, Scene ya da InterestSet.
target.roomId / sceneId / interestSetIdUUIDHedefin kimliği (kind’a göre; PlayerSelf kimlik taşımaz).
cursorUUID, isteğe bağlıÖnceki bir DurableRecovery’den gelen opak devam imleci.

Sunucu hedefi yetkilendirir; ham bir Redis anahtarı ya da konu adı verilmez. Başarı: Joined. Ardından modülün türüne göre bir Snapshot ya da DurableRecovery gelir.

Leave: abonelikten çık

{"type":"Leave","protocolVersion":1,"requestId":"…","subscriptionId":"…"}

Yanıt Left’tir; tekrarlanan Leave zararsızdır.

Input: geçici girdi

Sunucu-otoriter modüllerde oyuncu girdisi (hareket, direksiyon). Kayıp kabul edilmez, sıra numarası zorunludur.

{"type":"Input","protocolVersion":1,"requestId":"…","pluginId":"arena","moduleId":"arena.movement","messageType":"arena.movement.input","schemaVersion":1,"subscriptionId":"…","sessionEpoch":"7","inputSeq":"42","payload":{"delta":1}}
AlanAnlam
messageType, schemaVersionModülün şema belgesindeki girdi tanımıyla birebir eşleşmelidir.
subscriptionIdJoined’dan gelen abonelik.
sessionEpochReady’deki değer.
inputSeqArtan sıra numarası (ondalık metin). Aynı numara ve aynı içerik tekrar gönderilirse Replayed alırsınız; aynı numara farklı içerikle Mismatch.
payloadEklentinin girdi şemasına uyan nesne. Kodlanmış boyutu en çok 4 KiB ve modülün maxBytes değeri kadardır; aşım InvalidPayload alır.

Girdi hızı modülün şema belgesindeki ratePerSecond ve burst değerleriyle tanımlıdır; bağlantı başına genel çerçeve bütçesi ayrıca uygulanır (limitler).

Command: kalıcı komut

Veritabanı işlemiyle kalıcı yazan komut (ödül, teslimat); sonuç yalnız işlem kesinleştikten sonra döner. HTTP komut ucuyla aynı kurallara tabidir (eklentileri çağırma).

{"type":"Command","protocolVersion":1,"requestId":"…","pluginId":"package-ride","moduleId":null,"messageType":"package-ride.progression-missions.claim.input","schemaVersion":1,"idempotencyKey":"claim-7f3a","expectedVersion":"12","payload":{"missionId":"daily-1"}}
AlanAnlam
idempotencyKeyZorunlu mantıksal tekrar anahtarı. Aynı anahtar ve aynı içerik aynı sonucu döndürür (idempotency).
expectedVersionİsteğe bağlı iyimser sürüm (ondalık metin). Uyuşmazlık hata olur.
moduleIdOda kapsamlı komutlarda modül bağlamı; yoksa null.
payloadEklentinin komut şemasına uyar; kodlanmış boyutu en çok 16 KiB’dir, aşım InvalidPayload alır.

Ping

{"type":"Ping","protocolVersion":1,"nonce":"a1"}

Yanıt {"type":"Pong","nonce":"a1"}. nonce isteğe bağlıdır. Uygulama düzeyi Ping bağlantı canlılığını ölçer; oyuncu etkinliği sayılmaz, boşta kapanmayı ertelemez (boşta kapanma).

Sunucu çerçeveleri

typeNe zamanAlanlar
ReadyAuthenticate başarılıprotocolVersion, connectionId, playerId, sessionEpoch, authorityGeneration
JoinedJoin yetkilendirildirequestId, subscriptionId, target
LeftLeave işlendirequestId, subscriptionId
CommandResultCommand kesinleştirequestId, messageType, schemaVersion, payload
AckInput işlendirequestId, inputSeq, status, revision (isteğe bağlı)
SnapshotGeçici durum yayınısubscriptionId, resourceId, messageType, schemaVersion, streamEpoch, revision, payload
DurableRecoveryKalıcı akış için kurtarma bariyerisubscriptionId, cursor, barrierSequence, coveredEventIds, snapshot (boş olabilir)
DurableEventKalıcı, sıralı eklenti olayısubscriptionId, streamId, sequence, eventId, messageType, schemaVersion, cursor, payload
PongPing yanıtınonce
ErrorHatarequestId (varsa), code, retryable

Ack durumları

statusAnlamNe yapmalı
AppliedGirdi durumu ilerletti. revision yeni revizyondur.—
ReplayedAynı sıra numarası ve içerik zaten işlenmişti.Yoksayın; güvenli tekrar.
StaleSıra numarası güncel yazarın gerisinde ya da daha yeni bir soket aldı.Bu girdiyi bırakın; yeni soket varsa bu soket artık yazar değildir.
MismatchAynı sıra numarası farklı içerikle geldi.İstemci hatasıdır; sıra numarasını yeniden üretmeyin, tekrar etmeyin.
ResyncRequiredSıra boşluğu ya da kurtarılamayan durum.Taze anlık görüntü için yeniden Join edin (cursor olmadan).

Applied ve Replayed dışında revision bulunmayabilir. Alan yokken null gönderilmez; alan hiç yoktur.

Snapshot ve DurableRecovery

  • Snapshot geçici durumdur: streamEpoch (sunucu önbelleği yeniden başladığında artar) ve revision (monoton) birlikte sıralanır. Aynı streamEpoch içinde daha eski bir revision, yeni durumu ezmemelidir. streamEpoch değişmişse yeni bir akış başlamıştır; eski revision ile karşılaştırmayın.
  • DurableRecovery kalıcı akışta taban çizgisidir: cursor bu noktaya bağlı yeni imleçtir, barrierSequence anlık görüntünün ve tekrar oynatmanın kapsadığı akış başıdır, coveredEventIds anlık görüntünün zaten içerdiği olay kimlikleridir (bunları ayrıca uygulamayın). snapshot yalnız taze Join’de dolu gelir; cursor ile devam ederken yoktur.
  • DurableEvent’ler sequence sırasıyla gelir; eventId uçtan uca tekilleştirme anahtarıdır, cursor o olaya kadar ilerlemiş yeni imleçtir. İmleci saklayın: yeniden bağlanırken Join.cursor olarak verirsiniz (yeniden bağlanma).

Hata çerçevesi

{"type":"Error","requestId":"0192a3b4-0000-7000-8000-000000000010","code":"InvalidPayload","retryable":false}

Hata çerçevesi gerekçe metni taşımaz: yalnız kod ve retryable. Bir eklenti reddi (ör. iş kuralı) bile InvalidPayload olarak görünür; neden istemciye telden verilmez.

codeNe zamanretryableSoket
UnexpectedFrameFazına uymayan çerçeve (ör. kimlik doğrulamadan önce Join, ya da ikinci Authenticate)hayırkapanır
UnsupportedProtocolprotocolVersion 1 değilhayırkapanır
InvalidPayloadBozuk JSON, bilinmeyen alan, şemaya uymayan yük, eklenti/iş kuralı reddi, kaynak yok, sürüm çakışması, modül kapalıhayırçerçeve bazlı hatada açık kalır; ayrıştırma hatasında kapanır
AuthenticationFailedBilet geçersiz/kullanılmış/süresi dolmuş, 5 sn zaman aşımı, oturum iptal edildihayırkapanır
PermissionDeniedHedef, modül ya da işlem için yetki yok; yasaklı hesaphayıraçık kalır
PayloadTooLargeÇerçeve bütçesini aştı (4 KiB kimlik doğrulama, 16 KiB sonrası)hayırkapanır
CapacityExceededHız ya da kapasite sınırı dolduçerçeve bütçesi ve oran sınırında evet; oyuncu/adres başına soket sayısı sınırında hayırçerçeve bütçesi ve soket sayısı sınırında kapanır
UnavailableBağımlılık geçici kullanılamıyor ya da yerleşim hazır değilgenellikle evet (IdempotencyExpired hâli hayır)duruma göre
ResyncRequiredİmleç yok, süresi dolmuş, uyuşmuyor, boşluklu ya da tekrar bütçesini aştı—açık kalır

Uyarı

Soketin kapanıp kapanmayacağını yalnız retryable’dan çıkarmayın. İstemci, Error çerçevesinden sonra soketin hâlâ açık olup olmadığını bağlantı durumundan (kapanış olayı) öğrenmelidir. Uzun ömürlü soketlerde çerçeve bazlı hatalar (InvalidPayload, PermissionDenied) bağlantıyı düşürmez.

Kapanış kodları

KodNedenAnlam
4100idle_timeoutOyuncu etkinliği gelmedi; boşta kapanma (ayrıntı). Tipli Error çerçevesi olmaz.
1008module_disabledplugins modülü kapatıldı.
1013unavailableModül kapısı doğrulanamadı; sonra yeniden deneyin.

Örnek: elle bağlanma (özet)

Tarayıcıda SDK kullanın; aşağıdaki iskelet protokol sırasını gösterir.

const res = await fetch("/v1/realtime/tickets", {
  method: "POST",
  credentials: "include",
  headers: { "x-crx-env": environmentId, "x-csrf-token": csrfToken },
});
const { ticket } = await res.json();
const ws = new WebSocket(`wss://${location.host}/v1/realtime/ws`, ["openplay.realtime.v1"]);
ws.onopen = () => ws.send(JSON.stringify({ type: "Authenticate", protocolVersion: 1, ticket }));
ws.onmessage = (event) => {
  const frame = JSON.parse(event.data as string);
  if (frame.type === "Ready") {
    // Join gönderin, frame.sessionEpoch'u Input çerçeveleri için saklayın.
  }
};

Sıradaki adım

Bağlantı koptuğunda imleç ve epoch ile nasıl devam edeceğiniz, limitler ve sıkıştırma için yeniden bağlanma ve limitler.

Yeniden bağlanma ve limitler

Bu sayfa, realtime soketi koptuğunda durumun nasıl kurtarıldığını, ResyncRequired geldiğinde ne yapılacağını, boşta kapanma (4100) davranışını ve sunucunun uyguladığı limitleri anlatır. Mobil ya da dengesiz ağda oynanan bir oyun yazıyorsanız bu sayfayı baştan sona okuyun.

Kopma sonrası kurtarma

Soket her zaman kopabilir (ağ, dağıtım, yük dengeleyici). Open Play’de kurtarma yeni soketle yeniden katılma (re-join) ile yapılır; eski soketin durumu sunucuda yeniden kullanılmaz.

  1. Yeni bilet al. Her bağlantı yeni bir bilet ister (tek kullanımlık, 30 sn); eski bilet yeniden kullanılamaz (protokol).
  2. Authenticate, Ready. Yeni sessionEpoch gelir. Yeni soket aynı kaynağa yazmaya başladığında oyuncunun eski soketi artık yazar değildir: eski soketin girdileri Ack: Stale alır.
  3. Her abonelik için yeniden Join.
    • Kalıcı (durable) akışlarda: son aldığınız cursor’ı Join.cursor olarak verin. Sunucu kaldığınız yerden DurableEvent’leri tekrar oynatır ve yeni bir DurableRecovery bariyeri yollar (snapshot boştur).
    • Geçici (ephemeral) durumda: cursor yoktur; Join taze bir Snapshot getirir. Gönderilmiş ama Ack almamış Input’lar tekrar oynatılmaz: sonucu belirsizdir, oyun mantığınız en son Snapshot’a göre devam etmelidir.
    • Kalıcı komutlarda (Command): Ack gelmeden kopan komutun sonucu belirsizdir. Aynı idempotencyKey ile yeniden gönderin ya da sonucu sorgulayın (eklentileri çağırma); yeni anahtar üretmeyin.
  4. ResyncRequired gelirse (imleç yok, süresi dolmuş, başka oyuncuya ait, boşluklu ya da tekrar bütçesini aşmış) imleci atın ve cursor olmadan tekrar Join edin; bu taze bir anlık görüntü getirir. Aynı imleçle ısrar etmeyin.

İmleç (cursor) kuralları

  • Opaktır ve oyuncuya bağlıdır; başka oyuncunun imleci kullanılamaz. İçeriğini ayrıştırmayın.
  • Her DurableRecovery ve her DurableEvent yeni bir imleç taşır; en sonuncusunu saklayın.
  • Tek kurtarmada en çok 256 kalıcı olay tekrar oynatılır; fazlası ResyncRequired ile taze anlık görüntüye çevrilir.
  • İmleç ve teslimat saklama penceresi 24 saattir; ondan eski imleç ResyncRequired alır.
  • coveredEventIds’deki olaylar taze anlık görüntüde zaten vardır; bunları ayrıca uygulamayın.

TypeScript SDK ne yapar

SDK bu akışı sizin yerinize yürütür (RealtimeClient, client.realtime):

DavranışVarsayılanAyar
Otomatik yeniden bağlanmaaçıkautoReconnect
Deneme gecikmesimin(reconnectBaseMs · 2^(n−1), 30 000) ms + [0, reconnectBaseMs) jitterreconnectBaseMs: 1000 (100 ile 10 000 arası)
En çok deneme8maxReconnectAttempts: 0 ile 20 arası
Kimlik doğrulama zaman aşımı5 snauthTimeoutMs (en çok 5 sn)
Uygulama Ping aralığı15 snheartbeatMs: 10 ms ile 60 sn
Kalıcı komut zaman aşımı15 sncommandTimeoutMs: 1 ms ile 120 sn
Cevap bekleyen girdi penceresi4maxPendingInputs: 1 ile 8
Çerçeve boyutu16 KiBmaxFrameBytes (en çok 16 KiB)

Durum makinesi idle, ticketing, connecting, authenticating, ready, recovering, live, backoff, reauthRequired, upgradeRequired, idleClosed ve closed değerlerinden geçer. Önemli olanlar:

  • recovering / backoff: SDK yeniden bağlanıyor; aboneliklerin imleçleri korunur.
  • reauthRequired: bilet ya da kimlik reddedildi (AuthenticationFailed, 401/403). Oturumu yeniden kurun; SDK kendiliğinden sonsuz döngüye girmez.
  • upgradeRequired: sunucu protokol sürümünü ya da çerçeveyi tanımadı (UnsupportedProtocol, 426). İstemciyi güncelleyin.
  • Sunucu PayloadTooLarge ya da CapacityExceeded gönderirse SDK bunu geri basınç (backpressure) hatası olarak yüzeye çıkarır; bağlantı durumunu değiştirmez.

Not

Tamamlanmamış girdiler soket koptuğunda “belirsiz sonuçlu” olarak reddedilir ve yeniden bağlanınca otomatik tekrar gönderilmez. Sinyal (best-effort) mesajları da tekrar edilmez.

Boşta kapanma (4100)

Sunucu, girdi kabul eden (etkileşimli) bir aboneliği olan ve belirli süre oyuncu etkinliği göndermeyen soketi kapatır.

  • Kapanış kodu 4100, neden idle_timeout. Tipli Error çerçevesi yoktur; yalnız WebSocket kapanış çerçevesi.
  • Süre varsayılan 5 dakikadır ve operatör tarafından değiştirilebilir ya da kapatılabilir. Kontrol aralığı min(süre/4, 15 sn) olduğundan kapanış eşikten en çok bu kadar geç gelir.
  • Menüde ya da oyun öncesinde (girdi kabul eden abonelik yokken) sayaç işlemez.
  • Etkinlik sayılan şey eklentinin tanımıdır: girdi çerçevesi genellikle sayılır; uygulama Ping, Authenticate ve protokol düzeyi ping sayılmaz. Oyununuz duraklatılmış ekranda da bağlı kalacaksa eklentinin tanımladığı canlı tutma girdisini gönderin; duraklatma, garaj gibi ekranlarda canlı tutma girdisi göndermek AFK sayılmamasını sağlar.
  • Açık bir soket oturumu yaşatmaz: oturum ömrü ancak oyuncu REST isteği yaptıkça uzar (güvenlik); sunucu soketteki oturumu 25 saniyede bir yeniden doğrular.
  • Bu kapanışı bir hata olarak ele almayın: oyuncuya “Devam et” gösterin, onayında yeni bilet alıp yeniden bağlanın; imleçler sayesinde abonelikler kurtarılır.

SDK’da onIdleClose geri çağrısını verirseniz istemci idleClosed durumuna geçer ve kendiliğinden yeniden bağlanmaz; kullanıcı onayında connect() çağırın. Geri çağrıyı vermezseniz kapanış sıradan bir kopma gibi işlenir ve SDK kendisi yeniden bağlanır.

import { createPlayerClient, type PlayerClient } from "@ferdiunal/open-play-ts";

/**
 * Sunucu boşta kapanışında (`4100`, `idle_timeout`) SDK kendiliğinden yeniden bağlanmaz; oyuncuya "Devam et"
 * gösterin ve onayında `connect()` çağırın. Abonelikler imleçleriyle kurtarılır.
 */
export function createClientWithIdlePrompt(
  baseUrl: string,
  environmentId: string,
  showContinueButton: (onContinue: () => void) => void,
): PlayerClient {
  const client: PlayerClient = createPlayerClient({
    baseUrl,
    environmentId,
    realtime: {
      reconnectBaseMs: 250, // ilk yeniden bağlanma denemesi 250 ms (varsayılan 1000)
      onIdleClose: () => showContinueButton(() => void client.realtime.connect()),
    },
  });
  return client;
}

Limitler

LimitDeğerAşılınca
Bilet ömrü30 sn, tek kullanım, Origin’e bağlıAuthenticationFailed
Authenticate bekleme süresi5 snAuthenticationFailed, soket kapanır
Kimlik doğrulama fazı çerçevesi4 KiBPayloadTooLarge, kapanır
Çerçeve (kimlik doğrulama sonrası)16 KiBPayloadTooLarge, kapanır
Taşıma katmanı mesajı64 KiB16 KiB ile 64 KiB arası tipli hata alır; 64 KiB üstü tipli hata olmadan soket hatasıyla düşer
Input yüküen çok 4 KiB ve modülün maxBytes değeriInvalidPayload
Command yüküen çok 16 KiBInvalidPayload
Bağlantı başına çerçeve hızı40 çerçeve/sn ortalama, 4 sn’lik (160 çerçeve) patlama payıCapacityExceeded (retryable: true) ve soket kapanır
Oyuncu başına açık soket4CapacityExceeded (retryable: false), soket kapanır
Adres başına açık soket8CapacityExceeded (retryable: false), soket kapanır (operatör gözlem kipine alırsa yalnız sayılır)
Adres başına yeni bağlantı30/dakikayükseltme reddedilir
Soket başına abonelik32 (sunucu varsayılanı)Join reddedilir
Oturum yeniden doğrulama25 sn’de biroturum iptal edilmişse AuthenticationFailed ve kapanış
Modül kapısı yeniden kontrol2 sn’de bir1008 / 1013 ile kapanış
Kurtarmada tekrar oynatılan olay256ResyncRequired
İmleç saklama24 saatResyncRequired
Boşta kapanma5 dk (varsayılan)4100 idle_timeout

Notlar:

  • Çerçeve hızı bütçesi Input’la sınırlı değildir; alınan her metin çerçevesi ve protokol düzeyi Ping bir çerçeve sayılır (Pong sayılmaz). Dürüst bir istemci yaklaşık 30 ile 35 çerçeve/sn gönderir; 40/sn’nin altında kalın ve bir duraksamadan sonra biriken çerçeveleri toplu göndermeyin.
  • Sayılar sunucu varsayılanlarıdır; operatör bazılarını (boşta süresi, bağlantı sınırları, çerçeve bütçesi) ortam yapılandırmasıyla değiştirebilir. Barındırılan bir Open Play kullanıyorsanız değerleri sağlayıcınıza sorun.
  • Input hızı ayrıca modülün şema belgesindeki ratePerSecond ve burst değerleriyle tanımlıdır.

Sıkıştırma

  • Sunucu permessage-deflate (RFC 7692) sıkıştırmasını destekler, yalnız istemci teklif ederse devreye girer. Tarayıcılar bunu kendiliğinden teklif eder; ayrı bir şey yapmanız gerekmez. Benzer JSON çerçevelerinde belirgin küçülme sağlar.
  • Operatör sıkıştırmayı kapatabilir; kapalıysa soket sıkıştırmasız çalışır, protokol değişmez.
  • Unity yerel (native) WebSocket istemcisinde sıkıştırma henüz yoktur; bu bilinen bir kısıttır. WebGL’de tarayıcı bunu üstlenir (Unity SDK).

Sunucu tarafı canlılık sondası

Sunucu, bağlantı gecikmesini ölçmek için yaklaşık 5 saniyede bir protokol düzeyi WebSocket Ping gönderir. Tarayıcı ve standart istemci kütüphaneleri Pong’u kendiliğinden yanıtlar; elle WebSocket yazıyorsanız Pong yanıtını unutmayın. Bu sonda oyuncu etkinliği sayılmaz.

Sorun giderme

BelirtiOlası nedenÇözüm
Her bağlanışta hemen AuthenticationFailedBilet tekrar kullanıldı ya da 30 sn geçti; Origin bilet isteğinden farklıHer bağlantıda yeni bilet alın; bileti aldığınız origin’den bağlanın
CapacityExceeded ile kapanmaÇerçeve hızı ya da bağlantı sınırıÇerçeve gönderim hızını düşürün; eski sekmeleri kapatın (oyuncu başına 4 soket)
ResyncRequired döngüsüEski imleçle ısrarİmleci atın, cursor olmadan Join edin
5 dakika sonra kopmaBoşta kapanma (4100)“Devam et” akışı kurun ya da canlı tutma girdisi gönderin
Yükseltme 401Sorgu dizesi var, alt protokol istenmedi ya da Origin izinli değilProtokol: soketi aç

Daha fazlası için sorun giderme.

Eklentilere genel bakış

Bu sayfa, Open Play’de eklenti (plugin) kavramını açıklar: oyuna özgü komutları, sorguları ve gerçek zamanlı modülleri çekirdeğe oyun adı eklemeden sunan paketler. Çekirdek modüllerin (karakter, ekonomi, mağaza…) ötesinde bir oyunun mantığına nasıl bağlandığınızı ve neyin bugün desteklenmediğini burada görürsünüz.

Eklenti nedir

Bir eklenti, Open Play sunucusunun içinde çalışan ve şu üç yüzeyi tanımlayan bir pakettir:

YüzeyNe verirNasıl çağrılır
Komut (command)Durumu değiştiren, kalıcı ve tekrar-güvenli işlem (ör. ödül al, teslimatı tamamla)HTTP POST ya da realtime Command çerçevesi: eklentileri çağırma
Sorgu (query)Salt-okunur okuma (ör. aktif teslimat, katalog)HTTP GET ya da POST
Realtime modülüAnlık girdi ve durum yayını (hareket, oda, sahne)/v1/realtime/ws: realtime protokolü

Her eklenti bir şema belgesi (artifact) yayınlar: hangi komut, sorgu ve modülün hangi girdi ve çıktı şemasıyla var olduğunu, hangi kimlikle çağrılabildiğini ve yeniden deneme kuralını tarif eder. SDK’ların tipli bağlamaları bu belgeden üretilir: bağlamalar.

Komutlar tek bir veritabanı işleminde (transaction) kesinleşir: komutun etkisi, sonucu, olayı ve tekrar kaydı birlikte yazılır ya da hiç yazılmaz. Gerçek zamanlı geçici durum ise yalnız hız için tutulur ve ödeme, XP, eşya ya da ödül gibi kalıcı sonuçların otoritesi değildir.

Bugünkü durum: yalnız güvenilir, derlenmiş eklentiler

Uyarı

Üçüncü taraf eklenti yükleme henüz yok. Eklentiler, Open Play sunucusunun kendisine derlenmiş (Rust) kodlardır ve güvenilir kod sayılır. Bir oyun geliştiricisi olarak kendi eklentinizi paneldan ya da API’den yükleyemezsiniz; yalıtılmış (sandbox) çalıştırma, kaynak ölçümü ve imzalı dağıtım gelecekteki bir tasarımdır ve bugün çalışmaz.

Kendi oyun mantığınızı bugün şu yollarla bağlarsınız:

  • Kurallar (panelde Kurallar ekranı): karakter olaylarına bağlı otomasyonlar. Bkz. oyun kuralları.
  • Webhook’lar: Open Play olaylarını kendi sunucunuza bildirir. Bkz. webhook rehberi.
  • Kendi sunucunuz + sunucu anahtarı: kendi mantığınızı kendi sunucunuzda çalıştırıp çekirdek REST uçlarını çağırın. Bkz. uç nokta referansı.

Depoda gelen eklentiler:

EklentiSürümNe içinYüzey
package-ride1.44.0Teslimat oyunu: sürüş, görevler, yarış bahisleri, sahne36 komut, 33 sorgu, 3 realtime modülü
arena1.0.0Minimal örnek: tek hareket modülü0 komut, 0 sorgu, 1 realtime modülü

Bunlar sunucuyla birlikte gelir; sizin ortamınızda hangilerinin etkin olduğunu panelde Eklentiler ekranından görürsünüz (Eklentiler).

Ortam başına durum

Bir eklenti, her ortam (environment) için ayrı ayrı bir durumda bulunur:

DurumKomutlarSorgular
Etkin (Enabled)kabul edilirkabul edilir
Salt okunur (ReadOnly)reddedilirkabul edilir
Boşaltılıyor (Draining)yeni komut reddedilir, süren işler biterkabul edilir
Kapalı (Disabled)reddedilirreddedilir

Reddedilen çağrı 403 ve forbidden alır (hatalar). Aynı sonuç, ortama verilen eklenti sürümü sunucudaki sürümle uyuşmadığında da görülür: sunucu yeni bir eklenti sürümüyle güncellendiyse ortamın eklenti yetkisi (grant) de o sürüme geçirilmelidir. Beklenmedik 403 forbidden alıyorsanız önce panelde eklentinin durumuna ve sürümüne bakın.

Ek olarak ürün modülü plugins kapalıysa tüm /v1/plugins/... ve realtime uçları 404 ve module_disabled döner (Oyun ayarları: modüller).

Kimlikle erişim

Her işlemin şema belgesinde bir access değeri vardır:

accessKim çağırır
playerOyuncu oturumu (çerez ya da Bearer oturum jetonu)
serverYalnız sunucu anahtarı; oyuncu istemcisinde görünmez
publicKimlik gerektirmeyen, yalnız GET sorgusu (yalnız x-crx-env gerekir)

Belirtilmemiş ya da bilinmeyen değer güvenli tarafa (player) düşer. İstemciye gömülen (client) API anahtarıyla eklenti çağrılamaz: 401 alır. Ayrıntı: eklentileri çağırma.

Sıradaki adım

Eklenti yazmak

Bu sayfa, Open Play’e yeni bir eklenti (plugin) yazmak isteyenler içindir: eklentinin parçalarını, küçük çalışan bir örneği, sürüm yükseltirken izlenecek güvenli sırayı ve gerçek olaylardan çıkan yaygın hataları gösterir. Eklentiyi kullanmak için eklentilere genel bakış, eklentileri çağırma ve bağlamalar sayfalarına bakın.

Bu sayfa kimin için

Uyarı

Barındırılan (hosted) stüdyolar bugün eklenti kodu yükleyemez. Eklentiler Open Play sunucusunun içine derlenen Rust kodudur ve güvenilir kod sayılır. Sandbox’lı çalıştırma, kaynak ölçümü ve imzalı dağıtım henüz yoktur. Bu rehber, Open Play’i kendi sunucusunda çalıştıran (self-hosted) ekipler ve Open Play’e katkı verenler içindir.

Kendi sunucunuzda kod derleyemiyorsanız oyun mantığınızı bugün şu yollarla bağlarsınız: panelde oyun kuralları, webhook’lar ya da kendi sunucunuz + sunucu anahtarı (uç nokta başvurusu).

Eklentinin anatomisi

Bir eklenti, tek bir Rust crate’idir (örneğin crx-plugin-arena, crx-plugin-work-items, crx-plugin-package-ride). Parçalar şunlardır:

ParçaNe işe yararNerede tanımlanır
ManifestEklentinin kimliği (PluginId), sürümü (PluginVersion), host API major’ı, sağladığı ve gerektirdiği eklentiler, izinleri (PluginPermission), yapılandırma şeması, mesajları, migration özeti ve gerçek zamanlı modülleriOpenPlayPlugin::manifest
Tipli mesajlarKomut/sorgu girdisi ve çıktısı; her biri tek yönlü (CommandInput, CommandOutput, QueryInput, QueryOutput, gerçek zamanlı girdi/durum, olay), katı serde tipiPluginMessage uygulayan struct’lar
KomutDurumu değiştiren, kalıcı ve tekrar oynatması güvenli işlem. Karar fonksiyonu saftır: I/O, saat, rastgelelik yokturTypedCommand::decide, OpenPlayPlugin::register
SorguSalt-okunur okuma; yalnız girdi/çıktı kodekleri kaydedilirOpenPlayPlugin::register_queries
Artifact (şema belgesi)İstemci SDK’larının ve bağlamaların okuduğu plugin.schema.json: şemalar, işlem tablosu, modüller, schemaSha256OpenPlayPlugin::artifact, PluginArtifact::from_manifest
Gerçek zamanlı modül (isteğe bağlı)Sunucu-otoriter canlı girdi/durum indirgeyicisi (reducer)OpenPlayPlugin::register_realtime, TypedRealtimeModule
DispatcherKomutun veritabanına gerçekten yazıldığı, sorgunun okunduğu yer; sunucudaki ortak işlem sınırını (PluginUnitOfWork) kullanırPluginCommandDispatcher, PluginQueryDispatcher, PluginCommandResultLookup
Panel yüzeyi (isteğe bağlı)Panelde bildirimsel yönetim ekranları, özel bileşenler, oyun kuralı kataloğu, leaderboard bildirimiadmin_surface, ui_components, game_rule_catalog, leaderboards

Sorumluluk bölümü önemlidir: sunucu (host) idempotency’yi, iyimser kilidi (optimistic lock), tek veritabanı işlemini, olay (event) ve outbox yazımını, makbuzu (receipt) ve yetki kontrolünü yapar. Eklenti yalnızca mesajları, saf kararı ve yazılacak kayıtların planını (PluginMutationPlan) sağlar. Kalıcı sonuç (ödeme, XP, eşya, ödül) her zaman komut yolundan geçer; gerçek zamanlı durum yalnız hız içindir ve ekonomik etki üretemez.

Küçük örnek: “hello counter”

Aşağıdaki örnek, en küçük gerçek eklentilerin (crx-plugin-arena, crx-plugin-scene) kalıbını izler: bir komut (increment) ve bir sorgu (getCounter). Kod parçaları depodaki docs/developer/examples/rust/ dosyalarından birebir alınmıştır ve crx-plugin-arena crate’inin bir testinde gerçekten derlenip kayıt defterine kaydedilir; bu yüzden sayfadaki kod eskiyemez.

Üç parça aynı modülün içindedir (birinde yapılan use, diğerlerinde de görünür).

1. Mesajlar

Her mesaj kimliği eklenti kimliğiyle başlar (hello-counter. ön eki; aksi hâlde kayıt reddedilir). int64 değerler için PluginI64 kullanın: tel üzerinde ondalık metin olarak gider (sayılar ve kodlama). deny_unknown_fields bilinmeyen alanı reddeder.

#![allow(unused)]
fn main() {
// Mesajlar: her biri tek bir yönü olan, katı (deny_unknown_fields) serde tipleridir.
use crx_plugin::{MessageDirection, PluginI64, PluginMessage};
use serde::{Deserialize, Serialize};

pub const PLUGIN_ID: &str = "hello-counter";

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IncrementInput {
    pub by: i32,
}
impl PluginMessage for IncrementInput {
    const MESSAGE_ID: &'static str = "hello-counter.increment.input";
    const SCHEMA_VERSION: u32 = 1;
    const DIRECTION: MessageDirection = MessageDirection::CommandInput;
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IncrementOutput {
    pub value: PluginI64, // int64 tel üzerinde ondalık metindir
}
impl PluginMessage for IncrementOutput {
    const MESSAGE_ID: &'static str = "hello-counter.increment.output";
    const SCHEMA_VERSION: u32 = 1;
    const DIRECTION: MessageDirection = MessageDirection::CommandOutput;
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GetInput {}
impl PluginMessage for GetInput {
    const MESSAGE_ID: &'static str = "hello-counter.get.input";
    const SCHEMA_VERSION: u32 = 1;
    const DIRECTION: MessageDirection = MessageDirection::QueryInput;
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GetOutput {
    pub value: PluginI64,
}
impl PluginMessage for GetOutput {
    const MESSAGE_ID: &'static str = "hello-counter.get.output";
    const SCHEMA_VERSION: u32 = 1;
    const DIRECTION: MessageDirection = MessageDirection::QueryOutput;
}
}

2. Eklenti: manifest, komut, sorgu

Komutun karar fonksiyonu decide eşzamansız değildir ve I/O yapmaz; yalnızca girdiyi doğrular ve sonucu üretir. Zaman ve tohum gerekiyorsa dispatcher bunları host’tan alır ve olaya yazar (eval_now_ms, eval_seed); decide içinde SystemTime veya rand kullanılmaz.

#![allow(unused)]
fn main() {
// Eklenti: manifest + komut/sorgu kaydı. Kalıcı yazım burada değil, dispatcher'ın mutasyon planındadır.
use crx_application::{
    plugin_platform::{OpenPlayPlugin, PluginRegistryError},
    plugin_runtime::{PluginRegistrar, PluginRuntimeError, TypedCommand},
};
use crx_plugin::{ManifestError, PluginArtifact, PluginId, PluginManifest, PluginPermission, PluginVersion};
use serde_json::json;

pub struct IncrementCommand;
impl TypedCommand for IncrementCommand {
    type Input = IncrementInput;
    type Output = IncrementOutput;

    // Saf ve eşzamansız olmayan karar: yalnız doğrular, I/O yapmaz.
    fn decide(&self, input: &Self::Input) -> Result<Self::Output, PluginRuntimeError> {
        if !(1..=100).contains(&input.by) {
            return Err(PluginRuntimeError::HandlerRejected);
        }
        Ok(IncrementOutput { value: PluginI64::new(i64::from(input.by)) })
    }
}

pub struct HelloCounterPlugin;

fn plugin_id() -> PluginId {
    PluginId::try_new(PLUGIN_ID).expect("static plugin id")
}

impl OpenPlayPlugin for HelloCounterPlugin {
    fn manifest(&self) -> Result<PluginManifest, ManifestError> {
        PluginManifest::new(
            plugin_id(),
            PluginVersion::new(1, 0, 0),
            1,                       // host API major
            vec![plugin_id()],       // provides
            vec![],                  // requires
            vec![PluginPermission::Outbox],
            json!({"type":"object","additionalProperties":false,"properties":{}}),
            vec![
                IncrementInput::descriptor().expect("descriptor"),
                IncrementOutput::descriptor().expect("descriptor"),
                GetInput::descriptor().expect("descriptor"),
                GetOutput::descriptor().expect("descriptor"),
            ],
            "0".repeat(64),          // migration checksum (64 onaltılık karakter)
            vec![],                  // realtime modülleri
        )
    }

    fn register(&self, registrar: &mut PluginRegistrar) -> Result<(), PluginRuntimeError> {
        registrar.register_command(IncrementCommand)?;
        Ok(())
    }

    fn register_queries(&self, registrar: &mut PluginRegistrar) -> Result<(), PluginRuntimeError> {
        registrar.register_query::<GetInput, GetOutput>()?;
        Ok(())
    }

    fn artifact(&self) -> Result<Option<PluginArtifact>, PluginRegistryError> {
        Ok(Some(plugin_artifact()?))
    }
}
}

3. Artifact

Artifact, manifestteki her mesajı bir işleme veya modüle bağlamak zorundadır; fazla ya da eksik mesaj kayıtta hata verir. Komutlarda idempotency_required: true, pozitif bir makbuz saklama süresi ve Read olmayan bir retry politikası zorunludur; sorgularda tersi geçerlidir.

#![allow(unused)]
fn main() {
// Artifact: SDK'nın ve bağlamaların okuduğu şema belgesi; manifestle birebir eşleşmek zorundadır.
use std::collections::BTreeMap;

use crx_plugin::{
    MessageId, PluginAccess, PluginArtifactSpec, PluginHttpMethod, PluginJournalPolicy,
    PluginOperationDefinition, PluginOperationKind, PluginRetryPolicy,
};

fn operation(
    name: &str,
    kind: PluginOperationKind,
    request: (&str, &str),  // (şema adı, mesaj kimliği)
    response: (&str, &str),
) -> PluginOperationDefinition {
    let is_command = kind == PluginOperationKind::Command;
    let segment = if is_command { "commands" } else { "queries" };
    PluginOperationDefinition {
        name: name.to_owned(),
        kind,
        path: format!("/v1/plugins/{PLUGIN_ID}/{segment}/{}", request.1),
        method: if is_command { PluginHttpMethod::Post } else { PluginHttpMethod::Get },
        request_schema: request.0.to_owned(),
        response_schema: response.0.to_owned(),
        schema_version: 1,
        retry: if is_command { PluginRetryPolicy::SameKeyCommand } else { PluginRetryPolicy::Read },
        journal: if is_command { PluginJournalPolicy::Full } else { PluginJournalPolicy::None },
        resource: None,
        result_path: is_command
            .then(|| format!("/v1/plugins/{PLUGIN_ID}/commands/{}/result", request.1)),
        success: Some(200),
        access: PluginAccess::Player,
        request_message_type: MessageId::try_new(request.1).expect("message id"),
        response_message_type: MessageId::try_new(response.1).expect("message id"),
        receipt_retention_seconds: is_command.then_some(86_400),
        idempotency_required: is_command,
        deprecated: None,
    }
}

pub fn plugin_artifact() -> Result<PluginArtifact, PluginRegistryError> {
    let value = json!({
        "type":"object","additionalProperties":false,
        "properties":{"value":{"type":"integer","format":"int64"}},
        "required":["value"]
    });
    let schemas = BTreeMap::from([
        ("IncrementInput".to_owned(), json!({
            "type":"object","additionalProperties":false,
            "properties":{"by":{"type":"integer","format":"int32","minimum":1,"maximum":100}},
            "required":["by"]
        })),
        ("IncrementOutput".to_owned(), value.clone()),
        ("GetInput".to_owned(), json!({"type":"object","additionalProperties":false,"properties":{}})),
        ("GetOutput".to_owned(), value),
    ]);
    let spec = PluginArtifactSpec {
        schemas,
        commands: BTreeMap::from([("increment".to_owned(), operation(
            "increment",
            PluginOperationKind::Command,
            ("IncrementInput", IncrementInput::MESSAGE_ID),
            ("IncrementOutput", IncrementOutput::MESSAGE_ID),
        ))]),
        queries: BTreeMap::from([("getCounter".to_owned(), operation(
            "getCounter",
            PluginOperationKind::Query,
            ("GetInput", GetInput::MESSAGE_ID),
            ("GetOutput", GetOutput::MESSAGE_ID),
        ))]),
        modules: BTreeMap::new(),
    };
    let manifest = HelloCounterPlugin.manifest().map_err(PluginRegistryError::Manifest)?;
    Ok(PluginArtifact::from_manifest(&manifest, spec)?)
}
}

Örneğin yapmadığı şey: kalıcı yazım

IncrementCommand::decide yalnızca girdiyi doğrular. Sayacı gerçekten artıran kod, dispatcher’dadır. Dispatcher şunları yapar (gerçek örnek: crx-plugin-work-items, prepare_create ve prepare_claim):

  1. PluginRegistry::prepare_command ile çağrıyı yetkilendirir, girdiyi katı çözer ve parmak izini (fingerprint) çıkarır.
  2. Hedef kaydı okur (PluginRecordQuery), iyimser kilit için sürümünü kontrol eder.
  3. Bir PluginMutationPlan kurar: yazılacak kayıtlar (PluginRecordWrite, beklenen sürüm ve indeksleriyle) ve eklenecek olaylar (PluginEventAppend).
  4. Planı PluginUnitOfWork ile tek veritabanı işleminde uygular; sonuç ancak commit’ten (ya da aynı anahtarın makbuzundan) sonra döner.

Eklentiler kayıtlarını genel plugin_records (ve indeks, olay, makbuz) tablolarına yazar; eklenti başına özel SQL tablosu çoğunlukla gerekmez. Özel SQL gerekirse eklemeli bir migration ile ve eklentinin kendi migration alanında açılır.

Not

Bugün sunucuda komut yönlendirmesi eklenti başına dispatcher’larla yapılır ve bileşim kökünde (bin/crx-api) bağlanır. Komutu olan yeni bir eklenti, kendi dispatcher’ını ve bu bağlamayı da gerektirir; yalnız manifest + register yetmez. Yalnız realtime modülü olan eklentilerde (crx-plugin-arena, crx-plugin-scene) bu adım yoktur.

Sunucuya kaydetmek

Eklenti crate’i, bin/crx-api (ve zamanlayıcı/outbox işi varsa bin/crx-worker) bağımlılıklarına eklenir ve açılışta kayıt defterine verilir:

#![allow(unused)]
fn main() {
let mut registry = PluginRegistryBuilder::new();
registry.register_plugin(HelloCounterPlugin)?;
let registry = registry.freeze()?;
}

freeze kayıt defterini tüketip değiştirilemez hâle getirir. Tekrarlanan eklenti/mesaj/modül, eksik ya da uyumsuz bağımlılık, desteklenmeyen host API major’ı, karışık kodek ve ad alanı ihlali açılışı durdurur; hata çalışma zamanında değil, derlemeden hemen sonra görünür.

Artifact, sabitleme (pin) ve release lock

schemaSha256, artifact’taki şemaların kanonik JSON özetidir. Şemalar değişirse özet değişir; yalnız sürüm metni değişirse değişmez. İstemciler bu özeti bağlamalarına sabitler (bağlamalar).

Depoda üç yer aynı kaynaktan, aynı değişiklikte güncellenir:

  1. artifacts/plugins/<eklenti>.plugin.schema.json: Rust’tan dışa aktarılır (PluginArtifact::to_pretty_json; örnek: crx-plugin-arena içindeki export_arena_artifact).
  2. artifacts/plugins/release-lock.json: eklenti başına artifact yolu, üretilmiş bağlama yolu, tam sürüm, schemaSha256, protokol major’ı, kodek profili ve modül kimlikleri.
  3. Üretilmiş istemci bağlaması: elle düzenlenmez, üreticiyle yenilenir.

Bir test, “depodaki JSON’un Rust’ın ürettiğinin tam kopyası olduğunu” doğrular (checked_in_artifacts_are_exact_rust_exports, crx-plugin-arena). Pin uyuşmazlığı yayını durdurur; eski özeti kabul eden bir uyumluluk katmanı eklenmez. Yeniden üretin.

Oyun istemcileri için bağlama üretmek

Oyun geliştirici, eklentinin artifact’ını alıp kendi bağlamasını üretir ve özeti bilinçli olarak sabitler:

npx crx plugins generate \
  --input hello-counter.plugin.schema.json \
  --schema-sha256 <artifact içindeki schemaSha256> \
  --output src/generated/hello-counter.ts

CI’da --check ile dosyanın güncelliği doğrulanır. Ayrıntı ve Unity sınırı: bağlamalar. Unity için hazır bağlama bugün yalnız package-ride eklentisi içindir.

Sürümleme ve güvenli yayın

Ortamın eklenti yetkisi (grant) tam sürüm ile eşleşmelidir. Sunucuda yeni bir eklenti sürümü çalışırken ortamın yetkisi hâlâ eskisini gösteriyorsa, o ortamda eklenti ve gerçek zamanlı çağrıları reddedilir (403 forbidden; şema ucu 404). Bu, gerçekte yaşanmış bir olaydır: sürüm artırıp yetkiyi yeni sürüme geçirmeyi unutmak.

Sürüm yükseltme sırası:

  1. Kapılar: eklentinin testleri, artifact tam eşleşme testi ve cargo xtask check yeşil olmalı; migration varsa up + down çalışmalı.
  2. Drain (boşaltma): ortamın eklenti yetkisini Enabled → Draining yapın. Yeni komut ve girdi hemen reddedilir. En az drain asgarisi kadar bekleyin: manifestteki en uzun gerçek zamanlı durum saklama süresi + 15 sn; gerçek zamanlı modülü olmayan eklentide 15 sn.
  3. İkili: yeni sunucu imajını (varsa migration ile) dağıtın; migration’lar eklemeli (additive) olmalı.
  4. Yetkiyi yeni sürüme geçirin: panelde Sürüme geç (Eklentiler). Atlanırsa yeni ikili eklenti çağrılarını reddeder.
  5. İstemci: yeni bağlama ve (gerekirse) SDK ile oyun istemcisini yayınlayın.
  6. Doğrulama: komut sonucu, makbuz tekrar oynatma ve reddedilen çağrı sayılarını izleyin.

Geri alma kuralları: eklenti yeni sürümde henüz yazı üretmediyse Draining → önceki uyumlu ikili ve yetki. Yazı üretildikten sonra kör ikili geri alma yapılmaz: yazmaları ReadOnly ya da Disabled ile durdurun, makbuz/olay kayıtlarıyla durumu doğrulayın ve ileri düzeltme (forward-fix) sürümü çıkarın.

Ortam başına açmak

Eklenti, sunucuya derlenmiş olması yetmez; her ortamda ayrı bir yetki satırı gerekir. Panelde Eklentiler ekranından eklentiyi oyuna bağlarsınız; durumlar Enabled, ReadOnly, Draining, Disabled’dır (durumların anlamı: eklentilere genel bakış). Eklentinin izni başka bir ürün modülüne bağlıysa (örneğin leaderboard izni Competition modülüne), o modül kapalıyken geçiş 409 plugin_requires_module verir. Kanarya için önce ayrı bir test ortamında ReadOnly, sonra Enabled yapıp gözlemleyin.

Panel entegrasyonu

Eklentinin panelde ekran sunması isteğe bağlıdır ve eklentiye özel panel kodu gerektirmez:

  • admin_surface: bildirimsel yönetim yüzeyi. Yalnız server erişimli operatör işlemlerine bağlanır ve kayıtta manifest ve artifact’a karşı doğrulanır. Yüzey artifact’ın parçası değildir: oyunculara ve SDK’ya sunulmaz, schemaSha256’yı, sürümü ve yetkiyi değiştirmez.
  • ui_components: eklentinin include_str! ile derlemeye gömdüğü tek dosyalık HTML bileşenleri (izole çerçevede çalışır). Bildirimdeki özet, varlıktan hesaplanır; uyuşmazlık kayıtta reddedilir.
  • game_rule_catalog: panelden ayarlanabilir oyun kuralı değerleri (oyun kuralları).
  • leaderboards: eklentinin core leaderboard’a puan yazacağı tabloların bildirimi (PluginPermission::Leaderboard gerekir).

Yüzey kaydı için eklentinin bir artifact’ı olmalıdır; artifact’sız yüzey kayıtta reddedilir.

Testler

En az şunları ekleyin:

  • Manifest ↔ artifact tam eşleşmesi ve şema özeti kayması (PluginArtifact::validate_against).
  • Depodaki artifact JSON’u ve release-lock.json satırı Rust çıktısıyla aynı mı.
  • int64 kodeği, sınırlı girdi (uzunluk, aralık) ve reddedilen sınır değerleri.
  • Tek dondurulmuş kayıt defterinde birden çok eklenti (PluginRegistryBuilder; örnek: crx-plugin-arena/tests/two_game_contract.rs).
  • Kalıcı komutlarda gerçek PostgreSQL ile işlem testi: aynı komutun tekrarında tek uygulama, sürüm çakışması, makbuz (örnek: crx-infra-postgres/tests/plugin_uow_d1_integration.rs).

Kapanışta cargo xtask check ve izole bağımlılıklı tam kapı çalıştırılır; yalnız --lib koşusu tam kanıt sayılmaz.

Kontrol listesi

  • Eklenti kimliği küçük harf ve kısa çizgiyle; her mesaj eklenti. ön ekli.
  • Tüm girdi/çıktı tipleri deny_unknown_fields; sınırlar şemada açık; int64 için PluginI64.
  • Karar fonksiyonu saf (I/O, saat, rastgelelik yok); kalıcı sonuç dispatcher’ın planında.
  • Komutlar idempotent ve makbuz süreleri tanımlı; sorgular salt-okunur ve erişimi (player, server, public) açık.
  • Artifact, depodaki JSON, release-lock.json ve üretilmiş bağlama aynı değişiklikte güncellendi.
  • Kayıt bin/crx-api (gerekirse bin/crx-worker) içinde yapıldı; açılış freeze ile geçiyor.
  • Yayın sırası uygulandı: drain, ikili, yetkiyi yeni sürüme geçirme, istemci.
  • Ortam yetkisi açık ve durumu doğru.

Sık yapılan hatalar

HataBelirtiDoğrusu
Sürüm artırıldı, yetki geçirilmediEklenti ve gerçek zamanlı çağrılar 403 forbidden, şema ucu 404Drain, ikili, sonra Sürüme geç
Şema değişti, artifact/pin yenilenmediArtifact tam eşleşme testi kırmızı; istemcide schemaSha256 uyuşmazlığıArtifact’ı Rust’tan yeniden üretin; release lock ve bağlamayı aynı değişiklikte güncelleyin
Belirsiz sonuçlu komut yeni anahtarla tekrar gönderildiAynı işlem iki kez uygulanırAynı Idempotency-Key ve aynı gövde; emin değilseniz önce sonuç sorgusu (çağırma)
int64 JSON sayısı olarak tanımlandıBüyük değerlerde kayıp ya da kodek hatasıPluginI64; tel üzerinde ondalık metin
decide içinde saat, rastgelelik veya ağTekrar oynatma farklı sonuç verirZaman ve tohum host bağlamından gelir; kalıcı etki dispatcher planında
Ekonomik sonuç gerçek zamanlı reducer’daRedis kaybında sonuç kaybolurKalıcı sonuç komut yoluna
Artifact’ta olmayan ya da fazla mesajAçılış freeze/kayıt hatasıManifest ve artifact aynı Rust kaynağından türesin
Komut için istemci API anahtarı401Oyuncu oturumu ya da sunucu anahtarı
Ayrı dispatcher bağlanmadan komut eklendiKomut yönlendirilemezDispatcher yazın ve bileşim kökünde bağlayın

Bilinen sınırlar

  • Üçüncü taraf eklenti yüklemek, sandbox’lı çalıştırma ve imzalı dağıtım yoktur.
  • Depo dışında (out-of-tree) eklenti yazmak için ayrı bir SDK yoktur; eklenti, Open Play depo ağacındaki bir crate’tir.
  • Yeni eklenti iskeleti (scaffold) üreten bir komut yoktur; kalıp, mevcut küçük eklentilerden kopyalanır.
  • Komutu olan eklentiler için dispatcher bağlaması elle yapılır.

Sıradaki adım

Eklentileri çağırma

Bu sayfa, bir eklentinin komut ve sorgularını HTTP ile nasıl çağıracağınızı anlatır: başlıklar, sürüm denetimi, tekrar güvenliği, sonuç sorgusu ve hata zarfı. SDK kullanıyorsanız çoğunu SDK sizin yerinize yapar; yine de ne olduğunu bilmek hata ayıklamayı kolaylaştırır.

Not

Eklentiler için ortak bir /openapi.json yoktur; eklentinin kendi şema belgesi (artifact) GET /v1/plugins/{plugin_id}/schema ucundan alınır. Kullanabileceğiniz işlemlerin listesi oradadır (bağlamalar).

Uçlar

Yöntem ve yolAmaç
POST /v1/plugins/{plugin_id}/commands/{message_type}Komutu çalıştırır
GET /v1/plugins/{plugin_id}/commands/{message_type}/resultKomutun sonucunu idempotency anahtarıyla sorgular
GET /v1/plugins/{plugin_id}/queries/{message_type}Sorguyu çalıştırır (parametreler sorgu dizesinde)
POST /v1/plugins/{plugin_id}/queries/{message_type}Sorguyu çalıştırır (parametreler gövdede)
GET /v1/plugins/{plugin_id}/schemaEklentinin şema belgesini verir

{plugin_id} eklenti kimliğidir (ör. package-ride). {message_type} işlemin istek mesaj türüdür (ör. package-ride.progression-missions.login-bonus.input); işlemin adı (claimLoginBonus) değildir. Tam yol ve mesaj türü şema belgesindeki her işlemin path ve requestMessageType alanında yazar. Hangi yöntemin (GET ya da POST) kullanılacağını da işlemin method alanı söyler: bazı sunucu sorguları POST ile çağrılır.

Kimlik

ÇağıranGönderilecek
Oyuncu (çerez)Cookie (__Secure-crx.session) + x-crx-env: <ortam uuid>; değiştirici (POST) isteklerde ayrıca x-csrf-token + __Secure-crx.csrf çerezi
Oyuncu (Bearer)Authorization: Bearer <oturum jetonu> + x-crx-env: <ortam uuid>. Eklenti ucu değiştirici isteklerde Bearer ile de CSRF çiftini (__Secure-crx.csrf çerezi + x-csrf-token) ister; çerezsiz istemci çifti GET /v1/auth/csrf ile alıp kendisi taşır (oturumlar ve CSRF)
SunucuAuthorization: Bearer crx_live_<önek>_<gizli> (sunucu anahtarı). x-crx-env gönderirseniz anahtarın ortamıyla aynı olmalı, aksi halde 401. CSRF ve Origin denetimi uygulanmaz
Herkese açık sorguYalnız x-crx-env; yalnız GET ve yalnız şema belgesinde access: public olan sorgular

crx_ ile başlayan ama sunucu türünde olmayan (istemci) anahtar 401 unauthorized alır. Oyuncu isteklerinde Origin başlığı varsa ortamın izinli kökenlerinden biri olmalıdır (tarayıcı kendisi ekler); aksi halde 403 forbidden. Değiştirici bir çağrı başarılı olduğunda yanıt yeni CSRF çiftini (Set-Cookie ve x-csrf-token başlığı) döndürür; bir sonraki değiştirici istekte bunu kullanın.

Komut çağırma

POST /v1/plugins/package-ride/commands/package-ride.progression-missions.login-bonus.input HTTP/1.1
Host: oyun.example.com
Origin: https://oyun.example.com
Cookie: __Secure-crx.session=<oturum>; __Secure-crx.csrf=<csrf-çerezi>
x-crx-env: 0192a3b4-0000-7000-8000-000000000001
x-csrf-token: <csrf-jetonu>
x-crx-schema-version: 1
Idempotency-Key: 5d1f2c88-4f0b-4c8f-9f1e-9a3f6b0c1d22
If-Match: "3"
Content-Type: application/json

{"expectedVersion":"3"}
HTTP/1.1 200 OK
Content-Type: application/json
x-crx-message-type: package-ride.progression-missions.login-bonus.output
x-crx-schema-version: 1

{"version":"4","rewardMinor":"250","streak":"2","day":"19300","walletBalanceMinor":"1250","claimEventId":"0192a3b4-0000-7000-8000-0000000000c1","ledgerCorrelationId":"0192a3b4-0000-7000-8000-0000000000c2"}
BaşlıkZorunluAnlam
Idempotency-Keykomutlarda evetMantıksal tekrar anahtarı; boş olamaz. Kurallar
x-crx-schema-versionevetGönderdiğiniz yükün şema sürümü (pozitif tam sayı); işlemin schemaVersion değeriyle eşleşmelidir. Sorgularda da zorunludur
If-MatchhayırBeklenen kaynak sürümü (iyimser kilit, optimistic lock). Ondalık tam sayı, tırnaklı ya da tırnaksız; * ve W/ önekli değerler 400 alır. Gerçek sürüm farklıysa 409
Content-Type: application/jsonevetGövde JSON’dur ve işlemin istek şemasına uyar

Not

Gövde alanlarındaki int64 değerler ondalık metin olarak gider ("expectedVersion":"3"), JSON sayısı olarak değil; yanıt da aynı biçimdedir. Ayrıntı: sayılar ve kodlama. int32 ve uint32 alanlar JSON sayısıdır.

Başarılı yanıtın gövdesi, işlemin yanıt şemasına uyan JSON’dur; x-crx-message-type ve x-crx-schema-version yanıt başlıkları çıktı mesajını belirtir.

Aynı anahtarla tekrar

Ağ koparsa ya da yanıtı alamazsanız aynı Idempotency-Key ve aynı gövdeyle tekrar gönderin; sunucu işlemi ikinci kez uygulamaz, kayıtlı sonucu döner.

DurumYanıt
İlk istekİşlenir, 200
Aynı anahtar, aynı gövdeKayıtlı sonuç döner
Aynı anahtar, farklı gövde422 ve idempotency_mismatch
Aynı anahtarlı istek hâlâ işleniyor409 ve idempotency_in_flight (retryable: true); biraz sonra tekrar deneyin
Kayıt süresi doldu (işlemin receiptRetentionSeconds değeri, örnekte 86 400 sn)410 ve idempotency_expired

Yeni bir anahtar üretmek yeni bir işlem demektir; belirsiz sonuçlu bir komutu yeni anahtarla yeniden göndermeyin.

Sonuç sorgusu

Yanıtı hiç alamadığınız bir komutun sonucunu, tekrar göndermeden öğrenin:

GET /v1/plugins/package-ride/commands/package-ride.progression-missions.login-bonus.input/result?idempotency_key=5d1f2c88-4f0b-4c8f-9f1e-9a3f6b0c1d22&output_message_type=package-ride.progression-missions.login-bonus.output&output_schema_version=1 HTTP/1.1
x-crx-env: 0192a3b4-0000-7000-8000-000000000001
Cookie: __Secure-crx.session=<oturum>

Üç sorgu parametresi de zorunludur: idempotency_key, output_message_type ve output_schema_version. Yol, işlemin resultPath alanıyla aynıdır.

YanıtAnlamNe yapmalı
200 + gövdeKomut kesinleşmişti; gövde çıktıdırSonucu kullanın
202 (boş gövde)Henüz işleniyorKısa süre sonra tekrar sorgulayın
404Bu anahtarla kayıt yok: komut hiç uygulanmamışYalnız bu durumda aynı anahtar ve gövdeyle komutu gönderebilirsiniz
422 + reasonKomut işlendi ve iş kuralı tarafından reddedildiGönderme; reason makine kodudur
410Kayıt süresi dolduSonuç bilinmiyor; durumu sorguyla yeniden okuyun

Sorgu çağırma

GET ile parametreler sorgu dizesinde gider. Her değer önce JSON olarak ayrıştırılır; olmazsa metin sayılır; null değeri null olur. Aynı parametreyi iki kez vermek 400 alır.

GET /v1/plugins/package-ride/queries/package-ride.delivery-trip.active.input HTTP/1.1
x-crx-env: 0192a3b4-0000-7000-8000-000000000001
x-crx-schema-version: 1
Cookie: __Secure-crx.session=<oturum>

POST ile aynı alanlar JSON gövdesinde gider. Karmaşık ya da uzun girdiler için POST uygundur; hangi yöntemin geçerli olduğunu işlemin method alanından okuyun. Herkese açık sorgu örneği:

GET /v1/plugins/package-ride/queries/package-ride.world-catalog.get.input HTTP/1.1
x-crx-env: 0192a3b4-0000-7000-8000-000000000001
x-crx-schema-version: 1

Şema ucu

GET /v1/plugins/{plugin_id}/schema eklentinin şema belgesini (JSON) verir (Cache-Control: no-store). Oyuncu oturumu ya da sunucu anahtarı gerekir. Eklenti ortamınız için etkin değilse ya da grant sürümü uyuşmuyorsa 404’tür. Belgenin alanlarını ve schemaSha256 değerinin kullanımını bağlamalar sayfasında bulursunuz.

Hata zarfı

Eklenti uçları JSON hata zarfı döner (çekirdek REST uçlarının düz metin hatalarından farklı olarak; karşılaştırma için hatalar):

{"code":"conflict","message":"version conflict: expected 3, actual 5","retryable":false,"requestId":"0192a3b4-0000-7000-8000-0000000000d1"}

Yanıtta ayrıca x-request-id başlığı bulunur; destek talebinde bu kimliği iletin.

codeHTTPretryableNe zaman
unauthorized401hayırOturum/anahtar yok, geçersiz, istemci anahtarı ya da ortam uyuşmazlığı
forbidden403hayırCSRF ya da Origin reddi; eklenti etkin değil ya da sürüm uyuşmuyor; yasaklı hesap (message: account banned)
module_disabled404hayırplugins modülü kapalı (module alanı modül adını taşır)
not_found404hayırBilinmeyen eklenti ya da mesaj türü; kayıt yok
invalid_argument400hayırEksik/bozuk başlık, bilinmeyen şema sürümü, şemaya uymayan yük, kopya sorgu parametresi
conflict409hayırIf-Match sürümü gerçekle uyuşmuyor
idempotency_in_flight409evetAynı anahtarlı istek hâlâ işleniyor
idempotency_mismatch422hayırAynı anahtar, farklı gövde
unprocessable422hayırİş kuralı reddi; reason alanı makine kodunu taşır
idempotency_expired410hayırKayıt süresi doldu
rate_limited429evetOran sınırı; Retry-After: 60 (hız sınırları)
tenant_unavailable503evetOrtamın yerleşimi geçici kullanılamıyor; Retry-After: 30
internal500evetSunucu hatası

reason alanı yalnız iş kuralı reddinde (unprocessable) bulunur (ör. street_reset_too_early) ve eklentiye özgüdür; anlamı eklentinin belgesindedir. Sürüm uyuşmazlığında (conflict) mesaj beklenen ve gerçek sürümü içerir: durumu yeniden okuyup yeni sürümle tekrar deneyin.

Uyarı

Eklenti uçlarında yasaklı hesap account_banned koduyla gelmez: 403 ve code: "forbidden", message: "account banned" döner. account_banned kodu oyuncu kimlik uçlarına (giriş, kayıt) aittir (hatalar). İstemciniz bu iki durumu ayırt edecekse mesaja değil oturum durumuna bakın.

Örnek: komutu belirsiz sonuca karşı güvenle çağırmak

Aşağıdaki TypeScript örneği, SDK bağlamasıyla komutu hazırlar (tek Idempotency-Key üretilir), gönderir; sonuç belirsizse yeni komut üretmeden sonucu sorgular ve yalnız notFound yanıtında aynı anahtarla yeniden gönderir. demo-game eklentisi bu örnek için hazırlanmış bir kurgudur: gerçek eklentinizde işlem adlarını ve alanları kendi şema belgenizden alırsınız (bağlamalar).

import { CrxError, ErrorKind, type PlayerClient } from "@ferdiunal/open-play-ts";
import { DemoGameBinding } from "./generated/demo-game.js";

/**
 * Günlük hediyeyi alır. `prepare` tek `Idempotency-Key` üretir; ağ hatasında sonuç belirsizse yeni komut üretilmez:
 * önce sonuç sorgulanır, yalnız "kayıt yok" yanıtında AYNI anahtarla yeniden gönderilir.
 */
export async function claimDailyGift(client: PlayerClient, expectedVersion: bigint): Promise<bigint> {
  const demo = client.plugin(DemoGameBinding);
  const command = await demo.commands.claimDailyGift.prepare({ giftKey: "daily" }, { expectedVersion });
  try {
    const output = await command.send();
    return output.walletBalanceMinor; // int64 alanlar bigint gelir
  } catch (error) {
    if (!(error instanceof CrxError) || error.kind !== ErrorKind.UnknownOutcome) throw error;
    const outcome = await command.lookup();
    if (outcome.status === "committed") return outcome.result.walletBalanceMinor;
    if (outcome.status === "notFound") return (await command.resume()).walletBalanceMinor; // aynı anahtar, aynı gövde
    throw error; // pending, rejected ya da expired: kör tekrar yok
  }
}

/** Salt-okunur sorgu: tekrar güvenlidir, anahtar gerekmez. */
export async function hasClaimedToday(client: PlayerClient): Promise<boolean> {
  const demo = client.plugin(DemoGameBinding);
  const status = await demo.queries.getGiftStatus({ includeHistory: false });
  return status.claimedToday;
}

Sıradaki adım

Bağlamalar ve şema sabitleme

Bu sayfa, bir eklentinin şema belgesinden (artifact) tipli istemci kodu üretmeyi ve istemcinizin hangi şema sürümüyle derlendiğini sabitlemeyi (pin) anlatır. Sabitleme sayesinde sunucu eklentiyi güncellediğinde istemciniz sessizce yanlış alanlarla konuşmak yerine uyuşmazlığı fark eder.

Şema belgesi ne içerir

GET /v1/plugins/{plugin_id}/schema ya da depodaki artifacts/plugins/<id>.plugin.schema.json aynı belgedir. Üst düzey alanlar:

AlanAnlam
pluginIdEklenti kimliği (ör. package-ride)
versionEklenti sürümü (ör. 1.44.0)
schemaSha256schemas bölümünün özeti; şema sabitleme anahtarı (aşağıda)
protocolVersionEklenti protokol ana sürümü (şimdilik 1)
codecProfileTel kodlaması: plugin-v1-decimal-int64 (sayılar)
schemasGirdi ve çıktı JSON şemaları (bileşen adı → şema)
queries, commandsİşlem tanımları
modulesRealtime modülleri

Bir işlem tanımı şu alanları taşır:

AlanAnlam
nameİşlem adı; istemci kodunda yöntem adıdır (ör. claimLoginBonus)
kind, method, pathcommand ya da query; HTTP yöntemi; tam yol
requestSchema, responseSchema, schemaVersionGirdi ve çıktı şemaları ve gönderilecek x-crx-schema-version
requestMessageType, responseMessageTypeYoldaki {message_type} ve çıktı mesaj türü
accessplayer, server ya da public
retryread (güvenle tekrar), same-key-command (yalnız aynı anahtarla tekrar), never
journalnone, full ya da reference: SDK’nın komutu yerelde nasıl kaydettiği (yeniden yüklemeden sonra devam için)
idempotencyRequired, receiptRetentionSeconds, resultPathKomutlar için: anahtar zorunluluğu, kayıt süresi, sonuç sorgu yolu
deprecatedVarsa kullanımdan kalkma bilgisi: since, sinceUnixS, replacement (sürümleme)

Realtime modülü tanımı moduleId, input, state, events, signals, authorityMode, deliveryClass, interestPolicy, stateTtlMs ve sequencePolicy taşır; girdi ve durum mesajlarında maxBytes, ratePerSecond ve burst bulunur (protokol).

schemaSha256 nedir

schemaSha256, belgedeki schemas nesnesinin kurallı JSON (anahtarlar sıralı, boşluksuz, UTF-8) çıktısının SHA-256 özetidir (küçük harfli onaltılık, 64 karakter). Şema alanları değişirse özet değişir; sürüm numarası aynı kalsa bile. Bu yüzden “sürüm eşleşiyor” demek yetmez: özet eşleşmelidir.

Özeti kendiniz de yeniden hesaplayıp doğrulayabilirsiniz:

import { createHash } from "node:crypto";

type Json = null | boolean | number | string | Json[] | { [key: string]: Json };

/** Anahtarları özyinelemeli sıralar; sunucudaki kurallı JSON biçimiyle aynı baytları üretir (boşluksuz). */
function canonical(value: Json): Json {
  if (Array.isArray(value)) return value.map(canonical);
  if (value !== null && typeof value === "object") {
    const sorted: { [key: string]: Json } = {};
    for (const key of Object.keys(value).sort()) sorted[key] = canonical(value[key] as Json);
    return sorted;
  }
  return value;
}

/** Şema belgesinin `schemas` bölümünden `schemaSha256` değerini yeniden hesaplar. */
export function schemaSha256(artifact: { schemas: Record<string, Json> }): string {
  return createHash("sha256").update(JSON.stringify(canonical(artifact.schemas)), "utf8").digest("hex");
}

/** Belgenin kendi bildirdiği özet, içeriğiyle uyuşuyor mu? */
export function artifactIsConsistent(artifact: { schemaSha256: string; schemas: Record<string, Json> }): boolean {
  return schemaSha256(artifact) === artifact.schemaSha256;
}

Not

Bu hesap yalnız schemas bölümünü kapsar; işlem listesini (commands, queries) ya da version alanını kapsamaz. Bir işlemin yolu ya da erişim türü değişirse özet değişmeyebilir; bu yüzden bağlamayı yeniden üretirken belgenin tamamının farkına da bakın.

TypeScript bağlaması üretme

  1. Şema belgesini alın ve bir dosyaya kaydedin. Sunucudan: oyuncu oturumuyla ya da sunucu anahtarıyla GET /v1/plugins/{plugin_id}/schema.
  2. Özeti belgeden okuyun (schemaSha256) ve release manifestinize/CI’ya yazın: bu sizin sabitleme değerinizdir.
  3. SDK’nın crx komutuyla bağlamayı üretin:
npx crx plugins generate \
  --input demo-game.plugin.schema.json \
  --schema-sha256 ffc38829f30ea0dd1b829f351f905854a9875806fbc8a2bbd591815c7ee52c2f \
  --output src/generated/demo-game.ts
  1. CI’da üretilmiş dosyanın güncel olduğunu denetleyin; --check dosyayı yazmaz, byte karşılaştırır:
npx crx plugins generate --input demo-game.plugin.schema.json \
  --schema-sha256 ffc38829f30ea0dd1b829f351f905854a9875806fbc8a2bbd591815c7ee52c2f \
  --output src/generated/demo-game.ts --check

Davranış:

  • Girdi belgesi yerel dosyadır; komut ağa çıkmaz ve belgeyi yürütmez.
  • --schema-sha256 zorunludur ve belgedeki schemaSha256 ile aynı olmalıdır; farklıysa üretim reddedilir. Özet otomatik yenilenmez: değişikliği bilinçli onaylarsınız.
  • Çıktı yalnız yeni dosya yazar; var olan dosyayı ezmez (üretimi yenilemek için dosyayı silin). Çıktı klasörü önceden var olmalı ve yolu sembolik bağ içermemelidir (komut yolu gerçek yoluyla karşılaştırır); sembolik bağ ve özel dosya reddedilir; hedef .ts olmalıdır. Girdi dosyası en çok 2 MiB olabilir.
  • Başarıda CRX PLUGINS GENERATE PASS (--check ile CRX PLUGINS CHECK PASS) yazar ve 0 ile çıkar.
  • Hata durumunda komut ayrıntı vermez (yol ve içerik sızmasın diye); tek satır CRX TYPES FAIL: input/version/hash/output reddedildi yazar ve 1 ile çıkar. Önce girdi dosyasını, özeti ve çıktı yolunu kontrol edin.
  • Aynı girdi her zaman aynı bayt çıktısını verir.

Üretilen dosya, şemalardan TypeScript tiplerini (bigint olarak int64), işlem tablolarını, modül tanımlarını ve gömülü artifact + özeti taşır. Kullanım:

import { DemoGameBinding } from "./generated/demo-game.js";

const demo = client.plugin(DemoGameBinding);
await demo.queries.getGiftStatus({ includeHistory: true });
const handle = await demo.commands.claimDailyGift.prepare({ giftKey: "daily" });

access: "server" olan işlemler oyuncu istemcisinde görünmez ve çağrılamaz; bunları sunucunuzdan sunucu anahtarıyla HTTP ile çağırın (eklentileri çağırma). access: "public" sorgular game.publicPlugin(...) ile kimliksiz çağrılır.

Not

Komut tanımı retry: "same-key-command" ise SDK ağ hatasında yalnız aynı anahtarla yeniden dener; journal: "full" ise sayfa yeniden yüklenince yarım kalan komut aynı anahtarla devam ettirilir. SDK kurulumu ve journal seçenekleri TypeScript SDK sayfasındadır.

Sabitleme (pin) ve uyuşmazlık

İstemci iki yerde sürüm uyumunu denetler:

  1. Derleme/başlatma zamanı, yerel: createGameClient ile bağlama kurarken oyununuzun release manifestindeki beklentiyi (pin) verirsiniz. Bağlama içindeki pluginId, schemaSha256, protocolVersion, codecProfile (ve verdiyseniz version) beklentiyle birebir eşleşmezse CrxError (kind: upgrade_required, code: plugin_binding_mismatch) fırlatır; hiçbir istek gitmez. Bu denetim sunucuya sormaz; yalnız elinizdeki bağlamanın beklediğiniz bağlama olduğunu doğrular.
  2. Sunucudaki gerçek belge: TypeScript SDK’sı çalışma zamanında /schema ucunu kendiliğinden çağırmaz. Sunucu eklentiyi güncellediyse istemcinizin eski bağlamasıyla uyuşmazlığı görmek için belgeyi başlangıçta kendiniz sorgulayıp özeti karşılaştırın:
import { DemoGameBinding } from "./generated/demo-game.js";

/**
 * Sunucudaki şema belgesini alır ve elinizdeki bağlamayla karşılaştırır. Uyuşmazsa istemciyi güncelleyin:
 * yeni belgeyi alın, yeni `schemaSha256`'yı manifestinize yazın, bağlamayı yeniden üretin.
 * Çerez oturumu açıkken tarayıcıda çalışır (aynı kökenden, `credentials: "include"`).
 */
export async function serverSchemaMatches(baseUrl: string, environmentId: string): Promise<boolean> {
  const response = await fetch(`${baseUrl}/v1/plugins/${DemoGameBinding.pluginId}/schema`, {
    credentials: "include",
    headers: { "x-crx-env": environmentId },
  });
  if (!response.ok) throw new Error(`şema belgesi alınamadı: HTTP ${response.status}`);
  const live = (await response.json()) as {
    pluginId: string;
    schemaSha256: string;
    protocolVersion: number;
    codecProfile: string;
  };
  return (
    live.pluginId === DemoGameBinding.pluginId &&
    live.schemaSha256 === DemoGameBinding.schemaSha256 &&
    live.protocolVersion === DemoGameBinding.protocolVersion &&
    live.codecProfile === DemoGameBinding.codecProfile
  );
}

Unity SDK’sında bu denetim hazırdır: PluginClient.VerifyServerSchemaAsync() sunucunun belgesini alır ve pluginId, schemaSha256, protocolVersion, codecProfile (ve pin’de verilmişse version) alanlarını bağlamanın pin’iyle karşılaştırır. Uyuşmazlıkta OpenPlayException (ErrorKind.UpgradeRequired, kod plugin_binding_mismatch) fırlatır; pin alanı yok ya da bozuksa ErrorKind.Contract.

using System.Threading.Tasks;
using OpenPlay;
using OpenPlay.PackageRide;

/// <summary>
/// Oyuna girerken sunucudaki eklenti şemasının, pakete gömülü bağlamayla aynı olduğunu doğrular.
/// Uyuşmazlıkta (<c>UpgradeRequired</c>, kod <c>plugin_binding_mismatch</c>) oyuncuyu güncellemeye yönlendirin.
/// </summary>
public static class PluginPinCheck
{
    public static async Task<bool> ServerSchemaMatches(OpenPlayClient client)
    {
        var rides = PackageRideClient.Create(client); // pin: pluginId, schemaSha256, protocolVersion, codecProfile, version
        try
        {
            await rides.Plugin.VerifyServerSchemaAsync(); // GET /v1/plugins/package-ride/schema; yalnız pin alanlarını okur
            return true;
        }
        catch (OpenPlayException ex) when (ex.Kind == ErrorKind.UpgradeRequired)
        {
            UnityEngine.Debug.LogWarning("Eklenti şeması değişti; istemciyi güncelleyin: " + ex.Code);
            return false;
        }
    }
}

Uyuşmazlık olursa istemci ne görür

Sunucu eklentiyi güncelleyip istemciniz eski bağlamayla çağırırsa:

Değişiklikİstemci görür
İşlemin schemaVersion değeri arttı400 invalid_argument (bilinmeyen şema sürümü). x-crx-schema-version eskiyi gönderiyordu
İşlem ya da mesaj türü kalktı404 not_found
Girdi şemasına zorunlu alan eklendi400 invalid_argument (şemaya uymayan yük)
Çıktı şeması değiştiSDK yanıtı bağlamaya göre çözemez; contract türünde hata
Eklenti ortamınız için etkin değil / sürüm yetkisi uyuşmuyor403 forbidden

Çözüm hep aynıdır: güncel şema belgesini alın, bilinçli olarak yeni schemaSha256’yı manifestinize yazın, bağlamayı yeniden üretin ve istemciyi yayınlayın. Eski istemcilerin bir süre daha çalışması gerekiyorsa sunucuda eski şema sürümünün korunup korunmadığını eklenti sağlayıcınıza sorun; bunun garantisi platformda yoktur.

Unity bağlaması

Unity SDK’sında bağlama yalnız package-ride eklentisi için hazır gelir (OpenPlay.PackageRide: PackageRideBinding, PackageRideClient). Başka bir eklenti için Unity bağlaması bugün üretilemez: SDK’nın üretici betiği depo içi bir araçtır ve yayımlanan pakette gelmez. Üretici yalnız access değeri player ya da public olan işlemleri paketler (sunucuya özel işlem pakete girmez) ve yalnız plugin-v1-decimal-int64 kodlamasını kabul eder; int64 alanlar C# long olur.

Kullanım için Unity SDK sayfasındaki PluginCall örneğine bakın.

Sıradaki adım

Hata referansı

Bu sayfa, Open Play’in döndürdüğü her hata türünü, durum kodunu ve gövde biçimini listeler. Hata yönetimi kodu yazarken önce hangi uç ailesinin (çekirdek REST mi, eklenti mi) çağrıldığına bakın: iki ailenin hata gövdesi farklıdır.

Uyarı

Çekirdek REST uçlarının çoğu düz metin (text/plain) hata döner, JSON değil. Yalnız bu sayfada “JSON” diye işaretlenen birkaç hata JSON zarfı taşır. Hata metnini ayrıştırmayın; durum koduna ve (varsa) JSON code alanına göre karar verin. Düz metin mesajlar sürümler arasında değişebilir.

Hata aileleri

AileHangi uçlarGövde
Çekirdek REST/v1/characters…, /v1/wallets…, /v1/store…, /v1/auth/… vb. (uç noktalar)Düz metin; birkaç hata JSON {code, message[, module]}
Eklenti/v1/plugins/…Her zaman JSON {code, message, retryable, requestId[, reason, module]} + x-request-id başlığı
Realtime/v1/realtime/wsError çerçevesi: {type, requestId?, code, retryable} (protokol)
Sohbet soketi/v1/chat/ws{"type":"error","code":"…"} (realtime genel bakış)

Çekirdek REST: durum tablosu

DurumGövde (düz metin, aksi belirtilmedikçe)Ne zamanYeniden dene?
400Hatanın açıklamasıEksik ya da bozuk başlık, geçersiz alan, bilinmeyen komut türü, If-Match biçimiHayır; isteği düzeltin
401unauthorizedAPI anahtarı ya da oyuncu oturumu yok, geçersiz, süresi dolmuş; x-crx-env yok ya da bozuk; yasaklı hesap (ayrım yapılmaz)Hayır; kimliği yenileyin
403permission deniedAnahtar türü yetmiyor (ör. istemci anahtarıyla sunucu ucu), CSRF ya da Origin reddiHayır
403JSON {"code":"account_banned","message":"account banned"}Giriş uçlarında, sahiplik kanıtlandıktan sonra yasaklı hesapHayır; döngü kurmayın
404not foundKaynak yok ya da silinmişHayır
404JSON {"code":"module_disabled","message":"module disabled","module":"store"}Ürün modülü ortamda kapalıHayır; modülü açın (modüller)
409version conflict: expected 3, actual 5If-Match sürümü gerçekle uyuşmuyorDurumu yeniden okuyup yeni sürümle tekrar edin
409already exists: <kaynak>Aynı kimlik/anahtarla kaynak zaten varHayır
409idempotent request in progressAynı Idempotency-Key ile ilk istek hâlâ işleniyorEvet, kısa süre sonra aynı anahtarla
410idempotent request result expiredAnahtarın kayıt süresi (24 saat) dolduYeni anahtarla, durumu önce okuyarak
413Gövdesizİstek gövdesi 2 MiB sınırını aştı (görsel yükleme ucu kendi sınırına tabidir)Hayır
422idempotency mismatchAynı anahtar, farklı içerikHayır; anahtarı yeniden kullanmayın
422unprocessable: <neden>İş kuralı reddi (yetersiz bakiye, stok, limit…)Hayır; durumu değiştirmeden tekrar etmeyin
422rejected: <kod>Eklenti iş kuralı reddi (çekirdek rotalar üzerinden)Hayır
429rate limited ya da rule budget exceeded: <kural>Oran sınırı ya da kural bütçesiEvet, bekleyerek (hız sınırları); Retry-After yoktur
500internal errorBeklenmeyen sunucu hatası; ayrıntı sızdırılmazEvet, sınırlı
503tenant unavailable; başlık Retry-After: 30Ortamın yerleşimi taşınıyor ya da geçici kullanılamıyorEvet, Retry-After sonra

Notlar:

  • Gövde ayrıştırma hatalarında çıkarıcıya göre 415 (yanlış Content-Type) ya da 422 (şemaya uymayan JSON) da görülebilir. Karar için yalnız durum koduna güvenin.
  • 401 ve 403 farkı: kimlik yok ya da çözümlenemiyorsa 401; kimlik geçerli ama bu işleme yetmiyorsa 403. Oyuncu bilet ucunda tüm kimlik hataları 403 döner (realtime protokolü).
  • İstemci anahtarıyla bir sunucu ucunu çağırırsanız 403 permission denied alırsınız; anahtarın bilinmemesi ise 401’dir.

Satın alma (POST /v1/player/store/listings/{listing_id}/buy) hata kodları

Oyuncu satın alma ucu, ret nedenini JSON code ile söyler: {"code":"out_of_stock","message":"…"} (message ayrıntı metnidir, kararınızı code’a göre verin).

codeDurumAnlam
not_visible404İlan yok ya da bu oyuncuya görünmüyor
listing_changed409İlanın sürümü (expected_version) değişti; kataloğu yenileyip yeniden gösterin
out_of_stock422Stok bitti
limit_reached422Oyuncu başına satın alma sınırı doldu
insufficient_funds422Bakiye yetmiyor
inventory_full422Envanter dolu

Bunun dışındaki hatalar (400 başlık/gövde, 401, 403 Origin ya da CSRF, idempotency uyuşmazlığı 422) yukarıdaki genel tablodaki gibidir. IAP ilanı bu uçtan alınamaz (422). Ayrıntı: mağaza rehberi.

Eklenti uçları

Eklenti uçları JSON zarf döner; requestId değerini destek taleplerinde iletin (aynı değer x-request-id başlığında da bulunur).

{"code":"unprocessable","message":"plugin command rejected","retryable":false,"requestId":"0192a3b4-0000-7000-8000-0000000000d1","reason":"street_reset_too_early"}
codeDurumretryableNot
unauthorized401hayırOturum/anahtar yok ya da geçersiz; istemci anahtarı
forbidden403hayırCSRF/Origin reddi; eklenti etkin değil; sürüm yetkisi uyuşmuyor; yasaklı hesap (message: account banned)
module_disabled404hayırmodule alanı kapalı modülü söyler
not_found404hayırBilinmeyen eklenti ya da mesaj türü
invalid_argument400hayırBozuk/eksik başlık, bilinmeyen şema sürümü, şemaya uymayan yük
conflict409hayırIf-Match sürümü uyuşmuyor
idempotency_in_flight409evetAynı anahtarlı istek işleniyor
idempotency_mismatch422hayırAynı anahtar, farklı gövde
unprocessable422hayırİş kuralı reddi; reason eklentiye özgü makine kodudur
idempotency_expired410hayırKayıt süresi doldu
rate_limited429evetRetry-After: 60
tenant_unavailable503evetRetry-After: 30
internal500evet

Ayrıntı ve örnekler: eklentileri çağırma.

Not

Eklenti ucunda yasaklı hesap, çekirdek kimlik uçlarının aksine account_banned koduyla gelmez: 403, forbidden ve message: "account banned" döner. Bu ayrıma yalnız bir kullanıcı arayüzü mesajı için ihtiyaç duyarsınız; yeniden deneme döngüsü kurmayın.

Çekirdek ile eklenti arasındaki farklar

Çekirdek RESTEklenti
GövdeDüz metin (birkaç JSON istisna)Daima JSON
Makine koduYalnız JSON olan hatalardaHer hatada code
retryableYok; durum koduna göre karar verinVar
Yeniden deneme süresiYalnız 503 (30 sn)429 (60 sn) ve 503 (30 sn)
Yasaklı hesap403 account_banned (giriş uçlarında)403 forbidden
x-request-idYokVar

Hata işleme önerisi

  1. Önce durum koduna bakın; 401 oturumu yenileyin ya da yeniden giriş yaptırın, 403 yetki ya da yapılandırma sorunudur (anahtar türü, CSRF, modül, eklenti durumu).
  2. 404 için gövdeyi okuyun: JSON ve module_disabled ise modül kapalıdır, yoksa kaynak yoktur.
  3. 409’u iki ayrı biçimde ele alın: sürüm çakışması (durumu yeniden okuyun) ile idempotency “işleniyor” (bekleyip aynı anahtarla tekrar edin).
  4. 429 ve 503’te üstel geri çekilme kullanın; Retry-After varsa onu bekleyin (idempotency ve eşzamanlılık sayfasındaki örnek).
  5. 422 iş kuralı reddidir; aynı isteği olduğu gibi tekrarlamak sonuç değiştirmez.
  6. Sunucu hata ayrıntısı sızdırmaz; sorun bildirirken durum kodunu, istek zamanını ve (eklenti uçlarında) requestId’yi iletin (sorun giderme).

Idempotency ve eşzamanlılık

Bu sayfa, ağ koptuğunda bir isteği iki kez uygulamadan nasıl tekrar edeceğinizi (idempotency, yani tekrar güvenliği) ve iki istemcinin aynı kaynağı aynı anda değiştirmesini iyimser sürüm denetimiyle (optimistic locking) nasıl yöneteceğinizi anlatır. Para, eşya ya da ödül veren her çağrıda bu iki kuralı kullanın.

Idempotency-Key

Değiştiren bir istek (POST, bazı PUT/PATCH/DELETE) Idempotency-Key başlığı taşıyabilir ya da taşımak zorundadır. Anahtarın kuralları:

KuralDeğer
Uzunluk1 ile 200 karakter (boş olamaz)
Karakter kümesiBoşluksuz yazdırılabilir ASCII (SDK yalnız bunu üretir ve kabul eder)
Önerilen değerRastgele UUID (v4); bazı uçlar UUID biçimini zorunlu kılar (klan, grup ve turnuva uçları: geçerli UUID olmalıdır)
KapsamÇekirdek komutlarda anahtar ortam genelinde benzersizdir: aynı ortamda iki farklı çağıran aynı anahtarı kullanırsa çakışır. Eklenti komutlarında anahtar eklenti ve çağıran (oyuncu ya da sunucu) başınadır. Bu yüzden her zaman rastgele UUID kullanın, oyuncu-1 gibi tahmin edilebilir değerler kullanmayın
Kayıt süresi24 saat (komut kayıtları); eklenti komutlarında işlemin receiptRetentionSeconds değeri (şimdilik 86 400 sn)

Hangi uçta anahtarın zorunlu, hangisinde isteğe bağlı olduğunu uç nokta referansı gösterir. Örnek: karakter komutlarında isteğe bağlıdır, cüzdan aktarımında, envanter ve eşya işlemlerinde, ticarette, mağaza ve eklenti komutlarında zorunludur.

Anahtar nasıl çalışır

  1. İlk istek anahtarla gelir; sunucu işlemi uygular ve sonucu anahtara bağlı saklar.
  2. Aynı anahtar + aynı içerik tekrar gelirse işlem yeniden uygulanmaz; kayıtlı sonuç döner. Bazı yanıtlar bunu replayed: true alanıyla bildirir (karakter komutları, mağaza satın alma).
  3. Aynı anahtar + farklı içerik 422 ve idempotency mismatch (eklentide idempotency_mismatch) alır.
  4. İlk istek hâlâ işleniyorsa ikincisi 409 (idempotent request in progress ya da idempotency_in_flight) alır; kısa süre sonra aynı anahtarla tekrar deneyin.
  5. Kayıt süresi dolduysa 410 döner.
  6. Çekirdek komutlarda anahtar yalnız başarıda “yanar”: yetki reddi (ör. istemci anahtarıyla sunucu komutu), doğrulama hatası ya da geri alınan işlem anahtarı tüketmez; düzeltip aynı anahtarla tekrar gönderebilirsiniz.

Uyarı

Belirsiz sonuçlu bir isteği yeni anahtarla tekrarlamayın. Yeni anahtar yeni işlem demektir ve çift ödül, çift harcama yaratır. Anahtarı bir kez üretin, saklayın (istek ve yeniden denemeler boyunca aynı kalsın) ve gerekirse sonucu sorgulayın.

Ne zaman yeni anahtar üretilir

  • Kullanıcı aynı işlemi bilerek yeniden başlattığında (ör. ikinci mağaza satın alması).
  • İş kuralı reddinden (422) sonra durumu değiştirip yeniden denediğinizde.
  • 410 (anahtar süresi doldu) sonrasında, durumu okuyup işlemin gerçekten uygulanmadığından emin olduktan sonra.

Örnek: güvenli yeniden deneme

Aşağıdaki yardımcı anahtarı bir kez üretir, ağ hatalarında ve 408, 429, 5xx yanıtlarında aynı anahtarla yeniden dener ve Retry-After süresini bekler:

/** `Retry-After` değerini milisaniyeye çevirir; saniye ya da HTTP tarihi olabilir, bilinmiyorsa `null`. */
function retryAfterMs(header: string | null): number | null {
  if (header === null) return null;
  if (/^\d+$/.test(header)) return Number(header) * 1000;
  const date = Date.parse(header);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

const RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);

/**
 * Bir değiştirici isteği AYNI `Idempotency-Key` ile güvenle yeniden dener.
 * Anahtarı bir kez üretin ve her denemede aynen gönderin; yeni anahtar yeni işlem demektir.
 * 429 ve 503'te sunucunun `Retry-After` süresini bekler; yoksa üstel geri çekilme kullanır.
 */
export async function postWithRetry(url: string, body: unknown, headers: Record<string, string>, maxAttempts = 4): Promise<Response> {
  const idempotencyKey = crypto.randomUUID();
  let lastError: unknown;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    let response: Response | undefined;
    try {
      response = await fetch(url, {
        method: "POST",
        credentials: "include",
        headers: { ...headers, "content-type": "application/json", "idempotency-key": idempotencyKey },
        body: JSON.stringify(body),
      });
    } catch (error) {
      lastError = error; // ağ hatası: istek uygulanmış olabilir, aynı anahtarla tekrar güvenlidir
    }
    if (response !== undefined) {
      if (!RETRYABLE_STATUS.has(response.status)) return response; // başarı ya da kalıcı hata
      lastError = new Error(`HTTP ${response.status}`);
      if (attempt === maxAttempts) return response;
      const wait = retryAfterMs(response.headers.get("retry-after")) ?? Math.min(200 * 2 ** (attempt - 1), 5000);
      await new Promise((resolve) => setTimeout(resolve, wait));
      continue;
    }
    if (attempt < maxAttempts) await new Promise((resolve) => setTimeout(resolve, Math.min(200 * 2 ** (attempt - 1), 5000)));
  }
  throw lastError;
}

TypeScript SDK’sı bunu sizin yerinize yapar: CrxClient (sunucu anahtarı) GET isteklerini ve Idempotency-Key taşıyan istekleri 408, 429, 500, 502, 503, 504 durumlarında (varsayılan 2 deneme, maxRetries ile 0 ile 5 arası) üstel geri çekilmeyle ve Retry-After’a saygı göstererek yeniden dener. Eklenti komutlarında PlayerClient anahtarı hazırlama anında üretir ve tarayıcı günlüğüne (journal) yazar; sayfa yenilense bile aynı anahtarla devam eder (bağlamalar).

Sonucu sorgulama (eklenti komutları)

Eklenti komutu için yanıtı hiç alamadıysanız, tekrar göndermeden GET …/commands/{message_type}/result?idempotency_key=… ile sonucu sorgulayın. Yalnız 404 (kayıt yok) yanıtında aynı anahtarla yeniden gönderin (eklentileri çağırma). Çekirdek komutlarda ayrı bir sorgu ucu yoktur; aynı anahtar ve gövdeyle yeniden göndermek aynı kayıtlı sonucu döndürür.

İyimser sürüm denetimi

Sunucu, değiştirilebilir kaynaklara (karakter, envanter, eklenti kaynağı, mağaza ilanı) artan bir sürüm (version) tutar. İsteğinizde “şu sürümü görüp karar verdim” derseniz ve bu arada başkası kaynağı değiştirdiyse, istek uygulanmaz ve çakışma döner. Böylece iki istemci birbirinin değişikliğini sessizce ezmez.

Kullanım yeriNasıl gönderilir
Karakter komutları (POST /v1/characters/{id}/commands)If-Match: 3 (tırnaklı "3" de kabul edilir)
Envanter ve eşya işlemleri (If-Match destekleyen uçlar)If-Match
Mağaza yönetim uçları (sayfa, ilan değişiklikleri)If-Match (isteğe bağlı)
Eklenti komutları (HTTP)If-Match: "12"
Eklenti komutları (realtime Command çerçevesi)expectedVersion: "12" (ondalık metin)
Oyuncu satın alma (/v1/player/store/listings/{id}/buy)If-Match kullanılmaz; ilan sürümü gövdedeki expected_version alanıdır (başlık gönderirseniz 400)

If-Match biçimi: tam sayı sürüm numarası. * ve zayıf doğrulayıcılar (W/"3") reddedilir (400); negatif sayı ve sayısal olmayan değer 400 alır. Başlık verilmezse sürüm denetimi yapılmaz (son yazan kazanır); para ve eşya gibi kritik kaynaklarda göndermenizi öneririz.

Çakışma gelince

Uç ailesiYanıt
Çekirdek REST409 ve version conflict: expected 3, actual 5
Eklenti409 ve {"code":"conflict","message":"version conflict: expected 3, actual 5",…}
Mağaza oyuncu satın alma409 ve {"code":"listing_changed",…}
Realtime komutuError çerçevesi InvalidPayload (retryable: false); ayrıntı telden verilmez

Çözüm döngüsü:

  1. Kaynağı yeniden okuyun (güncel version ile).
  2. Kullanıcının niyeti hâlâ geçerli mi karar verin (ör. hâlâ yeterli bakiye var mı).
  3. Yeni anahtarla (bu yeni bir deneme) güncel sürümle tekrar gönderin.

Not: sürüm çakışması iş kuralı değil, eşzamanlılık sonucudur; aynı isteği körlemesine tekrar göndermeniz yine çakışır. Idempotency anahtarı tekrar içindir, sürüm denetimi çakışma içindir; ikisini birlikte kullanabilirsiniz: aynı anahtar + aynı If-Match ile gönderilen tekrar kayıtlı sonucu döner.

Özet kural listesi

  • Para, eşya, ödül ve komut gönderen her isteğe anahtar ekleyin; anahtarı bir kez üretin, yeniden denemelerde değiştirmeyin.
  • Belirsiz sonucu sorgulayın ya da aynı anahtarla tekrarlayın; yeni anahtarla asla.
  • Paylaşılan kaynak yazarken If-Match/expectedVersion gönderin; 409’da yeniden okuyun.
  • 422 iş kuralı reddidir; tekrar etmek sonuç değiştirmez.
  • Ayrıntılı hata tablosu için hata referansı.

Sayfalama

Bu sayfa, liste döndüren uçların sayfalarını nasıl gezeceğinizi anlatır. Open Play’de tek bir sayfalama biçimi yoktur: çoğu uç opak imleç (cursor) kullanır, birkaç uç ise farklı bir yöntem kullanır. Aşağıdaki tablo hangi uçta hangisinin geçerli olduğunu ve sayfa boyutu sınırlarını gösterir.

İmleç (keyset) sayfalama

Çekirdek liste uçlarının çoğu imleç tabanlıdır:

  • İstek: ?limit=<n>&cursor=<imleç>. İlk sayfada cursor vermezsiniz.
  • Yanıt: {"items":[…],"next_cursor":"…"}. next_cursor null ise son sayfadasınız.
  • Sonraki sayfa: cursor olarak bir önceki yanıttaki next_cursor değerini aynen verin.
  • İmleç opaktır: içeriğini ayrıştırmayın, değiştirmeyin, saklayıp uzun süre sonra kullanmayı varsaymayın. Bozuk imleç 400 alır.
  • Sıralama sabittir ve OFFSET kullanılmaz; sayfalar arasında yeni kayıt eklenirse atlama ya da tekrar oluşmaz (kayıtlar sabit anahtara göre sıralanır).
  • Sayfa boyutu limit ile verilir; sınırların dışındaki değer çoğu uçta kısılır (hata vermez). İstisnalar tabloda belirtilmiştir.
GET /v1/characters?limit=2 HTTP/1.1
Authorization: Bearer crx_live_<önek>_<gizli>
{"items":[{"id":"…","definition":"hero","version":3}],"next_cursor":"<opak-imleç>"}
GET /v1/characters?limit=2&cursor=<opak-imleç> HTTP/1.1
Authorization: Bearer crx_live_<önek>_<gizli>

Uca göre biçimler

UçBiçimVarsayılanSınır
GET /v1/charactersİmleç (definition süzgeci)501 ile 200 arası kısılır
GET /v1/characters/{id}/events, …/timelineİmleç (artan seq)501 ile 200 arası kısılır
GET /v1/rules, GET /v1/webhooksİmleç501 ile 200 arası kısılır
GET /v1/ledgerİmleç (wallet_id zorunlu)501 ile 200 arası kısılır
GET /v1/items, GET /v1/player/itemsİmleç (owner ile)501 ile 200 arası kısılır
GET /v1/assetsİmleç50 (0 ya da yok = varsayılan)en çok 200
GET /v1/catalog/items, GET /v1/catalog/charactersİmleç501 ile 100 arası kısılır
GET /v1/notificationsİmleç (status, limit, cursor)501 ile 100 arası kısılır
GET /v1/leaderboards/{key}/entriesİmleç20 (0 ya da yok = varsayılan)en çok 100; fazlası 400 (kısılmaz)
GET /v1/leaderboards/{key}/around-meOyuncu çevresi (player_id, radius)—Sayfa yok; çevre pencereleri sınırlıdır
GET /v1/player/leaderboards/{key}Tek sayfa (limit)201 ile 50; dışı 400
GET /v1/chat/channels/{channel_id}/messagesZaman tabanlı (before_ms + limit)501 ile 100 (service doğrular)
GET /v1/questsoffset + limit (search süzgeci)20 (0 ya da yok = varsayılan)limit en çok 100, offset en çok 100000; fazlası 400
GET /v1/auth/admin/list-playersİmleç (UUID)uç varsayılanıUca özgü
GET /v1/clans, GET /v1/partiesSayfalama yok; tüm sonuç (parti için player_id süzgeci)——
GET /v1/wallets, GET /v1/inventories/{owner}Sayfalama yok (sahibe göre tüm liste)——
Eklenti sorguları (/v1/plugins/…/queries/…)Eklentiye özgü; girdi şemasında tanımlıdır—Eklentinin şema belgesi

Not

Yanıtta imleç alanı çekirdek REST’te next_cursor (yılan_harf) olarak gelir. Sohbet mesajlarında yeni sayfa için en eski mesajın sent_at_ms değerini before_ms olarak verirsiniz. Panel API’lerinin sayfalaması (offset/ızgara) panel arayüzünün iç kullanımıdır ve bu dokümanın kapsamında değildir.

SDK’da sayfalama

TypeScript SDK’sı imleçli listeler için yineleyiciler sunar: iterateListCharacterEvents, iterateGetCharacterTimeline, iterateListRules, iterateListWebhooks. Bu yineleyiciler next_cursor’ı sizin yerinize izler. Önemli kurallar:

  • İmleç opaktır; yineleyici onu çevirmeden geri verir.
  • Boş next_cursor döngüyü bitirir; imleç döngüsü (aynı imlecin tekrar gelmesi) ya da maxPages aşımı sessiz kesme değil, pagination türünde hatadır.
  • limit tel üzerinde u32’dir; sunucu 1 ile 200 aralığına kısar.

Örüntüler

Hepsini gezmek (sunucudan sunucuya iş): imleç boş gelene kadar döngü kurun; her sayfada kısa bir üst sınır (ör. en çok 1000 sayfa) koyun.

cursor = yok
tekrarla:
    sayfa = GET /v1/rules?limit=200[&cursor=cursor]
    sayfa.items işlenir
    cursor = sayfa.next_cursor
    cursor boşsa dur

Oyun içi “daha fazla yükle” listesi: küçük limit (20 ile 50) kullanın, next_cursor’ı bellekte tutun, kullanıcı kaydırdıkça bir sonraki sayfayı isteyin. İmleci kalıcı saklamayın.

Tutarlılık: imleçli sayfalar canlı veriyi gezer; gezerken eklenen yeni kayıtlar, imlecin gerisinde kalıyorsa görünmeyebilir. Anlık görüntü gerektiren raporlar için sorgu zamanını (örneğin as of) kendi tarafınızda kaydedin.

İlgili: uç noktalar, hız sınırları.

Hız sınırları

Bu sayfa, Open Play’in bugün uyguladığı hız sınırlarını (rate limit) ve 429 aldığınızda ne yapacağınızı anlatır. Önemli bir dürüstlük notu: API anahtarı başına genel bir hız sınırı yoktur. Sınırlar belirli işlemlere (giriş, kayıt, sohbet, eklenti komutu, soket bağlantısı) konmuştur; geri kalan uçlar sınırsızdır ve yükü sizin yönetmeniz gerekir.

Bilinmesi gerekenler

  • Genel bir “dakikada N istek” kotası yok. Sunucu anahtarıyla yapılan çağrılar için anahtar ya da ortam başına genel sınır bulunmaz.
  • Sayaçlar Redis’te tutulur ve Redis’e ulaşılamazsa sınır uygulanmaz (fail-open); tek istisna, aşağıda belirtilen denetim satırı tavanıdır. Sınırı tek başına güvenlik kontrolü olarak kullanmayın.
  • Retry-After başlığı yalnız eklenti 429’unda (60 sn) ve 503 yanıtlarında (30 sn) vardır. Çekirdek ve kimlik uçlarının 429’u Retry-After taşımaz; kendi geri çekilmenizi uygulayın.
  • İstemci IP’si, kimlik uçlarındaki sınırların anahtarıdır. Ters vekil istemci IP’sini iletmiyorsa tüm oyuncular aynı kovayı paylaşır (üretim kontrol listesi).

Kimlik uçları (oyuncu giriş hattı)

Bu sınırlar servis katmanında uygulanır; REST’in kendisinde de aynıdır. Pencere sabittir (ilk istekle başlar ve süre dolunca sayaç sıfırlanır; kayan pencere değildir); aşım 429 ve düz metin rate limited döner (Retry-After yok).

KovaLimitPencereAnahtarHangi uçlar
Misafir açma3060 snİstemci IP’siPOST /v1/auth/sign-in/guest (yeniden giriş de sayılır)
E-posta girişi560 snİstemci IP’siPOST /v1/auth/sign-in/email (her deneme)
Başarısız giriş kilidi10900 snAdres + IPYanlış parola denemeleri; başarılı girişte hak iade edilir; parola değiştirmede yanlış mevcut parola da sayılır
Kayıt (IP)1060 snİstemci IP’siPOST /v1/auth/sign-up/email, misafir terfisi
Kayıt (oturum)3060 snOturum sahibiPOST /v1/auth/guest/upgrade/email
Kayıt (adres)5900 snE-posta adresiKayıt ve terfi
Parola değiştirme (IP)1060 snİstemci IP’siPOST /v1/auth/change-password
Parola değiştirme (hesap)660 snOyuncuPOST /v1/auth/change-password
Kurtarma (IP)560 snİstemci IP’siforgot-password, reset-password, sign-in/magic-link (üç uç tek kovayı paylaşır)
Kurtarma (adres)5900 snE-posta adresiforgot-password, sign-in/magic-link
OAuth başlangıcı3060 snİstemci IP’sisign-in/oauth/{provider}, link/{provider}
OAuth dönüşü6060 snİstemci IP’sicallback/{provider}

Ek olarak:

SınırLimitAnahtarNot
Görünen ad değiştirme10 / 3600 snOyuncuPOST /v1/auth/session/display-name; aşım 429
Hesap silme denemesi5 / 3600 snOyuncuPOST /v1/auth/account/delete; aşım 429
Başarısız giriş denetim satırı20 / 3600 snHesapGirişi reddetmez; yalnız arka plan denetim kaydını kısar. Redis kesintisinde kayıt atlanır (fail-closed)

Not

Misafir açma kovası IP başınadır. Ters vekil istemci IP’sini iletmiyorsa ya da bir NAT/yurt ağı arkasından çok oyuncu bağlanıyorsa 30 misafir/dakika sınırı hızla dolar. Bir lansman ya da yük testi planlıyorsanız IP iletimini doğrulayın.

Sohbet ve bildirim

SınırLimitPencereAnahtarAşılınca
Sohbet mesajı gönderme2010 snOrtam + oyuncuREST’te 429; sohbet soketinde message_rejected
Bildirim teslimi6060 snOrtam + oyuncuTeslim ertelenir (sağlayıcıya inilmez), oyuncuya hata dönmez

Eklenti komutları

Eklenti komut ve sorguları oyuncu başına, kova başına dakikalık bütçelere tabi olabilir. Bütçeyi eklenti tanımlar; sunucu bunu REST, realtime komutu ve gRPC yolunda aynı kararla uygular. Aşım eklenti zarfıyla gelir: 429, code: "rate_limited", retryable: true, Retry-After: 60.

Bugün bütçe tanımlayan eklenti package-ride’dır: 60 sn’lik pencerede kova başına 6 ile 60 komut (ör. teslimat kabul/başlatma/tamamlama 12, giriş bonusu 10, görev ödülü ve garaj 30, çarpışma/yarış bahsi 60). Kova değerleri eklentiye özgüdür ve değişebilir; kesin sayılar için eklentinin belgesine bakın. Operatör bazı kovaları “gözlem” kipine alabilir; bu kipte aşım yalnız sayılır, komut reddedilmez.

Unity SDK’sında PluginClient.RetryAfter(hata) eklenti 429’u için 60 saniye döndürür.

Realtime soket

SınırDeğerAşılınca
Adres başına yeni bağlantı30 / dakikaYükseltme reddedilir
Adres başına açık soket8CapacityExceeded, soket kapanır
Oyuncu başına açık soket4CapacityExceeded, soket kapanır
Bağlantı başına çerçeve hızı40/sn (160 çerçevelik patlama payı)CapacityExceeded (retryable: true), soket kapanır

Ayrıntı: yeniden bağlanma ve limitler.

429 aldığınızda

HTTP/1.1 429 Too Many Requests
Content-Type: text/plain; charset=utf-8

rate limited

Eklenti ucunda:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
x-request-id: 0192a3b4-0000-7000-8000-0000000000d1

{"code":"rate_limited","message":"rate limited","retryable":true,"requestId":"0192a3b4-0000-7000-8000-0000000000d1"}
  1. Retry-After varsa o kadar bekleyin; yoksa üstel geri çekilme kullanın (ör. 1, 2, 4, 8 sn; üst sınır 30 sn) ve rastgele bir sapma (jitter) ekleyin.
  2. İstek değiştiriciyse aynı Idempotency-Key ile tekrar edin (idempotency ve eşzamanlılık).
  3. Kullanıcı eylemiyle tetiklenen sınırlarda (giriş, kayıt) otomatik tekrar yerine kullanıcıya bekleme süresini gösterin; parola ve kayıt uçlarında otomatik tekrar kilit süresini uzatır.
  4. CrxClient (sunucu anahtarı) 429’u, GET ve anahtarlı isteklerde, Retry-After’a saygı göstererek kendisi yeniden dener (varsayılan 2 deneme).

Uyarı

Kural bütçesi aşımı da (rule budget exceeded: <kural>) 429 döner; bu bir hız sınırı değil, kural motoru bütçesidir (komut başına 50 kural, 200 eylem, 500 değerlendirme düğümü ve zincir derinliği 5). Yeniden deneme sonuç değiştirmez; kuralı sadeleştirin (oyun kuralları).

Kendi tarafınızda yapmanız gerekenler

Genel bir sunucu sınırı olmadığı için büyük yükte sizin koruma eklemeniz gerekir:

  • Sunucudan sunucuya toplu işlerde eşzamanlılığı sınırlayın (ör. aynı anda 5 ile 10 istek) ve hataları geri çekilmeyle yönetin.
  • Anahtar bazında istek hızını kendi tarafınızda ölçün; tek bir sızmış anahtar sınırsız çağrı yapabilir (güvenlik).
  • Lansman öncesi GET /v1/… okumalarını önbelleğe alın (katalog uçları ETag ve If-None-Match ile koşullu okumayı destekler; oyuncu başına yoklamayı 15 sn’den sık yapmayın).

Sayılar ve kodlama

Bu sayfa, Open Play’in sayıları, parayı, zamanı ve kimlikleri JSON üzerinde nasıl taşıdığını anlatır. En sık yapılan hata, 64 bit tam sayıları JavaScript number’ına çevirip hassasiyet kaybetmektir; bu sayfayı o hatadan kaçınmak için okuyun.

Kısa kurallar

Alan türüÇekirdek RESTEklenti ve realtime
64 bit tam sayı (int64)JSON sayısı ("amount": 250)Ondalık metin ("amount": "250")
32 bit tam sayıJSON sayısıJSON sayısı
Kayan noktaHiçbir yerde yokHiçbir yerde yok
ParaKüçük birim tam sayısı (amount, balance)Küçük birim, ...Minor alanları, int64
YüzdeBaz puan (10000 = %100)Baz puan
ZamanRFC 3339 metni (created_at)Unix milisaniye tam sayı (...Ms)
KimlikUUID metniUUID metni
Enumsnake_case metinsnake_case (eklenti), PascalCase (realtime çerçeve türleri)

64 bit tam sayılar

JavaScript’in JSON.parse işlevi sayıları double’a çevirir; 2^53 − 1 (9 007 199 254 740 991) üstü tam sayılar sessizce bozulur. Open Play bu yüzden iki profil kullanır:

  • Çekirdek REST int64 alanları JSON sayısı olarak gönderir (version, balance, amount). Değerler pratikte güvenli aralıkta kalır, ama garanti bigint ile okuyan bir ayrıştırıcıdır. TypeScript SDK’sı lossless-json ile okur ve bigint döner.
  • Eklenti ve realtime (plugin-v1-decimal-int64 kodlama profili) int64 alanları ondalık metin olarak gönderir ve bekler: {"expectedVersion":"3"}, {"sessionEpoch":"7"}. Böylece her istemci dili kayıpsız taşır.

Kurallı ondalık metin

Eklentilerde işaretli int64 kurallı biçimde yazılmalıdır; sunucu başka biçimi reddeder (400 invalid_argument):

GeçerliGeçersizNeden
"0", "42", "-7""+1"Artı işareti yok
"9223372036854775807""01", "007"Baştaki sıfır yok
"-9223372036854775808""-0"Eksi sıfır yok
" 5", "5 "Boşluk yok
"1.5", "1e3"Kesir ve üs yok
"9223372036854775808"int64 sınırı aşıldı
5 (JSON sayısı)Eklenti int64 alanı metin ister

Realtime çerçevelerindeki sayaçlar (sessionEpoch, inputSeq, revision, sequence, expectedVersion, streamEpoch, barrierSequence) aynı metin biçimindedir ve negatif olamaz. protocolVersion, schemaVersion ve diğer küçük sayaçlar JSON sayısıdır.

Eklenti şema belgesinde int64 alanın sınırları da metin olarak yazılır ("minimum": "0", "maximum": "9223372036854775807"); bu normaldir.

İstemci dillerinde

DilEşleme
TypeScript SDKint64 → bigint. Sayıya çevirmeyin; gösterim için value.toString() kullanın. JSON’u kendiniz JSON.parse ile okumayın
Unity (C#)int64 → long; eklenti çıktılarında ham JSON JsonValue ondalık metin olarak kalır
Ham JSON (başka dil)Eklenti alanlarını string olarak okuyup int64’e çevirin

TypeScript’te JSON’u kendiniz ayrıştırıyorsanız lossless-json gibi bir kütüphane kullanın; aksi halde büyük değerler bozulur.

Para ve ölçekli değerler

  • Para değerleri küçük birimli tam sayıdır (ör. kuruş): 250 kaç ana birim ettiği, oyununuzun gösterim kararına bağlıdır (panelde para biriminin “kuruş hanesi” ayarı gösterimi belirler); API yalnız tam sayıyı taşır. Bakiye ve tutar alanları amount, balance, eklentilerde ...Minor adıyla gelir.
  • Kayan nokta (float/double) hiçbir yerde kullanılmaz: ne istek, ne yanıt, ne de hesaplama. Kendi tarafınızda da para için float kullanmayın; tam sayıyla çalışıp yalnız gösterimde ondalığa çevirin.
  • İçerik gruplarındaki ölçekli değerler tamsayı + ölçek çiftidir: 4350 ve ölçek 2 → 43.50. TypeScript SDK’sında formatScaled(değer: bigint, ölçek) bunu kayıpsız biçimlendirir (0 ile 18 hane). Kendi float bölmenizi yapmayın.

Yüzdeler: baz puan

Yüzde ve oran alanları baz puan (basis point) tam sayısıdır: 10000 = %100, 2500 = %25, 1 = %0,01. Örnek: grup ödül payı share_bp 0 ile 10000 arasında olmalıdır; dışı 400 alır.

Zaman

YerBiçimÖrnek
Çekirdek REST yanıtları (created_at, as_of)RFC 33392026-11-03T10:15:30+00:00
Eklenti ve realtime (createdAtMs, sent_at_ms)Unix epoch milisaniye, tam sayı1793700930000
Sohbet sorgusu before_msUnix epoch milisaniye1793700930000
Webhook imzasındaki tUnix epoch saniye1793700930
Deprecation başlığı@<unix saniye> (RFC 9745)@1793491200
Eklenti kullanımdan kalkma (sinceUnixS)Unix saniye1793577600

Sunucu saatlerini UTC olarak yorumlayın. İstemci saati sunucu kararında kullanılmaz: zaman ve rastgelelik sunucuda üretilir ve olaya yazılır.

Kimlikler ve anahtarlar

  • UUID: küçük harfli, tireli metin (0192a3b4-0000-7000-8000-000000000001). Çoğu kaynak kimliği zamana göre sıralanan UUID v7’dir; sıralama için kullanabilirsiniz, ama biçimden anlam çıkarmayın.
  • key alanları (tanım anahtarları, skor tablosu, görev): oyun geliştiricinin verdiği okunabilir kimlikler; oluşturduktan sonra aynen kullanın, büyük/küçük harf ya da boşluk değiştirmeyin.
  • Eklenti kimlikleri: küçük harf ve tire (package-ride). Mesaj türleri: nokta ile ayrılmış, eklenti önekli (package-ride.delivery-trip.active.input).
  • İmleçler: opak metin; içeriğini ayrıştırmayın (sayfalama).
  • API anahtarları: crx_live_<önek>_<gizli>; önek yalnız tanımlama içindir, gizli kısım yalnız bir kez gösterilir (güvenlik).

Numaralandırma (enum) değerleri

Tüm numaralandırma değerleri sunucuda tek bir tanımdan gelir ve bilinmeyen değer reddedilir (sessizce yok sayılmaz):

  • Çekirdek REST alanları snake_case metindir ("type": "set_flag", "channel": "in_game", "status": "pending").
  • Realtime çerçeve type ve kind değerleri PascalCase’tir (Authenticate, PlayerSelf); alan adları camelCase’tir.
  • Sohbet soketi çerçeve type değerleri camelCase, alan adları snake_case’tir (channel_id).
  • Webhook olay türleri snake_case’tir (webhook olayları).
  • Eklenti şema belgesinde enum olarak listelenen değerler (ör. "image/png") birebir kullanılır.

İstemcinizde bilinmeyen bir enum değeri gelirse (yeni sürümde eklenmiş olabilir) çökmek yerine güvenli bir varsayılana düşün; SDK’lar bilinmeyen değeri kimi yerde reddeder, kimi yerde atlar, bu yüzden sürüm yükseltmelerini değişiklik günlüğünden izleyin.

Metin

  • Tüm metin UTF-8 JSON metnidir.
  • Alanların uzunluk sınırları vardır (ör. Idempotency-Key 200 karakter, eklenti dize alanlarında minLength/maxLength); sınır ihlali 400 alır.
  • Düz metin hata mesajları (çekirdek REST) İngilizce ya da Türkçe olabilir ve sürümler arasında değişir; makine kararı için kullanmayın (hatalar).

Sürümleme

Bu sayfa, Open Play’de karşılaşacağınız sürüm numaralarını (API yolu, protokol, eklenti şeması, SDK) ve bir uç ya da işlem kullanımdan kalkarken sunucunun size nasıl haber verdiğini anlatır. Önemli: yazılı bir kırıcı değişiklik ve kaldırma politikası henüz yok; aşağıda yalnız bugün kodda olan davranış anlatılır.

Hangi sürüm neyi söyler

SürümNerede görünürAnlamı
API yolu /v1Tüm uç yollarıTek yol sürümü; /v2 yoktur
Realtime protocolVersionHer çerçevede (1)Soket protokolü ana sürümü; farklı değer UnsupportedProtocol alır
Eklenti protocolVersionŞema belgesi (1)Eklenti sözleşmesinin ana sürümü
codecProfileŞema belgesi (plugin-v1-decimal-int64)Tel kodlaması; şimdilik tek profil
Eklenti versionŞema belgesi (ör. 1.44.0)Eklentinin sürüm metni
schemaVersionHer işlem ve mesaj tanımı; x-crx-schema-version başlığıTek bir mesajın şema sürümü (pozitif tam sayı)
schemaSha256Şema belgesiŞemaların özeti; sabitleme anahtarı (bağlamalar)
SDK sürümüPaket sürümüTypeScript @ferdiunal/open-play-ts (0.x), Unity com.openplay.sdk (0.x)

/v1 ve sunucu sürümü

Tüm HTTP uçları /v1 altındadır. Sunucunun ayrı bir “API sürüm” başlığı yoktur; hangi uçların var olduğunu uç nokta referansı ve (çekirdek REST için) GET /openapi.json gösterir. /openapi.json elle derlenmiş, çekirdek REST’i kapsayan bir belgedir: eklentileri, WebSocket’leri ve panel API’sini kapsamaz ve her uç için tam olmayabilir.

Protokol sürümü

Realtime çerçevelerinde protocolVersion: 1 zorunludur; başka değer UnsupportedProtocol hatası alır ve soket kapanır. İstemciniz bu hatayı alırsa yeniden denemek yerine kullanıcıdan güncelleme isteyin (TypeScript SDK’sı bu durumda upgradeRequired durumuna geçer).

Eklenti şema sürümleri

Bir eklenti şeması iki düzeyde sürümlenir:

  1. İşlem/mesaj düzeyi schemaVersion: her istekte x-crx-schema-version ile bildirilir. Sunucunun bildiğinden farklı bir sürüm gönderirseniz 400 invalid_argument alırsınız.
  2. Belge düzeyi schemaSha256: şemalarda herhangi bir değişiklik özeti değiştirir. İstemcinizi bu özete sabitlersiniz.

Sürüm metni (1.44.0) bilgilendiricidir; uyumluluk kararını özetle verin. Eklenti sürümünü ve özet eşleşmesini nasıl doğrulayacağınız için bağlamalar.

Kullanımdan kalkma (Deprecation)

Bir uç ya da işlem kullanımdan kalkarken çalışmaya devam eder; davranışı, durum kodları ve gövdesi değişmez. Kalkış size şu işaretlerle bildirilir:

İşaretNerede
Deprecation: @<unix saniye> yanıt başlığı (RFC 9745)Kullanımdan kalkan her yanıtta (başarılı ya da hatalı)
OpenAPI deprecated: true/openapi.json içindeki ilgili işlemde
Şema belgesinde deprecated nesnesiEklenti işlemlerinde: since (sürüm metni), sinceUnixS ve replacement (yerine geçen yüzeyin kısa adı)
SDK’da @deprecatedÜretilen TypeScript bağlamasında ilgili işlem

Bugün kullanımdan kalkanlar:

YüzeyBaşlık değeriYerine
/v1/auth/organization/* (oyuncu organizasyonları, 19 uç)@1793491200 (2026-11-01 UTC)Yok; organizasyon özelliği kullanımdan kalkıyor, stüdyo kavramıyla karıştırmayın
package-ride eklentisinin günlük skor sorguları (getDailyBoard, getLeaderboard)@1793577600 (2026-11-02 UTC)Çekirdek skor tablosu (“Günlük kazanç”)
package-ride operatör oyuncu görünümü (operatorPlayer, operatorPlayers)@1793577600Çekirdek karakter profili ve listesi

Başlığın değeri, kullanımdan kalkmanın başladığı anı (UTC unix saniyesi) verir; bir kaldırma tarihi vermez. Yanıtta Sunset başlığı yoktur.

Uyarı

Kaldırma tarihi, minimum duyuru süresi ya da “kullanımdan kalkan uç en az N ay çalışır” gibi bir söz yoktur. Kullanımdan kalkan bir yüzeye yeni bağımlılık eklemeyin ve Deprecation başlığını görünce geçiş planı yapın. Başlığı günlüğünüzde izlemek için yanıt başlıklarını kaydedin.

SDK sürümleri

  • TypeScript SDK’sı 0.x sürümlerindedir; 0.x sürümler arasında kırıcı değişiklik olabilir. Her sürümün değişikliklerini paketin değişiklik günlüğünden ve sürüm notlarından izleyin; sürümü tam (0.1.15 gibi) sabitleyin, ^ aralığı kullanmayın.
  • Unity SDK’sı 0.1.0 sürümündedir ve yalnız Unity 6000.6.7f1 ile doğrulanmıştır (Unity SDK).
  • SDK ve sunucu birlikte gelişir: yeni bir SDK özelliği (ör. skor tablosu, mağaza), o özelliği taşıyan sunucu sürümünü gerektirir. SDK’yı sunucudan önce güncellemeyin.
  • Eklenti bağlamalarında uyum, SDK sürümüne değil schemaSha256 sabitlemesine bağlıdır (bağlamalar).

Ne yapmalı

  1. SDK sürümünüzü ve eklenti schemaSha256 değerlerinizi sürüm manifestinize yazın.
  2. Her sunucu güncellemesinden sonra şema belgesini karşılaştırın ve gerekirse bağlamayı yeniden üretin.
  3. Deprecation başlığı gören her çağrı için geçiş işi açın.
  4. Realtime ve eklenti protokol sürümü uyuşmazlığında kullanıcıdan güncelleme isteyin; yeniden deneme döngüsü kurmayın.

Uç nokta referansı

Bu sayfa, her HTTP ucu için hangi kimlikle çağrılacağını ve hangi başlıkların beklendiğini gösterir. Tablo koddaki rota kayıt defterinden üretilir; yeni bir uç eklendiğinde sayfa da güncellenir.

Nasıl okunur

  • Modül: Kapalı bir modülün uçları 404 ve module_disabled döner (hatalar). Çekirdek uçlar kapatılamaz.
  • Kimlik: Sunucu anahtarı ve istemci anahtarı Authorization: Bearer crx_live_<önek>_<gizli> başlığıyla gönderilir. Oyuncu oturumu çerezle ya da Bearer oturum jetonuyla gelir ve x-crx-env: <ortam uuid> başlığı ister; çerez oturumunda değiştirici isteklere x-csrf-token ve izinli Origin eklenir.
  • Sunucu anahtarı olarak işaretli uçları istemci anahtarıyla çağırırsanız 403 alırsınız. Sunucu veya istemci anahtarı olarak işaretli uçlarda kodda anahtar türü kapısı yoktur; istemciye gömülen anahtarla çağrılabilir olmaları bir güvenlik kararıdır, bu uçları yalnız sunucunuzdan çağırın (güvenlik).
  • Idempotency-Key: zorunlu ise başlık olmadan istek 400 alır; isteğe bağlı ise gönderirseniz tekrar güvenli olur (idempotency). If-Match: uç iyimser sürüm denetimini destekler.
  • Eklenti uçları (/v1/plugins/...) için gereksinimler eklentinin şema belgesindedir; ayrıntı eklentileri çağırma sayfasındadır.

Toplam uç sayısı: 225.

Karakterler

Modül: characters

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/charactersSunucu veya istemci anahtarı——Karakterleri listeler (imleç sayfalama, definition süzgeci).
POST/v1/charactersSunucu veya istemci anahtarı——Bir tanımdan (definition) yeni karakter üretir.
GET/v1/characters/{id}Sunucu veya istemci anahtarı——Tek karakteri durumuyla okur.
DELETE/v1/characters/{id}Sunucu anahtarı——Karakteri siler (yumuşak silme).
POST/v1/characters/{id}/commandsSunucu anahtarı (istemci anahtarı yalnız set_flag)isteğe bağlıevetKarakter komutunu çalıştırır (set_flag, set_attribute, add_resource, add_modifier, remove_modifier, add_xp, rollback).
GET/v1/characters/{id}/eventsSunucu veya istemci anahtarı——Karakterin olay akışını listeler (imleç sayfalama, artan seq).
GET/v1/characters/{id}/stateSunucu veya istemci anahtarı——Karakterin at anındaki durumunu olayları yeniden oynatarak verir.
GET/v1/characters/{id}/statsSunucu veya istemci anahtarı——Etkin istatistikleri adım adım döküm (breakdown) ile verir.
GET/v1/characters/{id}/timelineSunucu veya istemci anahtarı——Olay akışını okunabilir özetlerle verir.
POST/v1/definitionsSunucu anahtarı——Karakter tanımını kaydeder (değişmez sürüm üretir).
GET/v1/definitions/{key}Sunucu veya istemci anahtarı——Güncel tanımı çözülmüş şemasıyla okur.
POST/v1/level-curvesSunucu anahtarı——Seviye eğrisini doğrulayıp kaydeder (değişmez sürüm).
GET/v1/level-curves/{key}Sunucu veya istemci anahtarı——Güncel seviye eğrisini okur.
GET/v1/rulesSunucu veya istemci anahtarı——Kuralları listeler (imleç sayfalama).
POST/v1/rulesSunucu anahtarı——Kural kaydeder; derlenemeyen kural hiç yazılmaz.
POST/v1/rules/dry-runSunucu veya istemci anahtarı——Kuralı gerçek bir karakter üzerinde yazmadan simüle eder.
GET/v1/rules/{key}Sunucu veya istemci anahtarı——Kuralın güncel sürümünü okur.
DELETE/v1/rules/{key}Sunucu anahtarı——Kuralı siler (yumuşak silme).
POST/v1/rules/{key}/disableSunucu anahtarı——Kuralı değerlendirme zincirinden çıkarır.
POST/v1/rules/{key}/enableSunucu anahtarı——Kuralı değerlendirme zincirine alır.
POST/v1/traitsSunucu anahtarı——Özelliği (trait) kaydeder (değişmez sürüm).
GET/v1/catalog/charactersSunucu anahtarı veya oyuncu oturumu——Karakter kataloğunu listeler.
GET/v1/catalog/characters/{key}Sunucu anahtarı veya oyuncu oturumu——Karakter katalog girdisini okur.

Ekonomi

Modül: economy

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/auth/admin/player-wallet-movementsSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Bir oyuncunun cüzdan hareketlerini listeler.
POST/v1/currenciesSunucu anahtarı——Para birimi tanımlar.
GET/v1/economy/faucet-sinkSunucu anahtarı——Günlük kaynak (faucet) ve gider (sink) toplamlarını verir.
GET/v1/economy/flagsSunucu anahtarı——Ekonomi anomali işaretlerini listeler.
GET/v1/economy/merchant-gapSunucu anahtarı——Tüccar alış-satış farkı raporunu verir.
GET/v1/economy/top-holdersSunucu anahtarı——Bir para biriminde en çok bakiyeye sahip oyuncuları listeler.
GET/v1/ledgerSunucu veya istemci anahtarı——Bir cüzdanın defter (ledger) kayıtlarını listeler (imleç sayfalama).
POST/v1/transfersSunucu anahtarızorunlu—İki cüzdan arasında para aktarır.
GET/v1/walletsSunucu veya istemci anahtarı——Bir sahibin cüzdanlarını (owner) listeler.

Eşyalar

Modül: items

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/inventories/{owner}Sunucu veya istemci anahtarı——Sahibin envanterini okur.
POST/v1/inventories/{owner}/equipSunucu anahtarızorunluevetEşyayı donatır.
POST/v1/inventories/{owner}/moveSunucu anahtarızorunluevetEşyayı envanterde başka bir yuvaya taşır.
POST/v1/inventories/{owner}/splitSunucu anahtarızorunluevetBir yığını ikiye böler.
POST/v1/inventories/{owner}/stackSunucu anahtarızorunluevetİki yığını birleştirir.
POST/v1/inventories/{owner}/unequipSunucu anahtarızorunluevetDonatılmış eşyayı çıkarır.
POST/v1/item-definitionsSunucu anahtarı——Eşya tanımını kaydeder.
GET/v1/item-definitions/{key}Sunucu veya istemci anahtarı——Eşya tanımını okur.
GET/v1/itemsSunucu veya istemci anahtarı——Bir sahibin eşya örneklerini listeler (imleç sayfalama).
POST/v1/itemsSunucu anahtarızorunlu—Yeni eşya örneği üretir (mint).
GET/v1/items/{id}Sunucu veya istemci anahtarı——Tek eşya örneğini okur.
POST/v1/items/{id}/consumeSunucu anahtarızorunluevetEşyayı tüketir.
POST/v1/items/{id}/destroySunucu anahtarızorunluevetEşyayı yok eder.
POST/v1/items/{id}/transferSunucu anahtarızorunluevetEşyayı başka bir sahibe aktarır.
GET/v1/player/inventoryOyuncu oturumu——Oturumdaki oyuncunun envanterini okur.
GET/v1/player/itemsOyuncu oturumu——Oturumdaki oyuncunun eşyalarını listeler (imleç sayfalama).
GET/v1/catalog/itemsSunucu anahtarı veya oyuncu oturumu——Eşya kataloğunu listeler.
GET/v1/catalog/items/{key}Sunucu anahtarı veya oyuncu oturumu——Eşya katalog girdisini okur.

Ticaret

Modül: trading

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/merchantsSunucu anahtarı——Tüccar tanımlar.
GET/v1/merchants/{key}Sunucu veya istemci anahtarı——Tüccarı okur.
POST/v1/merchants/{key}/buySunucu anahtarızorunlu—Oyuncunun tüccardan satın almasını yürütür.
POST/v1/merchants/{key}/sellSunucu anahtarızorunlu—Oyuncunun tüccara satışını yürütür.
POST/v1/tradesSunucu anahtarızorunlu—İki oyuncu arasında takas başlatır.
GET/v1/trades/{id}Sunucu anahtarı——Takası okur.
POST/v1/trades/{id}/acceptSunucu anahtarızorunlu—Takası kabul eder.
POST/v1/trades/{id}/cancelSunucu anahtarızorunlu—Takası iptal eder.
POST/v1/trades/{id}/offerSunucu anahtarızorunlu—Takasa teklif ekler ya da günceller.

Mağaza

Modül: store

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/iap/verifySunucu anahtarızorunlu—Mağaza makbuzunu doğrular ve ürünü teslim eder (IAP).
GET/v1/store/catalogSunucu anahtarı——Ham mağaza kataloğunu verir (yönetim görünümü).
PUT/v1/store/listings/{id}Sunucu anahtarızorunluevetMağaza ilanını günceller.
DELETE/v1/store/listings/{id}Sunucu anahtarızorunluevetMağaza ilanını siler.
POST/v1/store/listings/{listing_id}/reserveSunucu anahtarızorunluevetİlan için rezervasyon açar.
GET/v1/store/pagesOyuncu oturumu——Oturumdaki oyuncunun görebildiği mağaza sayfalarını verir.
POST/v1/store/pagesSunucu anahtarızorunluevetMağaza sayfası oluşturur.
POST/v1/store/pages/reorderSunucu anahtarızorunluevetMağaza sayfalarını yeniden sıralar.
PUT/v1/store/pages/{id}Sunucu anahtarızorunluevetMağaza sayfasını günceller.
DELETE/v1/store/pages/{id}Sunucu anahtarızorunluevetMağaza sayfasını siler.
POST/v1/store/pages/{page_id}/listingsSunucu anahtarızorunluevetSayfaya ilan ekler.
POST/v1/store/pages/{page_id}/listings/reorderSunucu anahtarızorunluevetSayfadaki ilanları yeniden sıralar.
GET/v1/store/preview/{player_id}Sunucu anahtarı——Belirli bir oyuncunun göreceği vitrini önizler.
POST/v1/store/reservations/{id}/cancelSunucu anahtarızorunluevetRezervasyonu iptal eder.
POST/v1/store/reservations/{id}/checkout/currencySunucu anahtarıisteğe bağlıevetRezervasyonu oyun içi para ile tahsil eder.
POST/v1/store/reservations/{id}/checkout/iapSunucu anahtarıisteğe bağlıevetRezervasyonu mağaza makbuzu ile tahsil eder.
POST/v1/player/store/listings/{listing_id}/buyOyuncu oturumuzorunluevetOyuncu, kendi oturumuyla ilanı oyun içi para ile satın alır.
POST/v1/webhooks/iap/{platform}/{environment_id}Mağaza platformu imzası——Mağaza platformunun iade/iptal bildirimini alır.

Ödüller

Modül: rewards

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/login-rewards/{player_id}Sunucu anahtarı——Giriş ödülü durumunu verir.
POST/v1/login-rewards/{player_id}/claimSunucu anahtarı——Giriş ödülünü teslim eder.
POST/v1/npc-rewards/{key}/claimSunucu anahtarızorunlu—NPC ödülünü teslim eder.
GET/v1/npc-rewards/{key}/eligibilitySunucu anahtarı——NPC ödülüne uygunluğu sorgular.
POST/v1/reward-tablesSunucu anahtarı——Ödül tablosunu kaydeder.
GET/v1/reward-tables/{key}Sunucu anahtarı——Ödül tablosunu okur.
POST/v1/reward-tables/{key}/grantSunucu anahtarızorunlu—Ödül tablosundan oyuncuya ödül verir.
GET/v1/questsSunucu anahtarı——Görevleri listeler (offset + limit).
POST/v1/questsSunucu anahtarı——Görev tanımlar.
GET/v1/quests/{key}Sunucu anahtarı——Görevi okur.
POST/v1/quests/{key}/claimSunucu anahtarı——Görev ödülünü teslim eder.
GET/v1/vip/{player_id}Sunucu veya istemci anahtarı——Oyuncunun VIP durumunu verir.
POST/v1/vip/{player_id}/cards/{item_instance_id}/useSunucu veya istemci anahtarızorunlu—VIP kartını kullanır.

Sosyal

Modül: social

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/clan-applications/{application_id}Sunucu anahtarı——Klan başvurusunu okur.
POST/v1/clan-applications/{application_id}/resolveSunucu anahtarızorunlu—Klan başvurusunu kabul ya da reddeder.
GET/v1/clan-invites/{invite_id}Sunucu anahtarı——Klan davetini okur.
POST/v1/clan-invites/{invite_id}/respondSunucu anahtarızorunlu—Klan davetini yanıtlar.
GET/v1/clan-settingsSunucu anahtarı——Klan çalışma ayarlarını okur.
PATCH/v1/clan-settingsSunucu anahtarızorunlu—Klan çalışma ayarlarını günceller.
GET/v1/clansSunucu anahtarı——Klanları listeler.
POST/v1/clansSunucu anahtarızorunlu—Klan kurar.
GET/v1/clans/{clan_id}Sunucu anahtarı——Klanı okur.
PATCH/v1/clans/{clan_id}Sunucu anahtarızorunlu—Klan bilgilerini günceller.
POST/v1/clans/{clan_id}/applicationsSunucu anahtarızorunlu—Klana başvuru oluşturur.
POST/v1/clans/{clan_id}/disbandSunucu anahtarızorunlu—Klanı dağıtır.
POST/v1/clans/{clan_id}/invitesSunucu anahtarızorunlu—Klana davet gönderir.
POST/v1/clans/{clan_id}/leadershipSunucu anahtarızorunlu—Klan liderliğini devreder.
POST/v1/clans/{clan_id}/leaveSunucu anahtarızorunlu—Üyeyi klandan çıkarır (ayrılma).
POST/v1/clans/{clan_id}/members/kickSunucu anahtarızorunlu—Üyeyi klandan atar.
POST/v1/clans/{clan_id}/members/roleSunucu anahtarızorunlu—Üyenin rolünü değiştirir.
POST/v1/clans/{clan_id}/treasury/depositSunucu anahtarızorunlu—Klan kasasına para yatırır.
POST/v1/clans/{clan_id}/treasury/withdrawSunucu anahtarızorunlu—Klan kasasından para çeker.
POST/v1/clans/{clan_id}/xpSunucu anahtarızorunlu—Klana XP ekler.
GET/v1/partiesSunucu anahtarı——Bir oyuncunun gruplarını (party) listeler (player_id).
POST/v1/partiesSunucu anahtarı——Grup kurar.
GET/v1/parties/{party_id}Sunucu anahtarı——Grubu okur.
PATCH/v1/parties/{party_id}Sunucu anahtarı——Grup kapasitesini günceller.
POST/v1/parties/{party_id}/disbandSunucu anahtarı——Grubu dağıtır.
POST/v1/parties/{party_id}/invitesSunucu anahtarı——Gruba davet gönderir.
POST/v1/parties/{party_id}/leaveSunucu anahtarı——Üyeyi gruptan çıkarır (ayrılma).
POST/v1/parties/{party_id}/rewardsSunucu anahtarızorunlu—Grup ödülünü üyelere paylaştırır.
POST/v1/party-invites/{invite_id}/acceptSunucu anahtarı——Grup davetini kabul eder.

Sohbet

Modül: chat

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/chat/channelsOyuncu oturumu——Sohbet kanallarını listeler.
POST/v1/chat/channelsOyuncu oturumu——Sohbet kanalı oluşturur.
GET/v1/chat/channels/{channel_id}/messagesOyuncu oturumu——Kanal mesajlarını listeler (before_ms + limit).
POST/v1/chat/channels/{channel_id}/messagesOyuncu oturumu——Kanala mesaj gönderir (client_message_id ile tekrar güvenli).
GET/v1/chat/wsOyuncu oturumu (çerez veya Bearer)——Sohbet WebSocket’i (kanal aboneliği ve mesaj gönderme).

Rekabet

Modül: competition

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/leaderboardsSunucu anahtarı——Skor tablosu tanımlarını listeler.
POST/v1/leaderboardsSunucu anahtarı——Skor tablosu tanımı kaydeder (değişmez sürüm).
GET/v1/leaderboards/{key}Sunucu anahtarı——Skor tablosu tanımını ve etkin sezonu okur.
GET/v1/leaderboards/{key}/around-meSunucu anahtarı——Bir oyuncunun çevresindeki sıraları verir.
GET/v1/leaderboards/{key}/entriesSunucu anahtarı——Sıralamanın üst sayfasını verir.
GET/v1/player/leaderboards/{key}Oyuncu oturumu——Oturumdaki oyuncunun gördüğü sıralamayı verir.
POST/v1/leaderboards/{key}/scoresSunucu anahtarızorunlu—Oyuncu skoru gönderir.
POST/v1/tournamentsSunucu anahtarı——Turnuva oluşturur.
GET/v1/tournaments/{tournament_id}Sunucu veya istemci anahtarı——Turnuvayı okur.
POST/v1/tournaments/{tournament_id}/matches/{match_id}/resultSunucu anahtarızorunlu—Maç sonucunu bildirir.
POST/v1/tournaments/{tournament_id}/participantsSunucu anahtarızorunlu—Turnuvaya katılımcı kaydeder.
POST/v1/tournaments/{tournament_id}/startSunucu anahtarı——Turnuvayı başlatır.

Bildirimler

Modül: notifications

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/devicesOyuncu oturumu——Bildirim cihazı kaydeder.
DELETE/v1/devices/{token}Oyuncu oturumu——Bildirim cihazı kaydını siler.
GET/v1/notificationsOyuncu oturumu——Oyuncunun bildirim kutusunu listeler.
GET/v1/push/configKimlik gerektirmez (x-crx-env)——Web push için istemci yapılandırmasını verir (sırsız).
POST/v1/notificationsSunucu anahtarı——Oyuncuya bildirim gönderir.
POST/v1/notifications/{id}/readOyuncu oturumu——Bildirimi okundu işaretler.
GET/v1/wsOyuncu oturumu (çerez veya Bearer)——Bildirim WebSocket’i (yalnız sunucudan oyuncuya canlı itme).

Webhook’lar

Modül: webhooks

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/webhooksSunucu anahtarı——Webhook uç noktalarını listeler (sırsız).
POST/v1/webhooksSunucu anahtarı——Webhook uç noktası kaydeder; gizli anahtar yalnız bu yanıtta döner.
GET/v1/webhooks/{id}Sunucu anahtarı——Webhook uç noktasını okur (sırsız).
DELETE/v1/webhooks/{id}Sunucu anahtarı——Webhook uç noktasını siler.

Sahneler ve görseller

Modül: scenes

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/assetsSunucu anahtarı——Görselleri listeler (imleç sayfalama).
PUT/v1/assetsSunucu anahtarı——Görsel yükler (içerik adresli, SHA-256).
DELETE/v1/assets/{sha256}Sunucu anahtarı——Görseli siler (kullanımdaysa reddedilir).
PUT/v1/scenesSunucu anahtarı——Sahneyi oluşturur ya da var olanı döndürür.

Eklentiler ve realtime

Modül: plugins

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/plugins/{plugin_id}/commands/{message_type}Oyuncu oturumu veya sunucu anahtarı (işleme göre)zorunluevetEklenti komutunu çalıştırır (Idempotency-Key zorunlu).
GET/v1/plugins/{plugin_id}/commands/{message_type}/resultOyuncu oturumu veya sunucu anahtarı (işleme göre)——Komut sonucunu idempotency anahtarıyla sorgular.
GET/v1/plugins/{plugin_id}/queries/{message_type}Oyuncu oturumu veya sunucu anahtarı (işleme göre)——Eklenti sorgusunu çalıştırır (parametreler sorgu dizesinde).
POST/v1/plugins/{plugin_id}/queries/{message_type}Oyuncu oturumu veya sunucu anahtarı (işleme göre)——Eklenti sorgusunu çalıştırır (parametreler gövdede).
GET/v1/plugins/{plugin_id}/schemaOyuncu oturumu veya sunucu anahtarı (işleme göre)——Eklentinin şema belgesini (artifact) verir.
POST/v1/realtime/ticketsOyuncu oturumu (çerez kipinde CSRF)——Realtime WebSocket için tek kullanımlık bilet üretir.
GET/v1/realtime/wsTek kullanımlık bilet——Realtime WebSocket’i (openplay.realtime.v1).

Çekirdek: Keşif

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/openapi.jsonKimlik gerektirmez——Küratörlü OpenAPI belgesi (çekirdek REST yüzeyi; eklentiler ve WebSocket dahil değil).

Çekirdek: Oyuncu kimliği ve oturumu

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/auth/account/deleteOyuncu oturumu——Oyuncunun kendi hesabını siler; geri alınamaz.
GET/v1/auth/callback/{provider}Kimlik gerektirmez——OAuth dönüş adresi (code + state ile oturumu tamamlar).
POST/v1/auth/callback/{provider}Kimlik gerektirmez——OAuth form_post dönüş adresi (Apple).
POST/v1/auth/change-passwordOyuncu oturumu——Parolayı değiştirir; tüm oturumlar düşer.
GET/v1/auth/csrfKimlik gerektirmez (x-crx-env)——Yeni CSRF jetonu verir.
GET/v1/auth/identitiesOyuncu oturumu——Oyuncunun bağlı kimlik sağlayıcılarını listeler.
POST/v1/auth/forgot-passwordKimlik gerektirmez (x-crx-env)——Parola sıfırlama bağlantısı gönderir.
POST/v1/auth/guest/upgrade/emailOyuncu oturumu——Misafir hesabı aynı oyuncu kimliğiyle e-posta hesabına yükseltir.
GET/v1/auth/jwksKimlik gerektirmez (x-crx-env)——Erişim JWT’sini çevrimdışı doğrulamak için açık anahtar kümesini verir.
POST/v1/auth/link/{provider}Oyuncu oturumu——Mevcut oyuncuya sağlayıcı kimliği bağlamayı başlatır.
POST/v1/auth/logoutOturum ya da yenileme çerezi——Oturumu kapatır (bilinmeyen jeton sessizce başarılıdır).
GET/v1/auth/magic-link/verifyKimlik gerektirmez (x-crx-env)——Sihirli bağlantıyı doğrular ve oturum açar.
POST/v1/auth/native/{provider}Kimlik gerektirmez (x-crx-env)——Yerel platform kanıtıyla (Apple, Game Center, Play Games) giriş yapar.
GET/v1/auth/oauth/providersKimlik gerektirmez (x-crx-env)——Ortamda etkin OAuth sağlayıcılarını listeler.
POST/v1/auth/refreshOturum ya da yenileme çerezi——Oturumu yeniler (jeton döndürme).
POST/v1/auth/reset-passwordKimlik gerektirmez (x-crx-env)——Sıfırlama jetonunu tüketir ve yeni parolayı yazar.
GET/v1/auth/sessionOyuncu oturumu——Oturum kapsamını ve oyuncunun profil özetini verir.
POST/v1/auth/session/display-nameOyuncu oturumu——Oyuncunun görünen adını değiştirir.
POST/v1/auth/sign-in/emailKimlik gerektirmez (x-crx-env)——E-posta ve parola ile giriş yapar.
POST/v1/auth/sign-in/guestKimlik gerektirmez (x-crx-env)——Misafir olarak giriş yapar (aynı sır aynı oyuncuya döner).
POST/v1/auth/sign-in/magic-linkKimlik gerektirmez (x-crx-env)——Sihirli bağlantı gönderir.
POST/v1/auth/sign-in/oauth/{provider}Kimlik gerektirmez (x-crx-env)——OAuth yetkilendirme adresi ve state üretir.
POST/v1/auth/sign-up/emailKimlik gerektirmez (x-crx-env)——E-posta ve parola ile kayıt olur, oturum açar.
POST/v1/auth/unlink/{provider}Oyuncu oturumu——Sağlayıcı kimlik bağlantısını kaldırır.

Çekirdek: Sunucu tarafı oyuncu yönetimi

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/auth/admin/ban-playerSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncuyu yasaklar.
POST/v1/auth/admin/create-playerSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Yönetici eliyle oyuncu hesabı açar.
GET/v1/auth/admin/list-player-sessionsSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun oturumlarını listeler.
GET/v1/auth/admin/list-playersSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncuları listeler (imleç sayfalama).
GET/v1/auth/admin/player-security-eventsSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Bir oyuncunun güvenlik olaylarını listeler.
POST/v1/auth/admin/rename-playerSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun görünen adını değiştirir.
POST/v1/auth/admin/reset-passwordSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun parolasını sıfırlar.
POST/v1/auth/admin/revoke-player-sessionsSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun tüm oturumlarını kapatır.
GET/v1/auth/admin/security-eventsSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Ortamın güvenlik olay günlüğünü listeler.
POST/v1/auth/admin/set-player-roleSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun rolünü değiştirir.
POST/v1/auth/admin/unban-playerSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Oyuncunun yasağını kaldırır.
POST/v1/auth/admin/upgrade-guestSunucu anahtarı veya oyuncu oturumu (yönetici rolü)——Misafir oyuncuyu e-posta hesabına yükseltir.

Çekirdek: İçerik grubu kataloğu

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/catalog/groupsSunucu anahtarı veya oyuncu oturumu——Yayınlanmış içerik gruplarını listeler.
GET/v1/catalog/groups/{group}Sunucu anahtarı veya oyuncu oturumu——İçerik grubunu okur.
GET/v1/catalog/groups/{group}/entries/{key}Sunucu anahtarı veya oyuncu oturumu——İçerik grubu girdisini okur.

Çekirdek: Oyuncu verisi (bulut kaydı)

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/player/dataOyuncu oturumu——Oyuncunun bulut kaydı anahtarlarını (sürüm, boyut, zaman) listeler.
GET/v1/player/data/{key}Oyuncu oturumu——Bir bulut kaydı anahtarını okur; yanıt ETag taşır.
PUT/v1/player/data/{key}Oyuncu oturumu—evetBulut kaydı yazar; If-None-Match: * (oluştur) ya da If-Match (güncelle) zorunludur.
DELETE/v1/player/data/{key}Oyuncu oturumu—evetBulut kaydı anahtarını siler; If-Match zorunludur.
GET/v1/players/{player_id}/dataSunucu anahtarı——Bir oyuncunun bulut kaydı anahtarlarını sunucu anahtarıyla listeler (salt okuma).
GET/v1/players/{player_id}/data/{key}Sunucu anahtarı——Bir oyuncunun bulut kaydı anahtarını sunucu anahtarıyla okur (salt okuma).

Çekirdek: Genel içerik okuma

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
GET/v1/assets/{sha256}Kimlik gerektirmez——Görsel baytlarını verir (içerik adresli, kimliksiz).
GET/v1/model-files/{sha256}Kimlik gerektirmez——3D model dosyasını (GLB) verir (içerik adresli, kimliksiz).

Çekirdek: Oyuncu organizasyonları (kullanımdan kalkıyor)

Bu uçlar Deprecation: @1793491200 yanıt başlığı taşır; çalışmaya devam eder.

YöntemYolKimlikIdempotency-KeyIf-MatchAçıklama
POST/v1/auth/organization/accept-invitationOyuncu oturumu——Kullanımdan kalkıyor: organizasyon davetini kabul eder.
POST/v1/auth/organization/add-team-memberOyuncu oturumu——Kullanımdan kalkıyor: takıma üye ekler.
POST/v1/auth/organization/cancel-invitationOyuncu oturumu——Kullanımdan kalkıyor: daveti iptal eder.
POST/v1/auth/organization/createOyuncu oturumu——Kullanımdan kalkıyor: organizasyon kurar.
POST/v1/auth/organization/create-teamOyuncu oturumu——Kullanımdan kalkıyor: takım kurar.
POST/v1/auth/organization/deleteOyuncu oturumu——Kullanımdan kalkıyor: organizasyonu siler.
POST/v1/auth/organization/delete-teamOyuncu oturumu——Kullanımdan kalkıyor: takımı siler.
POST/v1/auth/organization/invite-memberOyuncu oturumu——Kullanımdan kalkıyor: üye davet eder.
GET/v1/auth/organization/listOyuncu oturumu——Kullanımdan kalkıyor: oturumdaki oyuncunun üyeliklerini listeler.
GET/v1/auth/organization/list-membersOyuncu oturumu——Kullanımdan kalkıyor: üyeleri listeler.
GET/v1/auth/organization/list-team-membersOyuncu oturumu——Kullanımdan kalkıyor: takım üyelerini listeler.
GET/v1/auth/organization/list-teamsOyuncu oturumu——Kullanımdan kalkıyor: takımları listeler.
POST/v1/auth/organization/reject-invitationOyuncu oturumu——Kullanımdan kalkıyor: daveti reddeder.
POST/v1/auth/organization/remove-memberOyuncu oturumu——Kullanımdan kalkıyor: üyeyi çıkarır.
POST/v1/auth/organization/remove-team-memberOyuncu oturumu——Kullanımdan kalkıyor: takımdan üye çıkarır.
POST/v1/auth/organization/set-activeOyuncu oturumu——Kullanımdan kalkıyor: oturuma etkin organizasyonu yazar.
POST/v1/auth/organization/updateOyuncu oturumu——Kullanımdan kalkıyor: organizasyonu yeniden adlandırır.
POST/v1/auth/organization/update-member-roleOyuncu oturumu——Kullanımdan kalkıyor: üye rolünü değiştirir.
POST/v1/auth/organization/update-teamOyuncu oturumu——Kullanımdan kalkıyor: takımı yeniden adlandırır.

Webhook olayları

Bu sayfa, bir webhook uç noktasına abone olurken kullanabileceğiniz olay türlerini ve her teslimatla gelen başlıkları listeler. Kayıt ve imza doğrulama adımları için webhook rehberine bakın.

Teslim başlıkları

HeaderAçıklama
X-Crx-Signaturet=<unix-sn>,v1=<hex>; v1 = HMAC-SHA256(gizli, "{t}.{ham gövde}").
X-Crx-Event-IdOlay kimliği (UUID); tekrar eden teslimatları ayıklamak için kullanın.
X-Crx-Event-TypeOlay türü (aşağıdaki tablodaki değerlerden biri).

Webhook olayları

Toplam olay türü: 23.

Olay türüKaynakNe zaman gönderilir
flag_setKarakterBir bayrak (flag) ayarlandı.
attribute_setKarakterBir öznitelik değeri ayarlandı.
resource_changedKarakterBir kaynağın değeri değişti (yeni değer olayda yer alır).
modifier_addedKarakterKaraktere bir değiştirici (modifier) eklendi.
modifiers_removedKarakterBir kaynaktan gelen değiştiriciler kaldırıldı.
xp_addedKarakterKaraktere XP eklendi.
level_upKarakterKarakter seviye atladı.
ability_unlockedKarakterKarakterin bir yeteneği açıldı.
event_emittedKarakterBir kural özel bir olay yayınladı.
character_rolled_backKarakterKarakter önceki bir sürüme geri alındı (yeni olay olarak yazılır).
store_page_createdMağazaMağaza sayfası oluşturuldu.
store_page_updatedMağazaMağaza sayfası güncellendi.
store_page_deletedMağazaMağaza sayfası silindi.
store_pages_reorderedMağazaMağaza sayfaları yeniden sıralandı.
store_listing_createdMağazaMağaza ilanı oluşturuldu.
store_listing_updatedMağazaMağaza ilanı güncellendi.
store_listing_deletedMağazaMağaza ilanı silindi.
store_listings_reorderedMağazaBir sayfadaki ilanlar yeniden sıralandı.
store_purchase_initiatedMağazaSatın alma rezervasyonu açıldı.
store_payment_startedMağazaSatın alma için ödeme başladı.
store_purchase_completedMağazaSatın alma tamamlandı ve ürün teslim edildi.
store_reservation_cancelledMağazaSatın alma rezervasyonu iptal edildi.
reward_grantedÖdülBir ödül tablosundan oyuncuya ödül verildi.

Sözlük

Bu sayfa, dokümanda geçen terimleri kısaca tanımlar ve ilgili sayfaya bağlar. Terimler alfabetik değil, konuya göre gruplanmıştır.

Yapı: stüdyo, oyun, ortam

TerimTanım
Stüdyo (studio)En üstteki sahiplik düzeyi; bir ekibi ya da şirketi temsil eder. Birden çok oyun barındırır. Yönetimi panelde Sunucu bölümündedir.
Oyun (game)Stüdyonun altındaki ürün. Kendi ortamları, modülleri ve API anahtarları vardır.
Ortam (environment)Bir oyunun yalıtılmış çalışma kopyası (ör. geliştirme, canlı). Veriler ortamlar arasında paylaşılmaz. Her API anahtarı ve oyuncu oturumu tek bir ortama bağlıdır.
Ortam alan adı (environment domain)Ortama bağlı alan adı; aynı zamanda tarayıcı isteklerinde izinli köken (Origin) listesi olarak kullanılır.
Modül (module)Açılıp kapatılabilen ürün parçası: karakterler, ekonomi, eşyalar, ticaret, mağaza, ödüller, sosyal, sohbet, rekabet, bildirimler, webhook’lar, sahneler ve eklentiler. Kapalı modülün uçları 404 module_disabled döner.
Panel (admin panel)Oyunu yönettiğiniz web arayüzü. Panel API’si oyuncu API’sinden ayrıdır ve bu dokümanın kapsamında değildir.

Kimlik ve anahtarlar

TerimTanım
Sunucu anahtarı (server key)crx_live_<önek>_<gizli> biçimli, yalnız sizin sunucunuzda tutulan gizli API anahtarı. Tüm sunucu uçlarını çağırabilir. İstemciye ya da oyun paketine asla konmaz.
İstemci anahtarı (client key)İstemciye gömülebilen API anahtarı. Hassas komutlar (ör. add_xp) çalıştıramaz; ancak bazı uçlarda tür kapısı yoktur, bu yüzden yalnız gerçekten gerekliyse kullanın (uç noktalar).
Oyuncu oturumu (player session)Oyuncunun girişinden sonra oluşan oturum. Çerezle (__Secure-crx.session) ya da Bearer oturum jetonuyla taşınır ve x-crx-env başlığı ister.
Erişim JWT’si (access JWT)Oturum açıldığında gövdede dönen, oyun sunucunuzun JWKS ile çevrimdışı doğrulayabildiği kısa ömürlü jeton. Oturum jetonu ile aynı şey değildir.
JWKSErişim JWT’sini doğrulamak için açık anahtar kümesi (GET /v1/auth/jwks).
Misafir (guest)Kayıt gerektirmeyen oyuncu; aynı gizli değerle her seferinde aynı oyuncuya döner ve sonradan e-posta hesabına yükseltilebilir.
CSRFTarayıcının kullanıcı adına istenmeyen istek göndermesini engelleyen çift gönderim: çerez + x-csrf-token başlığı. Çerez oturumunda değiştirici isteklerde gerekir.
Origin denetimiDeğiştirici isteklerde Origin başlığının ortamın izinli kökenlerinden olması kuralı. Sunucu CORS başlıklarını yalnız aynı alan adı listesindeki origin’lere gönderir; CORS yetki değildir, bu denetim ayrıca çalışır.
Aynı köken (same-origin)Oyunun ve API’nin aynı alan adı altında (ters vekil ile) sunulması; mimari ve barındırma.
Oyuncu bileti (realtime ticket)Realtime soketine bağlanmak için alınan tek kullanımlık, 30 saniyelik kimlik belirteci (protokol).

Güvenilirlik: tekrar ve çakışma

TerimTanım
Idempotency (tekrar güvenliği)Aynı isteğin birden çok kez gönderilse de tek kez uygulanması. Idempotency-Key başlığıyla sağlanır (idempotency ve eşzamanlılık).
Idempotency-Keyİstemcinin ürettiği, bir işlemi tanımlayan 1 ile 200 karakterlik anahtar.
İyimser kilit (optimistic lock)Kaynağın sürümünü isteğe ekleyip, sürüm değiştiyse isteğin reddedilmesi. If-Match ya da expectedVersion ile yapılır.
expectedVersionBeklenen kaynak sürümü: HTTP’de If-Match başlığı, realtime Command çerçevesinde alan, SDK’da seçenek.
Fail-openBir sınırlayıcı bağımlılığına (Redis) ulaşılamazsa isteğin sınırlanmadan geçmesi (hız sınırları).

Eklentiler

TerimTanım
Eklenti (plugin)Oyuna özgü komut, sorgu ve realtime modülleri sunan, sunucuya derlenmiş güvenilir paket (genel bakış). Üçüncü taraf eklenti yükleme henüz yoktur.
Komut (command)Durumu değiştiren, kalıcı ve tekrar-güvenli işlem.
Sorgu (query)Salt-okunur okuma.
Şema belgesi (artifact)Eklentinin tüm işlem ve şemalarını tarif eden JSON belge (GET /v1/plugins/{id}/schema).
schemaSha256Şema belgesindeki schemas bölümünün özeti; sürüm sabitleme anahtarı (bağlamalar).
Bağlama (binding)Şema belgesinden üretilen tipli istemci kodu.
Sabitleme (pin)İstemcinin hangi eklenti ve şema özetiyle derlendiğinin kaydı; uyuşmazlıkta upgrade_required hatası.
Erişim türü (access)Eklenti işleminin kimlik sınırı: player, server ya da public.
Eklenti durumu (grant state)Eklentinin bir ortamdaki durumu: etkin, salt okunur, boşaltılıyor, kapalı.
plugin-v1-decimal-int64Eklenti ve realtime int64 değerlerini ondalık metin olarak taşıyan kodlama profili (sayılar ve kodlama).

Realtime

TerimTanım
Abonelik (subscription)Join ile açılan, bir modül ve hedefe bağlı veri akışı; subscriptionId ile tanınır.
Hedef (target)Aboneliğin neye bağlandığı: PlayerSelf, Room, Scene ya da InterestSet.
Anlık görüntü (snapshot)Geçici durumun tam yayını; streamEpoch ve revision ile sıralanır.
Kalıcı olay (durable event)Veritabanından kurtarılabilen, sequence sıralı eklenti olayı.
İmleç (cursor)Kalıcı akışta kaldığınız yeri gösteren opak, oyuncuya bağlı değer (yeniden bağlanma).
Dönem (epoch)sessionEpoch: bir soketin yazma dönemi; yeni soket eskisinin girdilerini geçersiz kılar. streamEpoch: durum akışının sıfırlanma sayısı.
Boşta kapanma (idle close)Girdi kabul eden abonelikte oyuncu etkinliği gelmeyince sunucunun 4100 ile soketi kapatması.
Geri basınç (backpressure)İstemci kuyruğunun dolması ya da sunucunun çerçeve hızı sınırı nedeniyle işin reddedilmesi.

Veri ve oyun içi kavramlar

TerimTanım
Tanım (definition)Karakterin şeması ve başlangıç değerleri; değişmez sürümlerle saklanır.
Özellik (trait)Tanımlara karıştırılan yeniden kullanılabilir şema parçası.
Değiştirici (modifier)Bir istatistiği etkileyen geçici ya da kalıcı bonus (ör. eşyadan gelen).
Kural (rule)Karakter olaylarına bağlı otomasyon (oyun kuralları).
Küçük birim (minor unit)Para değerinin tam sayı karşılığı (ör. kuruş).
Baz puan (basis point)10000 = %100 olan yüzde birimi.
Yumuşak silme (soft delete)Kaydın görünmez olması ama olay geçmişinin korunması.
İçerik grubu (content group)Panelden yayınlanan, oyuna katalog olarak sunulan veri kümesi (içerik grupları).
WebhookOpen Play’in olayları kendi sunucunuza imzalı HTTP isteğiyle bildirmesi (webhook rehberi).
At-least-onceWebhook teslimatının en az bir kez, gerekirse birden çok kez yapılması; X-Crx-Event-Id ile tekilleştirin.

Güvenlik

Bu sayfa, Open Play ile entegre bir oyunu güvenli işletmek için bilmeniz gerekenleri toplar: anahtarları nerede tutacağınız, nasıl döndüreceğiniz, tarayıcı oturumunun nasıl korunduğu ve webhook isteklerini nasıl doğrulayacağınız. Üretime çıkmadan önce üretim kontrol listesi ile birlikte okuyun.

1. API anahtarları

İki tür anahtar vardır ve ikisi de tek bir ortama bağlıdır:

TürNerede tutulurNe yapabilir
Sunucu anahtarı (server)Yalnız sunucunuzda (gizli yönetim aracı ya da ortam değişkeni)Tüm sunucu uçları
İstemci anahtarı (client)İstemciye gömülebilirHassas komutlar (ör. add_xp, set_attribute) çalışmaz; ama bazı uçlarda anahtar türü kapısı yoktur (uç noktalar)

Uyarı

İstemci anahtarı “zararsız” değildir. Karakter oluşturma/silme, envanter hareketleri, takas, tüccar alım-satımı ve cüzdan aktarımı gibi uçlarda kodda anahtar türü kapısı yoktur; istemciye gömülen anahtar bunları çağırabilir. Bu uçları yalnız sunucunuzdan çağırın, oyuncu için ise oyuncu oturumuyla çalışan uçları (/v1/player/..., eklentiler, PlayerClient) kullanın.

Anahtar biçimi ve saklama

  • Biçim: crx_live_<önek>_<gizli>. Sunucu yalnız anahtarın özetini saklar (argon2id); gizli kısmı geri üretemez.
  • Gizli değer yalnız oluşturma anında bir kez gösterilir. Kaybederseniz geri alınamaz; yeni anahtar üretirsiniz.
  • Anahtarı depoya, günlüğe, hata raporuna, istemci paketine, ekran görüntüsüne koymayın. Authorization başlığını günlüklerden süzün.
  • Ortam başına ve hizmet başına ayrı anahtar kullanın (ör. canlı API sunucusu, toplu iş, yönetim betiği); bir sızıntıda yalnız birini iptal edersiniz.
  • Anahtara son kullanma tarihi verin. Panelde oluştururken expiresAt ayarlanabilir; süresi dolan anahtar reddedilir.

Yaşam döngüsü

Anahtarlar panelde Oyun ayarları: API anahtarları ekranında yönetilir (API anahtarları):

EylemEtki
OluşturYeni anahtar; gizli değer bir kez gösterilir
Devre dışı bırak / etkinleştirGeçici ve geri alınabilir
Döndür (rotate)Aynı adla yeni anahtar üretir ve eskisini aynı işlemde hemen iptal eder
İptal et (revoke)Kalıcı; geri alınamaz
SilYumuşak silme + iptal

Anahtar durumu her istekte veritabanından yeniden okunur: iptal ya da devre dışı bırakma bir sonraki istekte etkili olur (yalnız gizli değerin doğrulama sonucu 60 sn önbelleğe alınır, durum alınmaz). Her işlem için gerekçe yazılır ve işlem geçmişine düşer.

Kesintisiz döndürme

Paneldeki Döndür eylemi eski anahtarı hemen iptal eder; yeni anahtarı dağıtana kadar çağrılarınız 401 alır. Kesintisiz döndürmek için:

  1. Panelde farklı bir adla ikinci bir anahtar oluşturun (aynı ad ve sahiple aynı anda tek etkin anahtar olabilir).
  2. Yeni anahtarı gizli yönetim aracınıza yazın ve hizmetlerinizi yeniden başlatarak dağıtın.
  3. Hiçbir hizmet eski anahtarı kullanmadığını doğrulayın (işlem geçmişi ve kendi günlükleriniz).
  4. Eski anahtarı iptal edin.

Sızıntı olursa

  1. Sızan anahtarı hemen iptal edin (etkisi bir sonraki istektedir).
  2. Yeni anahtarı yukarıdaki sırayla dağıtın.
  3. İşlem geçmişinde ve kendi günlüklerinizde, sızıntı anından sonraki beklenmeyen işlemleri (cüzdan aktarımları, eşya üretimi, webhook kaydı) arayın.
  4. Anahtarın erişebildiği ortamlarda webhook uç noktalarını gözden geçirin; saldırgan bir uç nokta kaydetmiş olabilir (kayıtlı sırlar bir kez gösterilir, ama yeni kayıt yeni sır üretir).

2. Oyuncu oturumu ve tarayıcı güvenliği

  • Oyuncu girişinden sonra sunucu üç çerez yazar: __Secure-crx.session (oturum), __Secure-crx.refresh (yenileme) ve __Secure-crx.csrf (CSRF). Üçü de HttpOnly, Secure, Path=/ ve SameSite=Lax taşır (betikler okuyamaz); CSRF jetonunun betiğe verilen düz değeri çerezde değil, yanıt başlığı ve gövdesindedir. __Secure- öneki HTTPS gerektirir: düz HTTP’de oturum kurulamaz.
  • Oturum 30 gün kayar: oyuncu günde en az bir kez istek yaparsa uzar. Mutlak tavan 180 gündür: ne kadar aktif olursa olsun 180 gün sonra yeniden giriş gerekir. Açık bir realtime soketi oturumu yaşatmaz. Süreler sunucu kodunda sabittir.
  • Ortam isterse tek aktif oturum kuralı açabilir (yeni giriş eskisini düşürür); bu, panelde giriş ayarlarındandır.
  • Erişim JWT’si (EdDSA) oturum jetonu değildir; oyun sunucunuz onu GET /v1/auth/jwks ile çevrimdışı doğrular (sunucu doğrulaması).

CSRF

Çerez oturumunda her değiştirici (POST, PUT, PATCH, DELETE) istek iki koruma geçer:

  1. Origin denetimi: Origin (ya da Referer) başlığındaki host, ortamın izinli kökenlerinden olmalıdır. Ortama bağlı bir host yalnız o ortama yönelebilir. Sec-Fetch-Site: cross-site ve Sec-Fetch-Mode: navigate birlikteyse engellenir. Origin hiç göndermeyen istemciler (curl, sunucudan sunucuya) bu denetimden geçer; onları koruyan katman çift gönderimdir.
  2. Çift gönderim (double submit): __Secure-crx.csrf çerezi ile x-csrf-token başlığı birlikte gelmeli ve eşleşmelidir. Jeton tek kullanımlıktır ve her başarılı doğrulamada döner: yanıttaki yeni Set-Cookie ve x-csrf-token değerini bir sonraki istek için saklayın. Yeni jeton için GET /v1/auth/csrf.

TypeScript SDK’sı bunu otomatik yönetir; elle HTTP yazıyorsanız oturumlar ve CSRF sayfasına bakın. Hata durumu: 403 ve düz metin permission denied.

CORS ve aynı köken

Sunucu CORS başlıklarını yalnız ortam alan adı listesindeki (ve operatörün genel izinli listesindeki) https://<alan adı> origin’lerine, yalnız kamusal API’de gönderir: izinli origin aynen yansıtılır, Access-Control-Allow-Credentials: true eklenir, * kullanılmaz, panel yolu (/v1/panel/*) hiçbir koşulda CORS almaz. CORS yetki değildir: Origin, CSRF çift gönderimi ve ortam denetimleri aynen çalışır; CORS yalnız tarayıcının yanıtı okumasına izin verir. Kendi vekilinize ek CORS başlığı eklemeyin: çift başlık tarayıcıda hata verir.

Oturum çerezi SameSite=Lax olduğundan doğrudan çağrı yalnız oyun ile Open Play aynı sitede (aynı kayıtlı alan adının alt alanları) ise çalışır; farklı sitede ters vekil kullanın. Topoloji: mimari ve barındırma.

İzinli kökenler ortam alan adlarınızdır. Panelde ortam için tanımladığınız alan adı hem o ortama yönlendirmenin hem de Origin denetiminin dayanağıdır (Oyun ayarları: ortamlar). Bilinmeyen bir host’tan gelen değiştirici istek reddedilir.

3. Webhook doğrulaması

Open Play, abone olduğunuz olayları uç noktanıza imzalı bir POST ile gönderir. Her isteği doğrulamak zorundasınız; imzasız isteği işlemeyin.

BaşlıkAnlam
X-Crx-Signaturet=<unix saniye>,v1=<hex>
X-Crx-Event-IdOlay kimliği (UUID); tekilleştirme anahtarı
X-Crx-Event-TypeOlay türü (webhook olayları)

İmza: HMAC-SHA256(gizli, "{t}.{ham gövde}"), küçük harfli onaltılık. Anahtar, kayıtta aldığınız whsec_… metninin kendisidir (bayt olarak). Kurallar:

  1. Ham gövdeyi imzalayın/doğrulayın: JSON’u çözüp yeniden yazarsanız baytlar değişir ve imza tutmaz.
  2. Sabit zamanlı karşılaştırma kullanın (timingSafeEqual, hmac.compare_digest).
  3. t değerini kendi saatinizle karşılaştırın ve pencere dışını reddedin (5 dakika önerilir; bu alıcı politikasıdır, sunucu zorlamaz).
  4. At-least-once: aynı olay birden çok kez gelebilir. X-Crx-Event-Id ile tekilleştirin ve işlemi tekrar-güvenli yapın.
  5. Yalnız 2xx yanıt başarıdır. Yanıtı hızlı verin (işi kuyruğa alın); diğer durumlar yeniden denenir.

Teslimat davranışı:

DavranışDeğer
Yeniden denemeÜstel geri çekilme: 1 sn ile başlar, 2’nin kuvvetiyle artar, 5 dakikada tavanlanır, ±%20 sapma
En çok deneme10; sonra teslimat failed (ölü mektup kuyruğu, DLQ) olur
Yönlendirme (redirect)Takip edilmez
Gövde sınırı64 KiB üstü gövde gönderilmez
Uç nokta adresiYalnız https; özel, loopback ve link-local IP adresleri ile kullanıcı bilgisi (kullanici:parola@) içeren adresler kayıtta reddedilir (SSRF koruması)
Gizli anahtarKayıt yanıtında yalnız bir kez döner; döndürme = uç noktayı silip yeniden kaydetmek

Not

Webhook gizli anahtarını döndürmek için yerinde bir “rotate” yoktur. Kesintisiz geçiş: yeni uç noktayı (yeni gizli anahtarla) kaydedin, alıcınız ikisini birden doğrulasın, eski uç noktayı silin.

Doğrulama örneği (Node, node:crypto):

import { createHmac, timingSafeEqual } from "node:crypto";

/** İmzalı webhook isteğinin doğrulama sonucu. */
export type WebhookCheck = { ok: true; eventId: string; eventType: string } | { ok: false; reason: string };

const TOLERANCE_SECONDS = 300; // yeniden oynatma penceresi: alıcı politikasıdır, 5 dakika önerilir

/**
 * Open Play webhook isteğini doğrular. `rawBody`, ayrıştırılmamış HAM bayt dizisi olmalıdır:
 * JSON'u çözüp yeniden yazarsanız imza tutmaz.
 * İmza: HMAC-SHA256(secret, "{t}.{ham gövde}"), başlık biçimi `t=<unix saniye>,v1=<hex>`.
 */
export function verifyWebhook(
  secret: string,
  rawBody: Buffer,
  headers: { signature: string | undefined; eventId: string | undefined; eventType: string | undefined },
  nowSeconds: number = Math.floor(Date.now() / 1000),
): WebhookCheck {
  const parts = Object.fromEntries((headers.signature ?? "").split(",").map((part) => part.trim().split("=", 2) as [string, string]));
  const timestamp = Number(parts["t"]);
  const received = parts["v1"];
  if (!Number.isInteger(timestamp) || received === undefined) return { ok: false, reason: "imza başlığı bozuk" };
  if (Math.abs(nowSeconds - timestamp) > TOLERANCE_SECONDS) return { ok: false, reason: "zaman damgası pencere dışında" };
  const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(received, "utf8");
  if (a.length !== b.length || !timingSafeEqual(a, b)) return { ok: false, reason: "imza uyuşmuyor" };
  if (headers.eventId === undefined || headers.eventType === undefined) return { ok: false, reason: "olay başlıkları eksik" };
  return { ok: true, eventId: headers.eventId, eventType: headers.eventType };
}

Teslimat durumu ve DLQ’yu panelde izleyin: Webhook ve olay kuyruğu. Kayıt ve uç nokta yönetimi için webhook rehberi.

4. Sırların döndürülmesi: özet

SırNasıl döndürülür
Sunucu/istemci API anahtarıİkinci anahtar oluştur, dağıt, eskiyi iptal et (yukarıda)
Webhook gizli anahtarıYeni uç nokta kaydet, alıcıyı ikisini kabul edecek şekilde güncelle, eskiyi sil
Oyuncu parolasıOyuncu kendi parolasını değiştirir (tüm oturumlar düşer) ya da parola sıfırlama akışı (e-posta ve parola)
Oyuncu oturumlarıKullanıcı çıkış yapar; yönetici oyuncunun oturumlarını iptal eder (panel ya da POST /v1/auth/admin/revoke-player-sessions)

5. Neleri günlüğe yazmayın

API anahtarı, oturum ve yenileme jetonu, CSRF jetonu, realtime bileti, webhook gizli anahtarı, OAuth state ve code değerleri, parola ve misafir gizli değeri. SDK’lar hata nesnelerinde bunları içermez; kendi günlükleme kodunuzda da isteği ham yazdırmayın.

6. Diğer notlar

  • Hata ayrıntısı sızdırılmaz: sunucu 500 yanıtında iç ayrıntı vermez; hesap varlığı da sızdırılmaz (yanlış parola ile bilinmeyen adres aynı yanıtı alır).
  • Yasaklı hesap: giriş uçlarında, sahiplik kanıtlandıktan sonra 403 account_banned; diğer uçlarda ayrım yapılmaz (hatalar).
  • Güvenlik şalterleri: panelde ortam başına bazı koruma kurallarını gevşetebilir ya da geri zorlayabilirsiniz; her değişiklik gerekçeyle kaydedilir (Güvenlik şalterleri).
  • Eklentiler sunucuya derlenmiş güvenilir koddur; üçüncü taraf eklenti çalıştırma ve yalıtım henüz yoktur (eklentiler).
  • Panel yönetici hesapları için ayrı bir kimlik hattı ve iki aşamalı doğrulama vardır; oyuncu hesaplarında iki aşamalı doğrulama yoktur.

Üretim kontrol listesi

Bu sayfa, canlıya çıkmadan önce tek tek işaretleyeceğiniz maddeleri toplar. Her madde kısa bir nedenle gelir ve ilgili sayfaya bağlanır. Listeyi lansmandan önce bir kez ve her büyük değişiklikten sonra yeniden gözden geçirin.

1. Ağ ve barındırma

  • Her şey HTTPS. Oturum çerezleri __Secure- öneklidir ve Secure bayrağı taşır; düz HTTP’de oturum kurulamaz. Soketler wss:// ile açılır. Yerel geliştirmede http://localhost kullanıyorsanız tarayıcınızın localhost istisnasını doğrulayın.
  • Tarayıcıdan çağrı yolu seçildi. Ya oyunun alan adı bir ortamın alan adı listesindedir ve oyun Open Play’i doğrudan çağırır (sunucu CORS başlıklarını gönderir; oyun ile Open Play aynı kayıtlı alan adının alt alanları olmalıdır, yoksa SameSite=Lax oturum çerezi gitmez), ya da oyun API’yi kendi alan adı altından ters vekille çağırır (mimari ve barındırma).
  • Vekil şu başlıkları olduğu gibi iletir: Authorization, Cookie (ve yanıtta Set-Cookie), Origin, x-crx-env, x-csrf-token, Idempotency-Key, If-Match, x-crx-schema-version, Sec-WebSocket-* (alt protokol ve uzantı pazarlığı dahil). Yanıt başlıklarını (x-csrf-token, Deprecation, Retry-After, ETag) silmez.
  • WebSocket yükseltmesi vekilde açıktır, Upgrade/Connection başlıkları iletilir ve bağlantı en az 5 dakika boşta kalabilir (sunucu boşta kapatmayı kendisi yapar: limitler).
  • İstek gövdesi sınırı en az 2 MiB. Sunucu 2 MiB üstünü 413 ile reddeder; vekiliniz daha düşük bir sınır koymasın.
  • Zaman aşımı: vekilin yanıt-başlık bekleme süresi uzun işlemleri kesmez (Open Play’in kendi ağ geçidi 65 sn kullanır).
  • İstemci IP’si iletilir. Kimlik uçlarındaki hız sınırları istemci IP’sine bağlıdır. Vekil X-Forwarded-For eklemiyorsa tüm oyuncular tek kovada toplanır (ör. misafir açma dakikada 30). Birden çok vekil zincirliyse operatör güvenilir vekil bloklarını tanımlamalıdır.
  • Panel yolu internete kapalı. /v1/panel/* yolu halka açık ağ geçidinde 404 döndürülür; panele yalnız yönetim ağından ya da ayrı bir alan adından erişilir.
  • Önbellek: çerez ya da Authorization taşıyan API yanıtları paylaşımlı önbelleğe (CDN) alınmaz. Yalnız herkese açık, içerik adresli yanıtlar (/v1/assets/{sha256}, /v1/model-files/{sha256}) ve ETag taşıyan katalog yanıtları uygun önbellek kuralına tabidir.

2. Ortamlar ve alan adları

  • Geliştirme, hazırlık ve canlı için ayrı ortamlar kullanıyorsunuz; veriler paylaşılmaz.
  • Her ortamın alan adı panelde tanımlı (Oyun ayarları: ortamlar); izinli Origin listesi budur.
  • İstemci yapılandırmasına doğru ortam kimliği (x-crx-env değeri) konmuş; canlı istemci hazırlık ortamını göstermiyor.
  • Yalnız gereken modüller açık (modüller). Kapalı modül 404 module_disabled döner; açık bıraktığınız her modül saldırı yüzeyidir.
  • Giriş yöntemleri panelde yapılandırılmış (misafir, e-posta, Google); kullanmadığınız yöntem kapalı (Giriş yöntemleri).

3. Anahtarlar ve sırlar

  • Sunucu anahtarları yalnız sunucuda. İstemci paketinde, mobil uygulamada, WebGL derlemesinde, depoda yoktur (güvenlik).
  • Ortam ve hizmet başına ayrı anahtar; anahtarlara son kullanma tarihi verildi.
  • İstemci anahtarı kullanıyorsanız hangi uçları çağırabildiğini bildiğinizi doğruladınız; oyuncu işlemleri oyuncu oturumuyla yapılıyor (uç noktalar).
  • Anahtar döndürme provası yapıldı: ikinci anahtar, dağıtım, eskiyi iptal.
  • Anahtar ve jetonlar günlüklerde yok; Authorization, Cookie ve x-csrf-token süzülüyor.
  • Webhook gizli anahtarı alıcınızda gizli yönetim aracında; kayıtta yalnız bir kez gösterildiği için yedeği var.

4. Tekrar güvenliği ve eşzamanlılık

  • Para, eşya, ödül ve komut gönderen her çağrı Idempotency-Key taşıyor; anahtar bir kez üretilip tekrarlarda aynen kullanılıyor (idempotency).
  • Belirsiz sonuçta (zaman aşımı, ağ kopması) komut yeni anahtarla değil aynı anahtarla ya da sonuç sorgusuyla tamamlanıyor.
  • Paylaşılan kaynak yazan çağrılar If-Match/expectedVersion gönderiyor; 409’da durum yeniden okunuyor.
  • 429, 503 ve ağ hatalarında üstel geri çekilme + sapma (jitter) var; Retry-After dikkate alınıyor (hız sınırları).
  • Hata işleme durum koduna göre; düz metin mesajı ayrıştırılmıyor (hatalar).

5. Oyuncu kimliği

  • Oturum kaybı akışı kurulu: 401 gelince oturum yenileme, olmazsa yeniden giriş; yasaklı hesap için ayrı ekran (oturumlar ve CSRF).
  • Misafir kimliğinin saklama davranışı ve e-posta ya da Google’a yükseltme akışı kuruldu (misafir ilerlemesi yükseltmeyle korunur).
  • Oyun sunucunuz erişim JWT’sini JWKS ile doğruluyor, süre ve ortam alanlarını denetliyor (sunucu doğrulaması).
  • Kimlik hız sınırları (misafir 30/dk, e-posta girişi 5/dk, kayıt 10/dk, IP başına) beklenen lansman trafiğinizi taşıyor; gerekirse yük testi yapıldı.

6. Realtime

  • İstemci SDK’yı kullanıyor ya da protokolü (bilet, Origin, alt protokol) eksiksiz uyguluyor (protokol).
  • Yeniden bağlanma: her bağlantıda yeni bilet, üstel geri çekilme, kalıcı akışlarda son imleçle Join, ResyncRequired’da imleçsiz Join (yeniden bağlanma).
  • Boşta kapanma (4100) için “Devam et” ekranı var; duraklatma ekranlarında canlı tutma girdisi gönderiliyor.
  • Çerçeve hızı 40/sn altında; oyuncu başına 4 sekme/soket sınırı biliniyor.
  • Bildirim ve sohbet soketleri tarayıcıdan doğrudan açılamaz (başlık kısıtı); gelen kutusu yoklaması ya da REST uçları kullanılıyor (realtime genel bakış).

7. Eklentiler

  • Kullandığınız eklentinin şema özeti (schemaSha256) sürüm manifestinize yazılı ve bağlamalar bu özetle üretildi (bağlamalar).
  • Sunucu güncellemesinden sonra özet karşılaştırması (başlangıçta ya da CI’da) yapılıyor.
  • Eklentinin ortamdaki durumu etkin (Eklentiler); beklenmedik 403 forbidden için ilk bakılacak yer burasıdır.
  • Deprecation başlığı taşıyan çağrılar için geçiş işi açıldı (sürümleme).

8. Webhook’lar

  • Uç nokta https; imza her istekte doğrulanıyor, zaman damgası penceresi denetleniyor, X-Crx-Event-Id ile tekilleştirme yapılıyor (güvenlik).
  • Alıcı hızlı 2xx döner (işi kuyruğa alır); 10 deneme sonunda failed (DLQ) olan teslimatlar için izleme planı var (Webhook ve olay kuyruğu yalnız izleme sunar, yeniden gönderme yoktur); kaçırılan olayları ilgili kaynağı REST ile okuyarak telafi eden bir yolunuz var.

9. Gözlem ve işletme

  • İstek günlüğünüzde durum kodu, süre, çağrılan yol ve (eklenti uçlarında) x-request-id var; gizli değerler yok.
  • 5xx ve 429 oranı için uyarınız var; 503 + Retry-After yanıtlarının yeniden denendiği doğrulandı.
  • Panel yönetici hesapları kişiye özel, iki aşamalı doğrulama açık (Panel yöneticileri).
  • İşlem geçmişi düzenli gözden geçiriliyor; güvenlik şalterleri varsayılan konumunda (bilerek değiştirdiyseniz gerekçesi kayıtlı).
  • SDK sürümü tam sabitlenmiş (^ yok); yükseltmeler değişiklik günlüğünden izleniyor (sürümleme).

10. Lansman öncesi son deneme

  1. Canlı ortama benzer bir hazırlık ortamında uçtan uca akışı koşun: giriş, ödül, satın alma, soket bağlantısı, bağlantı kopması ve yeniden bağlanma.
  2. Bilerek Idempotency-Key ile aynı isteği iki kez gönderin; ikinci yanıtın aynı sonucu döndüğünü doğrulayın.
  3. Sunucu anahtarını bir kez iptal edin ve ikinci anahtara geçişi gözlemleyin.
  4. Webhook alıcınıza hatalı imzayla bir istek gönderin; reddettiğini doğrulayın.
  5. Kimlik uçlarında lansman trafiğinizi taklit eden bir deneme yapın; 429 oranına bakın.

Sorun çıkarsa sorun giderme sayfasına bakın.

Sorun giderme

Bu sayfa, entegrasyonda karşılaşacağınız sorunları belirti, neden, çözüm üçlüsüyle listeler. Belirtiyi hata kodundan ya da durum kodundan bulun. Kod tablolarının tamamı için hata referansı.

Not

Sorun bildirirken şunları ekleyin: çağrılan yol ve yöntem, durum kodu ve gövde (gizli değerler çıkarılmış), istek zamanı (UTC) ve eklenti uçlarında x-request-id/requestId. Sunucu hata ayrıntısı dışarı sızdırmaz; bu bilgiler incelemeyi hızlandırır.

Kimlik ve oturum

BelirtiOlası nedenÇözüm
Sunucu çağrısı 401 unauthorizedAuthorization: Bearer crx_live_… başlığı yok ya da biçimi yanlış; anahtar iptal edilmiş, devre dışı ya da süresi dolmuş; anahtar başka ortama aitBaşlığı ve anahtarı kontrol edin; panelde anahtarın durumuna ve ortamına bakın (API anahtarları). Sunucu “yanlış anahtar” ile “iptal edilmiş“i ayırt etmez
Sunucu çağrısı 403 permission deniedİstemci anahtarıyla sunucu ucu çağırıyorsunuzSunucu anahtarı kullanın (uç noktalar: “Sunucu anahtarı” olan uçlar)
Oyuncu çağrısı 401x-crx-env başlığı yok ya da UUID değil; oturum yok, süresi dolmuş ya da iptal; ortam uyuşmuyorBaşlığı ekleyin; oturumu yenileyin ya da yeniden giriş yapın. Yasaklı hesap da 401 görünür (ayrım yapılmaz)
Oyuncu 403 permission denied (POST)CSRF çifti eksik/eşleşmiyor; jeton zaten kullanılmış; Origin izinli değilAşağıdaki CSRF satırlarına bakın
Girişte 403 ve {"code":"account_banned"}Hesap yasaklıOyuncuya askı ekranı gösterin; yeniden deneme döngüsü kurmayın; yeni misafir açmayın
Oturum beklenenden önce bitiyor30 gün hiç istek yapılmadı; ya da 180 günlük mutlak tavan doldu; parola değişti (tüm oturumlar düşer); ortamda “tek aktif oturum” açık ve başka cihazdan girildiYeniden giriş akışı kurun; davranış güvenlik sayfasında
Tarayıcıda oturum çerezi hiç kurulmuyorHTTP üzerinden çalışıyorsunuz (Secure + __Secure- öneki); API başka bir alan adında (çerez üçüncü taraf olur, SameSite=Lax); tarayıcı çerezleri engelliyorHTTPS ve aynı köken kullanın (mimari ve barındırma)
429 rate limited giriş/kayıttaIP başına hız sınırı doldu (misafir 30/dk, e-posta girişi 5/dk…)Bekleyin; çok oyuncu tek IP’den geliyorsa ya da vekil istemci IP’sini iletmiyorsa hız sınırları ve üretim kontrol listesi
E-posta girişi başarısız, parola doğruBaşarısız deneme kilidi (adres + IP başına 10/15 dk)15 dakika bekleyin ya da parola sıfırlayın

CSRF ve Origin

BelirtiOlası nedenÇözüm
Her POST 403x-csrf-token başlığı ya da __Secure-crx.csrf çerezi gönderilmiyorGET /v1/auth/csrf ile jeton alın; çerezi ve başlığı birlikte gönderin. SDK bunu otomatik yapar
İlk POST geçiyor, ikincisi 403CSRF jetonu tek kullanımlıktır ve her başarılı yanıtla yenilenir; yanıtın yeni değerini saklamadınız ya da iki isteği aynı jetonla paralel gönderdinizHer yanıttaki yeni x-csrf-token ve Set-Cookie değerini kullanın; değiştirici istekleri sıraya koyun
Yalnız tarayıcıda 403, curl’de çalışıyorOrigin ortamın izinli kökenlerinde değil; Sec-Fetch-Site: cross-site ile gezinme isteğiOyunun alan adını ortam alan adı olarak tanımlayın (Oyun ayarları: ortamlar); API’yi aynı kökenden çağırın
Tarayıcı konsolunda “CORS policy” hatasıOyunun alan adı hiçbir ortamın alan adı listesinde ya da genel izinli listede değil (sunucu o origin’e CORS vermez); ya da https://<alan adı> dışında bir origin (http, port, null)Alan adını ortam alan adı olarak ekleyin (Oyun ayarları: ortamlar); olmuyorsa aynı köken ters vekil kurun. Vekilinize ek CORS başlığı eklemeyin

Çağrı hataları

BelirtiOlası nedenÇözüm
404 ve JSON module_disabled (module alanı)Ürün modülü ortamda kapalıPanelde modülü açın (modüller)
404 not foundKaynak yok, silinmiş ya da yanlış ortamKimliği ve ortamı kontrol edin
400 “Idempotency-Key zorunlu” benzeri mesajAnahtar zorunlu uçta başlık yok ya da boş/200+ karakterBaşlığı ekleyin (idempotency)
400 “geçerli UUID olmalı”Klan, grup ya da turnuva ucunda anahtar UUID değilUUID v4 üretin
400 If-Match hatası*, W/"3", negatif ya da sayısal olmayan değerTam sürüm numarası gönderin (3 ya da "3")
409 version conflictKaynak siz okuduktan sonra değiştiYeniden okuyun, yeni sürümle tekrar edin
409 idempotent request in progressAynı anahtarlı istek hâlâ işleniyorKısa süre sonra aynı anahtarla tekrar edin
422 idempotency mismatchAynı anahtarı farklı gövdeyle kullandınızHer farklı işlem için yeni anahtar; aynı işlemin tekrarında gövdeyi değiştirmeyin (anahtarın ortam genelinde benzersiz olduğunu unutmayın)
422 unprocessable: …İş kuralı reddi (bakiye, stok, limit)Durumu okuyup kullanıcıya gösterin; aynı isteği tekrarlamayın
410Anahtarın 24 saatlik kayıt süresi dolduDurumu okuyup işlem uygulanmadıysa yeni anahtarla yeniden başlatın
413Gövde 2 MiB üstüGövdeyi küçültün; görsel için görsel yükleme ucunu kullanın
429Oran sınırı ya da kural bütçesiBekleyip aynı anahtarla tekrar edin; kural bütçesi için kuralı sadeleştirin
503 ve Retry-After: 30Ortamın yerleşimi geçici kullanılamıyorRetry-After kadar bekleyip tekrar edin
Büyük sayı bozuk (9007199254740993 yerine 9007199254740992)JSON’u JSON.parse ile okudunuz; int64 JS number’ına sığmadıSDK’nın bigint okumasını ya da lossless-json kullanın (sayılar)
Contract türünde SDK hatasıYanıt, SDK’nın bildiği şemaya uymuyor (sunucu daha yeni) ya da yanıt 2 MiB üstüSDK’yı güncelleyin; sunucu ve SDK sürümlerini eşleştirin

Eklentiler

BelirtiOlası nedenÇözüm
403 ve forbiddenEklenti ortamda etkin değil (salt okunur, boşaltılıyor, kapalı) ya da ortamın eklenti sürümü sunucudakiyle uyuşmuyor; CSRF/Origin reddiPanelde eklentinin durumuna ve sürümüne bakın (Eklentiler)
400 ve invalid_argument (“x-crx-schema-version is required”)x-crx-schema-version başlığı yok ya da şema sürümü yanlış; yük şemaya uymuyor; sorguda aynı parametre iki kezİşlemin schemaVersion değerini ve girdi şemasını şema belgesinden doğrulayın
404 ve not_foundEklenti kimliği ya da mesaj türü yanlış; mesaj türü ile işlem adını karıştırdınızŞema belgesindeki requestMessageType değerini yola yazın
409 idempotency_in_flightAynı anahtarlı komut işleniyorretryable: true; bekleyip aynı anahtarla tekrar edin
422 unprocessable + reasonEklenti iş kuralı reddireason eklentiye özgü koddur; eklentinin belgesine bakın; tekrar etmek işe yaramaz
Komut sonucu belirsizAğ koptu…/result?idempotency_key=… ile sorgulayın ya da aynı anahtarla tekrar edin (eklentileri çağırma)
SDK upgrade_required (plugin_binding_mismatch)Bağlamanın sabitlemesi (pin) elinizdeki beklentiyle ya da sunucudaki belgeyle uyuşmuyorGüncel belgeyi alıp bağlamayı yeniden üretin (bağlamalar)
Üretilmiş bağlamada Cannot find module '@open-play/sdk/player'Üretici içe aktarımı @open-play/sdk adıyla yazar; yayımlanan paketin adı @ferdiunal/open-play-ts’tirpackage.json’da takma ad tanımlayın: "@open-play/sdk": "npm:@ferdiunal/open-play-ts@<sürüm>"
crx plugins generate hatası: CRX TYPES FAILÖzet uyuşmuyor, çıktı dosyası zaten var, çıktı klasörü yok ya da yolda sembolik bağ varÖzeti belgedeki schemaSha256 ile eşleyin; mevcut çıktıyı silin; klasörü oluşturun

Realtime

BelirtiOlası nedenÇözüm
Bilet isteği 403Kimlik, CSRF, Origin ya da x-crx-env hatası (hepsi aynı yanıtı alır)CSRF ve Origin kontrol edin; oyunun alan adı ortam alan adı listesinde olmalıdır
Yükseltme 401Sorgu dizesi var, openplay.realtime.v1 alt protokolü istenmedi ya da Origin izinli değilProtokol: soketi aç
Hemen AuthenticationFailed ve kapanmaBilet kullanılmış (tek kullanım), 30 sn dolmuş ya da farklı Origin’den alınmış; 5 sn içinde Authenticate gelmediHer bağlantıda yeni bilet; soket açılınca hemen Authenticate gönderin
UnexpectedFrameYanlış fazda çerçeve (kimlik doğrulamadan önce Join, ikinci Authenticate)Sırayı protokole göre kurun
InvalidPayloadBozuk JSON, bilinmeyen alan, şemaya uymayan yük, int64 sayı olarak gönderildiSayaçları ondalık metin gönderin; yükü şemaya göre doğrulayın
CapacityExceeded ve kapanmaÇerçeve hızı 40/sn üstü; oyuncu başına 4 ya da adres başına 8 soketGönderimi azaltın; eski sekmeleri kapatın
Soket 5 dakikada kapanıyor (4100)Boşta kapanma“Devam et” akışı; canlı tutma girdisi (limitler)
Girdiler Ack: StaleAynı oyuncunun daha yeni bir soketi açıldıEski soketi kapatın; tek soketle çalışın
ResyncRequired döngüsüSüresi geçmiş ya da boşluklu imleçle ısrarİmleci atın, imleçsiz Join edin
/v1/ws ya da /v1/chat/ws tarayıcıda 401Bu iki soket yükseltmede x-crx-env başlığı ister; tarayıcı başlık ekleyemezTarayıcıda REST kullanın (realtime genel bakış)

Webhook’lar

BelirtiOlası nedenÇözüm
Uç noktama hiç istek gelmiyorUç nokta kayıtlı değil, silinmiş ya da devre dışı; olay türü abone listesinde yok; webhooks modülü kapalı (teslimatlar yerinde bekler, veri silinmez); uç nokta herkese açık değilPanelde uç noktayı ve modülü kontrol edin (Webhook ve olay kuyruğu)
Kayıt 400Adres https değil; özel/loopback IP ya da kullanıcı bilgisi içeriyorGenel erişilebilir https adres verin (güvenlik)
İmza uyuşmuyorGövdeyi ayrıştırıp yeniden serileştirdiniz (ham bayt gerekir); gizli anahtar yanlış (whsec_… metnin kendisi, base64 değil); t. önekini unuttunuzHam gövdeyi doğrulayın; örnek: güvenlik
Zaman damgası reddiSunucunuzun saati sapmışSaati NTP ile eşitleyin; pencereyi 5 dakikada tutun
Aynı olay iki kez geldiTeslimat en az bir kez (at-least-once)X-Crx-Event-Id ile tekilleştirin
Teslimatlar failed10 denemede 2xx alınamadıAlıcı hatasını giderin. Panel teslimatları yalnız izlemek içindir (yeniden gönderme/replay eylemi yoktur); kaçırılan olayların etkisini ilgili kaynağı REST ile okuyarak telafi edin
Büyük olay gelmediGövde 64 KiB üstü gönderilmezOlay boyutunu küçültün

Unity

BelirtiOlası nedenÇözüm
Paket kurulmuyor~/.upmconfig.toml ya da kapsamlı kayıt defteri yapılandırması eksik; belirteçte read:packages yokUnity SDK kurulum adımları
WebGL’de istekler engelleniyorOyun ve API aynı kökende değil; Unity WebGL SDK’sı bugün aynı kökeni şart koşarAynı köken ters vekil kurun (Unity SDK)
UpgradeRequired (plugin_binding_mismatch)Pakete gömülü package-ride bağlaması sunucudakiyle uyuşmuyorSDK’yı güncelleyin
Çalışma zamanında oturum geri yüklenmiyorGizli saklama varsayılanı güvenli değil (PlayerPrefs)Unity SDK: kendi güvenli saklamanızı bağlayın

Hâlâ çözülmediyse

  1. Aynı isteği curl ile (gizli değerleri koruyarak) tekrarlayın; sorun SDK’da mı, ağda mı, sunucuda mı ayırın.
  2. Durum kodunu hata referansında bulun.
  3. Üretim kontrol listesindeki ağ maddelerini (başlık iletimi, istemci IP’si, HTTPS) gözden geçirin.
  4. Destek talebine yukarıdaki bilgileri ekleyin.

Sık sorulan sorular

Bu sayfa, entegrasyonda en sık gelen soruları kısa yanıtlarla toplar. Ayrıntı için bağlı sayfalara geçin.

Başlangıç ve topoloji

Sunucu anahtarını oyunun içine koyabilir miyim? Hayır. Sunucu anahtarı yalnız sizin sunucunuzdadır. Oyun istemcisi oyuncu oturumuyla çalışır; istemciye gömülen anahtar gerekiyorsa istemci anahtarı vardır, ama onun da çağırabildiği uçlar sınırlıdır ve bazı uçlarda anahtar türü kapısı yoktur (güvenlik, uç noktalar).

Neden CORS hatası alıyorum? Oyunun alan adı bir ortamın alan adı listesinde (ya da operatörün genel izinli listesinde) değilse sunucu o origin’e CORS başlığı vermez. Alan adını panelden ekleyin; origin tam https://<alan adı> olmalıdır. Oyun, Open Play’den farklı bir kayıtlı alan adındaysa oturum çerezi (SameSite=Lax) gitmez; o durumda aynı köken ters vekil kullanın. Kendi vekilinize ek CORS başlığı eklemeyin (mimari ve barındırma).

Oyunum mobil uygulama ya da Unity masaüstü; tarayıcı kısıtları geçerli mi? CORS ve aynı site kuralı yalnız tarayıcıyı ilgilendirir. Tarayıcı dışı istemciler çerez yerine Bearer oturum jetonu kullanabilir; ama eklenti komutlarında CSRF çifti ve realtime bilet isteğinde Origin gibi kurallar yine geçerlidir (eklentileri çağırma, protokol). Unity için Unity SDK.

Open Play’in gRPC arayüzünü kullanabilir miyim? Dışa açık bir yüzey olarak desteklenmiyor. Oyun entegrasyonları REST, WebSocket ve SDK’lar üzerindendir.

Hangi SDK’lar var? TypeScript (@ferdiunal/open-play-ts) ve Unity (com.openplay.sdk). Tipli CrxClient’ı olmayan modüller için ham REST kullanırsınız (uç noktalar). Ayrıntı TypeScript SDK ve Unity SDK sayfalarında.

Kimlik

Sunucu bir oyuncunun kim olduğunu nasıl doğrular? Oyuncu girişinde dönen erişim JWT’sini oyun sunucunuz JWKS ile çevrimdışı doğrular (sunucu doğrulaması). Oturum jetonu ya da çerez değildir.

Oturum ne kadar yaşar? 30 gün kayar (günde bir istek yeterlidir), mutlak tavan 180 gündür. Açık bir realtime soketi oturumu yaşatmaz (güvenlik).

Yasaklı oyuncuya ne olur? Giriş uçlarında, sahiplik kanıtlandıktan sonra 403 ve account_banned alır; diğer uçlarda 401 görünür. Eklenti uçlarında 403 forbidden (message: account banned) (hatalar).

Tekrar güvenliği, sürüm, sayılar

Idempotency-Key ne zaman yeni üretilir? Her yeni işlem için. Aynı işlemin yeniden denemelerinde aynı anahtar kullanılır; belirsiz sonuçlu komutu yeni anahtarla tekrarlamak çift uygulamaya yol açar (idempotency).

409 aldım. Aynı isteği tekrarlasam olur mu? İki farklı 409 vardır: sürüm çakışmasında (version conflict) önce durumu yeniden okuyun; “işleniyor” (idempotent request in progress) yanıtında aynı anahtarla kısa süre sonra tekrar edin.

Bakiye neden büyük bir tam sayı? Para küçük birimli tam sayıdır; kayan nokta hiçbir yerde kullanılmaz. Gösterim ondalığını kendiniz hesaplarsınız (sayılar ve kodlama).

Neden bazı sayılar metin olarak geliyor? Eklenti ve realtime int64 alanları JavaScript number’ının güvenli aralığını aşabilir; bu yüzden ondalık metin taşınır. TypeScript SDK’sı bunları bigint yapar.

Yüzde alanları neden 10000 üzerinden? Baz puan: 10000 = %100 (sayılar ve kodlama).

Hata gövdesi neden bazen düz metin, bazen JSON? Çekirdek REST düz metin döner (birkaç JSON istisna), eklenti uçları her zaman JSON döner. Karar için durum koduna ve varsa code alanına bakın (hatalar).

Hız sınırları

Anahtar başına istek sınırı var mı? Hayır; genel bir kota yok. Belirli işlemlerin (giriş, kayıt, sohbet, eklenti komutları, soket bağlantısı) sınırları vardır. Büyük toplu işlerde kendi eşzamanlılık sınırınızı uygulayın (hız sınırları).

Lansmanda yüzlerce misafir aynı anda açılacak. Sorun olur mu? Misafir açma IP başına dakikada 30’dur. Oyuncular ayrı IP’lerdense sorun olmaz; ama ters vekiliniz istemci IP’sini iletmiyorsa hepsi tek kovada toplanır. IP iletimini doğrulayın ve yük testi yapın (üretim kontrol listesi).

Eklentiler

Kendi eklentimi yükleyebilir miyim? Hayır, henüz yok. Eklentiler sunucuya derlenmiş güvenilir koddur; üçüncü taraf yükleme ve yalıtım yoktur. Bugünkü yollar: kurallar, webhook’lar ve kendi sunucunuz (eklentilere genel bakış).

Eklenti şeması değişti, istemcim bozulur mu? Eklenti şemaları özetle (schemaSha256) sabitlenir. Sunucu şemayı değiştirirse eski bağlama uyuşmazlığı görür ve güncel belgeden yeniden üretmeniz gerekir (bağlamalar).

Eklenti çağrısı 403 forbidden döndü. Neden? Çoğunlukla eklenti ortamınızda etkin değildir ya da yetki sürümü sunucudakiyle uyuşmuyordur; ya da CSRF/Origin reddi. Panelde eklentinin durumuna bakın (Eklentiler).

Realtime

Soket kopunca ne yapmalıyım? Yeni bilet alın, yeniden bağlanın ve kalıcı akışlarda son imleçle yeniden katılın; ResyncRequired gelirse imleçsiz katılın. TypeScript SDK’sı bunu kendiliğinden yapar (yeniden bağlanma).

Soket neden 5 dakikada bir kapanıyor? Girdi kabul eden bir abonelikte oyuncu etkinliği gelmediği için sunucu boşta kapatır (4100). “Devam et” ekranı gösterin ya da canlı tutma girdisi gönderin.

Bildirim ve sohbet için soket kullanabilir miyim? Tarayıcıdan doğrudan hayır: /v1/ws ve /v1/chat/ws yükseltmede x-crx-env başlığı ister ve tarayıcı bunu ekleyemez. Tarayıcıda gelen kutusunu REST ile okuyun, sohbet için REST uçlarını kullanın (realtime genel bakış).

Webhook’lar

Webhook’ları imzasız kabul edebilir miyim? Hayır. Her isteğin imzasını ham gövde üzerinde doğrulayın ve X-Crx-Event-Id ile tekilleştirin (güvenlik).

Kaçırdığım olayı yeniden gönderebilir miyim? Panelde yeniden gönderme eylemi yoktur; teslimatlar yalnız izlenir. İlgili kaynağın durumunu REST ile okuyarak telafi edin.

Yönetim

Anahtarı kaybettim, geri alabilir miyim? Hayır; gizli değer yalnız oluşturma anında gösterilir. Yeni anahtar üretin ve eskisini iptal edin (güvenlik).

Panelde ne yapabilirim, API ile ne yapmalıyım? Panel, oyunun yönetimi içindir (içerik, mağaza, ekonomi raporları, kurallar, ayarlar). Oyun içi akışlar API ve SDK ile yapılır. Panel ekranları için ilgili panel yardım sayfalarına bakın.

Dokümanda bulamadığım bir şey var. Önce sorun giderme ve sözlüğe bakın; bulamazsanız sorunuzu durum kodu, yol ve zaman bilgisiyle iletin.

Oyun özeti

Amaç

Bu sayfa, paneli açtığınızda ilk gördüğünüz ekrandır. Seçili oyunun ve ortamın durumunu özetler; her kartın “Aç” düğmesi ilgili ayrıntı sayfasına götürür. Sayfanın başlığı “oyun adı · ortam adı” biçimindedir (örneğin “Benim Oyunum · Production”), böylece hangi ortamda çalıştığınızı her zaman görürsünüz.

Ne zaman kullanılır

  • Güne başlarken oyunun genel durumuna bakmak için.
  • Bir değişiklikten sonra beklediğiniz sayıların (oyuncu, karakter, kural) yerinde olduğunu doğrulamak için.
  • Son işlemleri görüp kimin neyi değiştirdiğini hızlıca anlamak için.
  • Güvenlik korumalarından birinin gevşek bırakılıp bırakılmadığını fark etmek için.

Adım adım

  1. Sol üstteki seçiciden doğru oyunu ve ortamı seçtiğinizden emin olun. Bu sayfadaki her sayı yalnızca seçili ortamı kapsar.
  2. Kartlara bakın. Kartın altında bir not görüyorsanız sayı alınamamıştır (aşağıdaki tabloya bakın).
  3. Ayrıntı için kartın Aç düğmesine basın.
  4. “Son işlemler” kartında en yeni altı yönetim işlemini görürsünüz. Tümü için Tüm kayıtları gör düğmesini kullanın.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form yoktur. Kartlar şunları gösterir:

KartGösterdiğiAç düğmesi
OyuncularSeçili ortamdaki toplam oyuncu hesabı sayısıOyuncular
Oyuncu karakterleriKarakter sayısı; liste uzunsa sayının sonunda “+” görünürKarakterler
İçerikKarakter şablonu ve eşya tanımı sayısıİçerik
Kurallar“açık kural / toplam kural” (örneğin 3/5)Kurallar
EklentiOyuna bağlı eklentinin durumu: Açık, Salt okunur, Drain (kapanıyor, kalan saniye ile), Yetki yok ya da Eklenti yokEklentiler

“Son işlemler” kartı yalnızca denetim kaydını okuma yetkiniz varsa görünür. Her satırda işlem adı, zaman, işlemi yapan, hedef ve işlem nedeni bulunur.

Bir güvenlik koruması gevşekse sayfanın üstünde sarı bir bant çıkar (“N koruma gevşek”); durumu okunamadıysa “bilinmiyor” bandı çıkar. Şalterleri aç düğmesi güvenlik şalterleri sayfasına götürür.

Sık karşılaşılan durumlar

DurumNe yapmalı
Kartta “Bu sayıyı görme yetkiniz yok.” yazıyorHesabınızın rolü o alanı okuyamıyor; bir yöneticiden rolünüzü genişletmesini isteyin.
Kartta “Sayı alınamadı.” yazıyorGeçici bir okuma hatasıdır; sayfayı yenileyin. Sürerse sunucu durumunu kontrol edin.
“Bu oyunda henüz oyuncu yok” yazısı görünüyorOyun istemciniz henüz giriş yapmamıştır; ilk oyuncu girişinden sonra sayı güncellenir.
Eklenti kartında “Yetki yok” yazıyorEklentinin bu ortam için etkin bir izni yok; Eklentiler sayfasından durumu inceleyin.
Sayılar beklediğimden farklıBaşka bir ortamı seçmiş olabilirsiniz (örneğin canlı yerine geliştirme). Başlıktaki ortam adını kontrol edin.

İlgili dokümanlar

Kullanıcılar

Amaç

Bu sayfa, seçili oyunun ve ortamın oyuncu hesaplarını listeler. Bir oyuncuyu bulmak, hesabının durumunu görmek, profiline geçmek ve gerekirse elle yeni hesap oluşturmak için kullanın. Soldaki menüde bu sayfa “Oyuncular” adıyla görünür.

Ne zaman kullanılır

  • Bir oyuncunun yardım isteği geldiğinde hesabını bulmak için.
  • Yasaklı ya da şüpheli hesapları süzmek için.
  • Test için elle oyuncu hesabı oluşturmak için.

Adım adım

Oyuncu bulma

  1. Sayfanın üstündeki ortam seçiciden doğru oyunu ve ortamı seçtiğinizden emin olun. Liste yalnızca seçili ortamı gösterir.
  2. Tablonun arama kutusuna oyuncunun adını ya da e-postasını yazın.
  3. Durum, giriş yöntemi ya da tarih gibi koşullarla daraltmak için tablonun filtre menüsünü kullanın.
  4. Profili açmak için satıra tıklayın ya da Detay düğmesine basın.

Elle hesap oluşturma

  1. Kullanıcı oluştur düğmesine basın. Düğmeyi yalnızca hesap oluşturma yetkisi olan yöneticiler görür.
  2. Görünen adı yazın. E-posta, parola ve ek bilgiler isteğe bağlıdır.
  3. İşlem nedeni alanına kısa bir gerekçe yazın.
  4. Kullanıcı oluştur düğmesiyle kaydedin. Hesap listede görünür.

Not

Oyuncular çoğunlukla oyundan kendileri giriş yapar; bu formu yalnızca test ve destek durumlarında kullanın.

Alanlar ve seçenekler

Hesap oluşturma formu

AlanZorunluAçıklama
Görünen adEvetOyuncunun görünen adı; en az 2 karakter
E-postaHayırOyuncunun e-posta adresi
E-posta doğrulandıHayırAdresin doğrulanmış sayılmasını sağlar
Geçici parolaHayırEn az 12 karakter; boşsa parolalı hesap oluşmaz
Metadata JSONHayırEk bilgiler, JSON nesnesi
İşlem nedeniEvetİşlem geçmişine yazılan gerekçe, 8-500 karakter

Listedeki sütunlar

Kullanıcı, Durum, Giriş yöntemi, Aktif oturum, Son giriş ve Oluşturulma sütunları varsayılan olarak görünür. E-posta, Oyuncu kimliği, Hesap türü, Güncellenme ve E-posta doğrulandı sütunlarını tablonun sütun menüsünden açabilirsiniz.

Sık karşılaşılan durumlar

DurumNe yapmalı
Liste boş görünüyorDoğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin.
Oyuncuyu bulamıyorumMisafir hesapların e-postası olmaz; adı, kimliğini ya da giriş yöntemini kullanarak arayın.
“Kullanıcı oluştur” düğmesi yokHesabınızın rolünde hesap oluşturma yetkisi yoktur; bir yöneticiden isteyin.
Form “metadata” hatası veriyorAlana geçerli bir JSON nesnesi yazın; dizi ya da düz metin kabul edilmez.

İlgili dokümanlar

Oyuncu karakterleri

Amaç

Bu sayfa seçili ortamdaki karakterleri listeler. Karakter, oyuncunun oyunda oynadığı varlıktır; durumu (seviye, özellik, kaynak) komutlarla değişir ve her değişiklik kayıt altına alınır. Bir karakteri bulmak, profiline geçmek ve gerekirse elle yeni karakter oluşturmak için kullanın. Karakter şablonları (tanımlar) bu sayfada değil İçerik sayfasındadır.

Ne zaman kullanılır

  • Bir oyuncunun karakterinin durumuna bakmak için.
  • Sürücü gibi belirli bir türdeki karakterleri süzmek için.
  • Test ya da destek amacıyla elle karakter oluşturmak için.

Not

Karakter modülü kapalıysa bu sayfa menüde görünmez. Modülü Oyun ayarları › Modüller sayfasından açın.

Adım adım

Karakter bulma

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun; liste yalnızca seçili ortamı gösterir.
  2. Üstteki Tümü, Sürücüler ve Diğer düğmeleriyle türe göre daraltın.
  3. Tablonun arama kutusunu ve filtre menüsünü kullanarak ad, tür, seviye ya da tarihe göre daraltın.
  4. Profili açmak için satıra tıklayın. Profil sayfası için bkz. Karakter profili.

Yeni karakter oluşturma

  1. Yeni karakter düğmesine basın. Düğmeyi yalnızca karakter oluşturma yetkisi olan yöneticiler görür.
  2. Tanım listesinden karakterin türünü seçin. Tanım yoksa önce İçerik sayfasından bir tanım yayınlayın.
  3. İsterseniz Dış kimlik alanına oyun sunucunuzdaki kimliği yazın.
  4. İşlem nedeni yazıp Oluştur düğmesine basın. Karakter hemen oluşturulur ve profiline geçilir.

Alanlar ve seçenekler

Yeni karakter formu

AlanZorunluAçıklama
Tanım (ya da Tanım anahtarı)EvetKarakterin üretileceği tanım; güncel sürümü kullanılır
Dış kimlikHayırOyun sunucunuzla eşleştirme kimliği; aynı kimlikle ikinci karakter oluşturulamaz
İşlem nedeniEvetİşlem geçmişine yazılan gerekçe, 8-500 karakter

Listedeki sütunlar

Oyuncu, Tür, Seviye, Puan, Teslimat, Son değişiklik ve Oluşturulma sütunları varsayılan olarak görünür. Dış kimlik ve Oyuncu kimliği sütunlarını sütun menüsünden açabilirsiniz. Oyuncusu olmayan eski yer tutucu sürücü kayıtları “Taşınmamış” rozetini taşır.

Sık karşılaşılan durumlar

DurumNe yapmalı
Liste boş görünüyorDoğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin.
“Bu ortamda henüz tanım yok” yazıyorKarakter bir tanımdan üretilir. İçerik sayfasından bir tanım yayınlayıp geri dönün.
“Bu anahtarla yayınlanmış bir tanım yok” hatasıTanım anahtarı yanlış ya da yayınlanmamış. Listeden seçin ya da tanımı yayınlayın.
“Bu dış kimlikle zaten bir karakter var” hatasıBaşka bir dış kimlik girin ya da alanı boş bırakın.
“Yeni karakter” düğmesi yokHesabınızın rolünde karakter oluşturma yetkisi yoktur; bir yöneticiden isteyin.

İlgili dokümanlar

Klanlar

Amaç

Bu sayfa seçili ortamdaki klanları listeler ve seçtiğiniz klanın ayrıntısını gösterir: klan ayarları, üyeler, davetler, başvurular, klan kasası (hazine) transferleri ve tamamlanmış komut kayıtları. Sayfa salt okunurdur; panelden klan oluşturamaz, üye ekleyemez ya da kasadan para çekemezsiniz. Klan işlemleri oyundan, sunucu çağrılarıyla yapılır.

Ne zaman kullanılır

  • Bir klanın üyelerini, liderini ve seviyesini görmek için.
  • Bir oyuncunun klan kasasına yatırdığı ya da çektiği tutarı doğrulamak için.
  • Bir başvuru ya da davetin durumunu incelemek için.

Adım adım

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun. Liste yalnızca seçili ortamı gösterir.
  2. “Klan atlası” tablosunda ada, etikete, duruma, seviyeye ya da tarihe göre arayın ve süzün.
  3. Ayrıntıyı açmak için klanın adına tıklayın ya da satırı açın. Sayfa ilk açıldığında en yeni klan otomatik seçilir.
  4. “Klan detay ve ilişkiler” kartında üyeleri, davetleri, başvuruları, kasa transferlerini ve komut kayıtlarını görürsünüz. Uzun tablolar sayfalanır.
  5. Bir oyuncunun profiline geçmek için tablodaki oyuncu bağlantısını kullanın.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form yoktur.

Listedeki sütunlar

Klan (ad ve köşeli parantez içinde etiket), Durum, Seviye, XP, Üye, Oluşturulma varsayılan olarak görünür. Etiket, Lider ve Güncellenme sütunlarını sütun menüsünden açabilirsiniz. Durum “active” (etkin) ya da “disbanded” (dağıtılmış) olur.

Ayrıntı kartları

Kartİçerik
Klan ayarlarıOrtamın klan kuralları; yalnızca okunur
ÜyelerOyuncu, rol, katılım ve ayrılış zamanı. Ayrılmış üyeler de listelenir ve “Ayrıldı” rozetiyle işaretlenir
DavetlerDavet edilen oyuncu, durum ve son geçerlilik
BaşvurularBaşvuran oyuncu, durum ve oluşturulma zamanı
Kasa transferleriYatırma ya da çekme, miktar, işlemi yapan oyuncu, bakiyeler ve para birimi. Tutarlar metin olarak gösterilir
Komut geçmişiTamamlanmış komutun türü, istek özeti (hash) ve zamanı; yanıt gövdesi gösterilmez

Sık karşılaşılan durumlar

DurumNe yapmalı
Liste boş görünüyorDoğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin.
Üye sayısı ile satır sayısı uyuşmuyorAyrılmış üyeler de listelenir; “Ayrıldı” rozetli satırlar aktif üye sayısına girmez.
Panelden klanı düzenleyemiyorumSayfa salt okunurdur; klan işlemleri oyundan ya da sunucu çağrılarıyla yapılır.
“Bu sayıyı görme yetkiniz yok” benzeri bir uyarı çıkıyorRolünüzün klan okuma yetkisi yoktur; bir yöneticiden isteyin.

İlgili dokümanlar

Partiler

Amaç

Bu sayfa seçili ortamdaki partileri listeler; seçtiğiniz partinin üyelerini, davetlerini ve bir ödül dağıtımının sonucunu gösterir. Sayfa salt okunurdur: panelden parti kuramaz, üye çıkaramaz ya da ödül dağıtamazsınız. Parti işlemleri oyundan, sunucu çağrılarıyla yapılır.

Ne zaman kullanılır

  • Bir partinin üyelerini, liderini ve ganimet kuralını görmek için.
  • Bir oyuncunun neden partiye giremediğini, davet durumuna bakarak anlamak için.
  • Bir ödül dağıtımının tutarını, durumunu ve teslim biçimini doğrulamak için.

Adım adım

Parti inceleme

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun.
  2. “Party roster” tablosunda durum, kapasite, ganimet kuralı ya da tarihe göre süzün. Sayfa açıldığında en yeni parti otomatik seçilir.
  3. Başka bir partiyi seçmek için adına tıklayın ya da satırı açın.
  4. “Party detay ve ilişkiler” kartında lideri, ganimet kuralını, kapasiteyi, üyeleri ve davetleri görürsünüz.

Ödül dağıtımı sorgulama

  1. Bir parti seçili olsun.
  2. “Reward distribution lookup” kartındaki Dağıtım kimliği alanına dağıtımın UUID’sini yapıştırın.
  3. Yükle düğmesine basın. Kimlik geçerli bir UUID değilse istek gönderilmez ve alanın yanında hata görünür.
  4. Sonuç olarak miktar, durum ve teslim biçimi görünür. Ham ödül içeriği gösterilmez.

Alanlar ve seçenekler

AlanZorunluAçıklama
Dağıtım kimliğiEvet (Yükle için)Ödül dağıtımının UUID kimliği; seçili partiye aittir, parti değişince alan sıfırlanır

Listedeki sütunlar

Party (kısaltılmış kimlik), Durum, Üye (“üye sayısı/kapasite”), Ganimet kuralı, Lider ve Oluşturulma varsayılan olarak görünür. Kapasite ve Güncellenme sütunlarını sütun menüsünden açabilirsiniz. Durum “active” ya da “disbanded”, ganimet kuralı “equal” (eşit) ya da “weighted” (ağırlıklı) olur. Üye ve davet tablolarında “Pay” sütunu her kaydın ödül payını yüzde olarak gösterir.

Sık karşılaşılan durumlar

DurumNe yapmalı
Liste boş görünüyorDoğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin.
“Dağıtım kimliği 8-4-4-4-12 biçiminde olmalı” hatasıKimliği eksiksiz ve tireli kopyalayın; başında ya da sonunda fazladan karakter olmasın.
Dağıtım bulunamıyorKimlik başka bir partiye ya da ortama ait olabilir; doğru partiyi seçtiğinizden emin olun.
Panelden partiyi değiştiremiyorumSayfa salt okunurdur; parti işlemleri oyundan ya da sunucu çağrılarıyla yapılır.

İlgili dokümanlar

Şehir sohbeti

Amaç

Bu sayfa, oyunun şehir sohbetine yazılan mesajları gösterir ve moderasyon yapmanızı sağlar: zararlı bir mesajın metnini silebilir, mesajı yazan oyuncuyu süreli susturabilir ve aktif susturmaları kaldırabilirsiniz. Mesaj metni düz metin olarak gösterilir. Mesajlar 30 gün saklanır, sonra otomatik silinir.

Ne zaman kullanılır

  • Bir oyuncu hakkında sohbetle ilgili şikâyet geldiğinde mesajı bulup incelemek için.
  • Küfür, spam ya da zararlı bir mesajı silmek için.
  • Kural ihlali yapan bir oyuncuyu bir süre susturmak için.

Not

Bu sayfa yalnızca seçili oyunun bağlı eklentisi sahne sohbeti sunuyorsa çalışır. Sunmuyorsa “Bu oyunda sahne sohbeti yok” yazısı görünür. Menüde de yalnızca böyle oyunlarda görünür.

Adım adım

Mesajı silme

  1. Aktarım sekmesinde mesajı bulun. Tabloyu oda, yazar, hesap türü, silinmiş mi ve zamana göre süzebilirsiniz; en yeni mesaj üstte durur.
  2. Satırdaki Sil düğmesine basın.
  3. İşlem nedeni yazıp Mesajı sil düğmesiyle onaylayın. Mesajın metni silinir; uzunluğu kayıtta kalır ve satırda “Silindi · N karakter” görünür. Bu işlem geri alınamaz.

Oyuncuyu susturma

  1. Mesajın satırında Yazarı sustur düğmesine basın.
  2. Süre listesinden süreyi seçin. “Özel bitiş…” seçerseniz bitiş anını girin.
  3. İşlem nedeni yazın ve Sustur düğmesine basın. Oyuncunun mesajları reddedilir; oyuncuya neden iletilmez ve oturumu düşmez.

Susturmayı kaldırma

  1. Aktif susturmalar sekmesini açın. Süresi dolan susturmalar kendiliğinden listeden çıkar.
  2. Satırdaki Susturmayı kaldır düğmesine basın, nedeni yazıp onaylayın. Oyuncu hemen yeniden yazabilir.

Uyarı

Misafir hesap yeni bir misafir oluşturularak susturmayı aşabilir. Kalıcı çözüm hesabı yasaklamaktır; bunun için Oyuncu profili sayfasındaki “Banla” işlemini kullanın.

Alanlar ve seçenekler

Susturma penceresi

AlanZorunluAçıklama
SüreEvet5 dakika, 1 saat, 24 saat, 7 gün ya da Özel bitiş
Bitiş anıYalnız özel süredeGelecekte olmalı, en çok 30 gün sonra
İşlem nedeniEvet8-500 karakter

Mesaj silme ve susturmayı kaldırma pencerelerinde yalnızca İşlem nedeni alanı vardır.

Tablo sütunları

Aktarım: Zaman, Yazar (hesap türü rozetiyle: Misafir ya da Kayıtlı), Oda, Mesaj ve Eylemler. Hesap türü, Silindi, Uzunluk ve Oyuncu kimliği sütunları sütun menüsünden açılır. Aktif susturmalar: Oyuncu, Bitiş (kalan süreyle), Gerekçe, Susturan ve Eylem.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu oyunda sahne sohbeti yok” yazıyorSeçili oyunun eklentisi sahne sohbeti sunmuyor; sahne bildiren bir eklenti bağlanınca sayfa çalışır.
Sil ve Sustur düğmeleri görünmüyorRolünüzün sohbet yazma yetkisi yoktur; yalnızca okuyabilirsiniz.
Özel bitiş hata veriyorBitiş gelecekte olmalı ve 30 günden uzak olmamalı.
Eski mesajı bulamıyorumMesajlar 30 gün sonra otomatik silinir.
Susturulan oyuncu yeniden yazıyorMisafir hesap yeni bir misafir açmış olabilir; hesabı yasaklamayı düşünün.

İlgili dokümanlar

Oyuncu organizasyonları

Amaç

Bu sayfa, oyuncuların kurduğu organizasyonları listeler ve seçtiğiniz organizasyonun üyelerini, davetlerini, takımlarını ve rollerini yönetmenizi sağlar. Organizasyon bir stüdyo değildir; stüdyolar için Stüdyolar ve oyunlar sayfasını kullanın.

Uyarı

Bu ekran kullanımdan kalkıyor. Çalışmaya devam eder ve menüde varsayılan olarak gizlidir; yeni işlerde oyuncu grupları için Klanlar kullanılmalıdır.

Ne zaman kullanılır

  • Mevcut organizasyonlara üye, davet, takım ya da rol eklemek için.
  • Bir organizasyonu düzeltmek ya da kaldırmak için.
  • Eski bir kurulumun organizasyon verisini incelemek için.

Adım adım

Organizasyon oluşturma

  1. Organizasyon oluştur düğmesine basın. Düğmeyi yalnızca organizasyon yazma yetkisi olan yöneticiler görür.
  2. Ad ve Plan alanlarını doldurun.
  3. İşlem nedeni yazıp kaydedin. Organizasyonun sayfası açılır.

Üye davet etme

  1. Organizasyon sayfasında Üyeler ya da Davetler sekmesinde Davetle üye ekle ya da Davet oluştur düğmesine basın.
  2. E-posta, rol ve son geçerlilik tarihini girin; nedeni yazıp kaydedin.
  3. Davet Davetler sekmesinde görünür. Süresi dolmuş ya da beklemede olan daveti Yeniden gönder ile yeni bir son tarihle yenileyebilir, bekleyen daveti İptal ile kullanılamaz yapabilirsiniz.

Not

Panel yalnızca davet kaydını oluşturur ve yönetir; davet e-postasını göndermez.

Üye rolünü değiştirme

  1. Üyeler sekmesinde üyenin satırındaki Düzenle düğmesine basın.
  2. Yerleşik rolü seçin, gerekirse dinamik rolleri ekleyin, nedeni yazıp kaydedin.
  3. Üyeyi çıkarmak için satırdaki Çıkar düğmesini kullanın; onay için üyenin adını (yoksa e-postasını) yazarsınız.

Takım ve rol yönetimi

  1. Takımlar sekmesinde Takım oluştur ile takım ekleyin; Üye düğmesiyle organizasyon üyelerinden birini takıma ekleyin.
  2. Roller sekmesinde Dinamik rol oluştur ile yeni rol ekleyin. Yerleşik roller (“Built-in”) silinemez ve adı değiştirilemez.

Alanlar ve seçenekler

AlanPencereAçıklama
Ad, PlanOrganizasyonAd ve plan metni (plan en çok 64 karakter)
Built-in rol, Dinamik rollerÜyeyi düzenleYerleşik rol ve ek dinamik roller
E-posta, Rol, Son geçerlilikDavetRol admin ya da member; geçerlilik en çok 30 gün
Yeni son geçerlilikDaveti yeniden gönderYeni son tarih
Takım adıTakımTakımın adı
OyuncuTakıma üye ekleOrganizasyon üyelerinden biri
Rol adı, Permissions JSONDinamik rolRol adı ve izin nesnesi
İşlem nedeniHepsi8-500 karakter
Onay metniSilme ve çıkarmaKaydın adını aynen yazın; ad yoksa “SİL” yazılır

Sık karşılaşılan durumlar

DurumNe yapmalı
Sayfayı menüde göremiyorumKullanımdan kalktığı için varsayılan olarak gizlidir; hesap menünüzdeki “Gizlenen eski ekranları göster” anahtarını açın ya da adresi doğrudan kullanın.
Davet “geçerlilik” hatası veriyorSon tarih gelecekte olmalı ve 30 günü aşmamalı.
“Permissions JSON” hatasıİzin nesnesi { "kaynak": ["eylem"] } biçiminde olmalı; eylemler boş ve yinelenen olamaz.
Oyuncuyu takıma ekleyemiyorumYalnızca organizasyonun üyeleri takıma eklenebilir; önce üye olarak ekleyin.
Yerleşik rolü silemiyorumYerleşik roller silinemez; yalnızca dinamik roller silinir.

İlgili dokümanlar

Oturum merkezi

Amaç

Bu sayfa, seçili ortamdaki oyuncuları açık oturum sayılarıyla birlikte listeler. Oturum, oyuncunun giriş yaptığı bir cihaz ya da tarayıcıdır. Burada yalnızca özet görürsünüz; cihaz bilgisini görmek ve oturumları kapatmak için oyuncunun profiline geçersiniz. Sayfa menüde yer almaz; Oyuncular sayfasındaki oturum merkezi bağlantısıyla açılır.

Ne zaman kullanılır

  • Hangi oyuncuların açık oturumu olduğunu görmek için.
  • Çok sayıda açık oturumu olan ya da uzun süredir girmemiş hesapları bulmak için.
  • Bir oyuncunun oturumlarını kapatmak üzere profiline gitmek için.

Adım adım

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun.
  2. Tabloyu durum ve açık oturum sayısına göre sıralayın ya da süzün.
  3. Oyuncunun oturumlarını görmek ve kapatmak için satırdaki Oturumları yönet düğmesine basın. Oyuncu profili açılır; Oturumlar sekmesinde tek tek oturumları, Diğer işlemler menüsünde “Tüm oturumları kapat” işlemini bulursunuz.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form yoktur. Sütunlar: Kullanıcı, Durum, Aktif oturum, Son giriş ve Güvenlik. Oluşturulma sütununu sütun menüsünden açabilirsiniz.

Sık karşılaşılan durumlar

DurumNe yapmalı
Oturumu bu sayfadan kapatamıyorumSayfa yalnızca özettir; kapatma işlemi oyuncu profilindedir.
Oturumları kapatma seçeneği görünmüyorRolünüzün oturum iptal yetkisi yoktur; bir yöneticiden isteyin.
Oyuncu yeniden giriş yapabildiOturumları kapatmak girişi engellemez; girişi engellemek için hesabı yasaklayın.

İlgili dokümanlar

İçerik

Amaç

Bu sayfa, oyununuzun içeriğini gruplar halinde toplar: araçlar, NPC’ler, silahlar gibi. Her grup, sizin tanımladığınız alanlardan (sayı, metin, renk, 3D model…) oluşan bir şemaya ve o şemayla doldurulan kayıtlara sahiptir. Oyun içeriği yalnızca yayınladığınız hâliyle görür; taslakta yaptığınız değişiklik, yayınlayana kadar oyuncuya yansımaz.

Ne zaman kullanılır

  • Oyununuza yeni bir tür içerik (örneğin araçlar) eklemek için ilk grubu kurarken.
  • Bir grubun kayıtlarını değiştirip oyuna göndermek (yayınlamak) için.
  • Daha önce yayınladığınız bir hâle geri dönmek için.

Adım adım

Yeni grup oluşturma

  1. Yeni grup düğmesine basın. Düğmeyi yalnızca içerik yazma yetkisi olan yöneticiler görür.
  2. Boş bir grupla başlayabilir ya da hazır şablonlardan birini seçebilirsiniz; şablon yalnızca alan önerisi doldurur, kayıt oluşturmaz.
  3. Grup adını, simgesini ve açıklamasını yazıp Devam: alanlar ile alanları kurun. Ayrıntılar için Grubu düzenle sayfasına bakın.

Kayıtları değiştirme ve yayınlama

  1. Grup kartına tıklayın; kayıtların listesi açılır. Kartlar ya da tablo görünümü arasında geçebilirsiniz.
  2. Bir kaydı açın, Düzenle ile değiştirin ve Taslağı kaydet deyin. Değişiklik yalnızca taslaktadır. Ayrıntılar için Kayıt sayfasına bakın.
  3. Listenin üstündeki Yayınla düğmesine basın. Açılan pencere neyin oyuna gideceğini kayıt kayıt gösterir; Engelleyici satırları varsa önce onları düzeltmeniz gerekir, Uyarı satırları yayını durdurmaz.
  4. İsterseniz “Ne değişti?” notunu yazın ve yayınlayın.

Eski bir yayına dönme

  1. Kayıt listesinde Geçmiş düğmesine basın.
  2. Dönmek istediğiniz yayının yanındaki Farkı gör ile neyin değişeceğine bakın.
  3. Bu hale geri dön deyin. Bu işlem yeni bir yayın oluşturur; taslağınıza dokunmaz.

Önerilen gruplar

Bir eklenti kendi içeriğini hazır sunuyorsa sayfanın üstünde Önerilen bölümü çıkar. Kur düğmesi o grubu bugünkü değerlerle oluşturur; sonra istediğiniz gibi düzenleyebilirsiniz.

Alanlar ve seçenekler

AlanNeredeAçıklama
AraKayıt listesi (kart görünümü)Kayıt adı ya da anahtarıyla arar
Taslak değişikliği olanlarKayıt listesi (kart görünümü)Yalnızca yayınlanmamış değişikliği olan kayıtları gösterir
Ne değişti?Yayınla penceresiİsteğe bağlı yayın notu, en çok 280 karakter

Sayfadaki bölümler

  • Önerilen: Eklentilerin sunduğu hazır gruplar.
  • Gruplarınız: Sizin oluşturduğunuz gruplar; kartta kayıt sayısı, son yayın zamanı ve “N yayınlanmamış değişiklik” rozeti görünür.
  • Yerleşik içerik: Karakter ve eşya tanımları gibi sistemin kendi grupları. Bunlarda yayın adımı yoktur: kaydedince hemen oyunda geçerli olur. Kartlar Karakter şablonları sayfasına gider.
  • 3D model kütüphanesi: 3D modeller sayfasına bağlantı. Sahneler modülü kapalıysa bağlantı yerine nedeni yazılır.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Yeni grup” düğmesi yokHesabınızın rolünde içerik yazma yetkisi yoktur; bir yöneticiden isteyin.
Yayınla düğmesi çalışmıyor, “Engelleyici” görüyorumEngelleyici satırındaki bağlantıyla ilgili kaydı açın ve eksik ya da hatalı değeri düzeltin.
Oyuncular yaptığım değişikliği görmüyorDeğişiklik taslakta kalmıştır; grubu Yayınla.
3D model kütüphanesi bağlantısı görünmüyorSahneler modülü bu ortamda kapalıdır; Oyun ayarları içindeki modüller sekmesinden açın.

İlgili dokümanlar

Karakter şablonları

Amaç

Bu sayfa, oyunun karakter ve eşya kalıplarını tutar. Dört bölümden oluşur: karakter tanımları, eşya tanımları, seviye eğrileri ve özellikler. Karakter tanımı bir karakterin hangi özelliklerle ve hangi seviye eğrisiyle doğacağını belirler; eşya tanımı oyunda verilebilen her eşyanın kalıbıdır. Bir kalıbı değiştirdiğinizde eski sürüm silinmez; yeni sürüm yayınlanır.

Ne zaman kullanılır

  • Yeni bir eşya (silah, tüketilebilir, kozmetik…) tanımlamak için.
  • Karakterlerin hangi özelliklerle doğacağını değiştirmek için.
  • Seviye başına gereken XP’yi ayarlamak için.
  • Bir tanımın yayınlamadan önce geçerli olup olmadığını denemek için.

Adım adım

Yeni eşya ekleme

  1. Eşya tanımları bölümünde Yeni silah’a basın. Düğmeyi yalnızca ilgili yazma yetkisi olan yöneticiler görür. Silah ayrı bir tür değildir: “Silah / ekipman” türüdür; formdan türü değiştirebilirsiniz.
  2. Temel bilgiler: ad, anahtar, tür; eşya yığınlanıyorsa en çok yığın.
  3. Ticaret: takas edilebilirlik ve NPC alış ile satış fiyatları.
  4. Görünüm (3D): isterseniz yayınlanmış bir model seçin.
  5. Oyun verisi: oyunun okuyacağı ek değerleri satır satır ekleyin.
  6. Kayıt: bir neden yazıp kaydedin. Eşyanın sistem bilgileri ve ayrıntıları satıra basınca açılır.

Karakter tanımı için yeni sürüm yayınlama

  1. Karakter tanımları bölümünde Yeni sürüm yayınla’ya basın; ya da bir tanımı açıp Bu tanımla yeni sürüm hazırla’yı seçin.
  2. Anahtarı yazın ve en az bir özellik seçin.
  3. Gerekirse Üzerine yazılacak değerler ile özelliklerden gelen değerleri bu tanım için değiştirin.
  4. Görünen adı, modeli ve oyun verisini ayarlayın.
  5. Bir neden yazın ve yayınlayın. Panel, kaydetmeden önce yapılacak işi bir cümleyle söyler.

Not

Özellik sırası ve değerler yayın anında birleştirilir ve yeni sürüm olur. Görünen ad, model ve oyun verisi ise sürümden bağımsızdır; kaydedince hemen yansır.

Yayınlamadan deneme

Doğrula / önizle ile tanımın geçerli olup olmadığını ve birleşik hâlini hiçbir şey kaydetmeden görebilirsiniz. Seviye eğrisi bölümündeki Önizle de eğrinin en yüksek seviyesini ve bu seviye için toplam XP’yi sunucuda hesaplar.

Seviye eğrisi yayınlama

  1. Seviye eğrileri bölümünde Yeni sürüm yayınla’ya basın.
  2. Eğri anahtarını ve eğrinin JSON tanımını yazın; önce Önizle ile sonucu kontrol edin.
  3. Bir neden yazıp Yayınla’ya basın. Yeni sürüm eskisini değiştirmez.

Alanlar ve seçenekler

Eşya formu

AlanZorunluAçıklama
AdEvetOyuncunun göreceği ad
AnahtarEvetKüçük harf, rakam, alt çizgi; kayıttan sonra sabit
TürEvetTüketilebilir, Silah / ekipman, Malzeme, Kozmetik, VIP kartı
YığınlanabilirEvetEvet ya da Hayır
En çok yığınYığınlanabilirse1 ile 2147483647
Takas edilebilirEvetEvet ya da Hayır
NPC alış fiyatı, NPC satış fiyatıHayırBoşsa o yönde ticaret kapalı; 0 bedava
3D modelHayırYayınlanmış model
DeğerlerHayırOyun verisi satırları
İşlem nedeniEvetEn az 8 karakter

Karakter tanımı formu

AlanZorunluAçıklama
Tanım anahtarıEvetKayıttan sonra sabit
Özellik anahtarlarıEvetEn az bir; sıra önemli
Üzerine yazılacak değerlerHayırBölüm, ad, alan ve değer satırları
Görünen adHayırOyunda görünen ad
3D modelHayırYayınlanmış model
Oyun verisiHayırEk değerler
İşlem nedeniEvetEn az 8 karakter

Üzerine yazma satırlarında Bölüm şunlardan biridir: Nitelikler (alanlar: Varsayılan değer, En az, En çok), Kaynaklar (Varsayılan değer, En çok (ifade), Dakikada yenilenme, Sıfıra inince) ve Türetilmiş değerler (İfade). Bir eklentinin kullandığı sistem alanları kilitlidir.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Yayın için en az bir özellik anahtarı yazın”Özelliksiz sürüm tanımı boşaltır; en az bir özellik seçin.
“Yeni sürüm yayınla” ya da “Yeni silah” düğmesi yokHesabınızın rolünde ilgili yazma yetkisi yoktur; sayfa salt okunurdur.
Başkası aynı kaydı değiştirdi, çakışma uyarısı çıktıGüncel hâli yükle ile son hâli görün; ya da bilerek Yine de kaydet ile üzerine yazın.
“Bu anahtar zaten var”Yeni sürüm formuna var olan bir anahtar yazamazsınız; tanımı ayrıntısından Bu tanımla yeni sürüm hazırla ile düzenleyin.
“Sistem” rozetli kaydı silemiyorumBir eklentinin kullandığı sistem kaydıdır; silinmez, değişiklikler yeni sürüm olur.
Özellikleri bu sayfadan değiştiremiyorumÖzellikler bu ekranda yalnızca incelenir.
Eşya türü listesinde “silah” yokSilah ayrı tür değildir; “Silah / ekipman” türüdür.

İlgili dokümanlar

3D modeller

Amaç

Bu sayfa, oyununuzun kullanacağı 3D modellerin kütüphanesidir. Modeli yüklersiniz; sunucu onu oyunun okuyabileceği glb biçimine dönüştürür ve doğrular. Hazır olan modeli yayınlar ve bir eşyaya ya da karaktere bağlarsınız; oyun değişikliği, oyunu yeniden dağıtmadan görür.

Ne zaman kullanılır

  • Yeni bir araç, silah ya da karakter modelini oyuna eklemek için.
  • Dönüştürmesi süren ya da başarısız olan modelin durumuna bakmak için.
  • Bir modelin yayınlanmış sürümünü, kullanan tanımları ya da sürüm geçmişini incelemek için (satıra tıklayın; bkz. Model profili).

Adım adım

Model yükleme

  1. Model yükle düğmesine basın. Düğmeyi yalnızca model yazma yetkisi olan yöneticiler görür.
  2. Yükleme türünü seçin: tek dosya ya da zip paket.
  3. Dosyayı seçin. Dosya 50 MiB’ı aşamaz.
  4. İsterseniz bir ad yazın. Obj, stl, 3mf, dxf dosyalarında ve zip paketlerde Birim ve Yukarı eksen de gelir; varsayılanlar dosya biçimine göre doldurulur, değerleri kontrol edin.
  5. Yükle’ye basın. Yükleme sırasında ilerleme çubuğu görünür; Yüklemeyi iptal et ile durdurabilirsiniz.
  6. Model listede Sırada ve Dönüştürülüyor durumlarından geçip Hazır olur. Liste bu süre içinde kendiliğinden yenilenir.

Modeli yayınlama

Hazır bir model hemen oyuna gitmez. Model satırını açıp Model profilindeki Sürümler tablosundan sürümü Yayınla’yın.

Not

Animasyon taşımayan biçimler (obj, stl, 3mf, dxf) listede “Bu biçim animasyon taşımaz” notuyla görünür. Bu modellere klip eklenemez.

Alanlar ve seçenekler

Yükleme formu

AlanZorunluAçıklama
Yükleme türüEvetTek dosya ya da zip paket
DosyaEvetEn çok 50 MiB
AdHayırBoşsa dosya adı kullanılır, en çok 200 karakter
Ana dosyaHayırYalnızca zip kipinde; birden çok model dosyası varsa zip içindeki yol
BirimBiçime göreMikron, milimetre, santimetre, inç, fit, metre
Yukarı eksenBiçime göreY yukarı ya da Z yukarı

Liste sütunları

Ad, Biçim, Son sürüm (durumla birlikte), Yayında, Sürümler ve Eklendi sütunları varsayılan olarak görünür. Son sürüm no ve Yaşam döngüsü sütunlarını tablonun sütun menüsünden açabilirsiniz. Yaşam döngüsü: Etkin, Arşivli.

Sık karşılaşılan durumlar

DurumNe yapmalı
Model “Başarısız” durumdaModelin satırını açın; sürümün altında nedeni Türkçe cümleyle yazar (örneğin harici dosyaya başvuru ya da 2B dxf). Dosyayı düzeltip yeniden yükleyin.
Zip yükledim, hangi dosyanın ana dosya olduğunu bilmiyorAna dosya alanına model dosyasının zip içindeki yolunu yazın.
Model çok büyük ya da çok küçük görünüyorYükleme sırasında birimi yanlış seçmişsinizdir; modeli yeniden yükleyin.
Model yükle düğmesi yokHesabınızın rolünde model yazma yetkisi yoktur.
Dönüştürme hiç başlamıyor, “yalnız glb” uyarısıDönüştürücü yalnızca amd64 sunucularda çalışır; bu ortamda yalnız glb yükleyebilirsiniz.
Kütüphane listesi boşSeçili oyun ve ortamı kontrol edin; Sahneler modülü kapalıysa model kütüphanesi bu ortamda kullanılamaz.

İlgili dokümanlar

Mağaza

Amaç

Bu sayfa, oyuncuların oyun içinde satın aldığı ürünleri yönetir. Ürünler vitrinlere ayrılır: vitrin, oyunundaki bir mağaza ekranıdır (örneğin Garaj ya da Silahçı). Her ürün bir eşyayı oyun parasıyla ya da gerçek parayla satar; fiyatını, stoğunu, rozetini ve ne zaman satışta olacağını buradan ayarlarsınız.

Ne zaman kullanılır

  • Oyununuza yeni bir mağaza ekranı (vitrin) eklemek için.
  • Vitrine ürün koymak, fiyatını ya da stoğunu değiştirmek için (Ürün sayfası).
  • Ürünleri durumuna göre süzmek, vitrinleri ve ürünleri sıralamak için.

Adım adım

Hazırlık

Bir ürünü satabilmek için önce fiyat verebileceğiniz bir para birimi ve satılacak bir eşya (İçerik ya da Karakter şablonları altında) gerekir. Eksikse sayfa bunu uyarı kutusunda söyler ve ilgili sayfaya bağlantı verir.

Vitrin oluşturma

  1. Yeni vitrin’e (ya da hiç vitrin yoksa İlk vitrini oluştur’a) basın. Düğmeyi yalnızca mağaza yazma yetkisi olan yöneticiler görür.
  2. Adı yazın; anahtar addan önerilir. Anahtar, oyunun vitrini açarken kullandığı kimliktir.
  3. İsterseniz açıklama ve afiş ekleyin, durumu ve satış zamanını seçin.
  4. Bir neden yazın (panel bir öneri doldurur) ve Vitrini oluştur’a basın.

Bir vitrini düzenlemek için vitrin sekmesini seçip Vitrini düzenle’ye basın. Düzenleme penceresinden vitrin silinebilir; silinince içindeki ürünler de silinir, geçmiş satışlar korunur.

Ürünleri görüntüleme ve sıralama

  1. Üstteki vitrin sekmelerinden vitrini seçin.
  2. Izgara ve Liste düğmeleriyle görünümü değiştirin. Liste görünümü tabloyu kendi araç çubuğuyla aratır ve süzer.
  3. Sırala açıkken her ürünün yanındaki düğmelerle ürünü yukarı ya da aşağı, Vitrini sola/sağa taşı ile vitrinin kendisini taşıyabilirsiniz. Sıralama açıkken süzgeçler kapalıdır.
  4. Ürünün ayrıntısına gitmek için kartına ya da satırına tıklayın; yeni ürün için Yeni ürün’e basın.

Araçları mağazaya ekleme

Araçlar içerik grubu yayınlanmışsa Araçları mağazaya ekle düğmesi görünür. Garaj vitrinini ve her araç için kilitli bir ürünü tek adımda hazırlar; sonuç sayfada özetlenir.

Alanlar ve seçenekler

Vitrin formu

AlanZorunluAçıklama
AdEvetEn çok 120 karakter
AnahtarEvetKüçük harf, rakam, tire, alt çizgi; en çok 64 karakter
AçıklamaHayırEn çok 500 karakter
Vitrin afişiHayırPNG, JPEG ya da WebP görsel
DurumEvetTaslak ya da Yayında
Ne zaman satışta?EvetHemen ya da tarih aralığı
Başlangıç, BitişAralıktaBoş uç sınırsızdır
İşlem nedeniEvet8-500 karakter

Süzgeçler

Ara, Durum ve (dar ekranda) Vitrin. Durumlar: Yayında, Taslak, Zamanlanmış, Süresi doldu, Tükendi.

Bölümler

  • Ürünler: Vitrinler ve ürünler.
  • NPC tüccarları: Salt okunur liste. Mağaza ürünleri arka planda otomatik “Mağaza” tüccarını kullanır; burada bir şey yapmanız gerekmez.
  • Uygulama içi ürünler: Salt okunur liste; App Store ve Google Play ürünleri. Bir mağaza ürününü bunlardan birine bağlayabilirsiniz.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Mağaza bu ortamda kapalı”Mağaza modülü kapalıdır; Oyun ayarları içindeki modüller sekmesinden açın.
“Fiyat vermek için önce bir para birimi oluşturun”Para birimleri sayfasından bir para birimi ekleyin.
“Satacak bir eşya yok”Önce İçerik sayfasından bir eşya ekleyin.
Ürün oyunda görünmüyorÜrünün ve vitrinin durumunun Yayında olduğunu ve satış zamanının geldiğini kontrol edin.
“Bu sırada başka biri mağazayı değiştirdi”Mağaza kataloğu sürüm kontrolüyle yazılır; Yeniden yükle’ye basıp değişikliği tekrar yapın.
Vitrin anahtarını değiştirdim, oyun vitrini bulamıyorOyun vitrini anahtarıyla açar; oyundaki çağrıyı da yeni anahtarla güncelleyin.

İlgili dokümanlar

Para birimleri

Amaç

Bu sayfa, oyununuzun para birimlerini listeler. Para birimi, mağazada fiyat vermek ve ödül tablolarında para vermek için kullanılan birimdir (altın, elmas, kuruş gibi). Her kartta para biriminin adı, ikonu, anahtarı, kuruş hanesi, arz tavanı ve oyuncular arası gönderilip gönderilemediği görünür.

Ne zaman kullanılır

  • Mağazada fiyat vermeden ya da ödül tablosu kurmadan önce bir para birimi oluşturmak için.
  • Mevcut para biriminin ayrıntısını açmak için (karta tıklayın; bkz. Para birimi).

Adım adım

  1. Üstteki ortam seçicisinden doğru oyunu ve ortamı seçin.
  2. Yeni para birimi düğmesine basın. Düğmeyi yalnızca para birimi yazma yetkisi olan yöneticiler görür.
  3. Açılan formu Para birimi sayfasındaki yönergeyle doldurun.
  4. Kaydedince para birimi listeye eklenir.

Not

Bu sayfada para birimi silmek yoktur. Aynı anahtarla yapılan her kayıt yeni bir sürüm üretir; eski sürümler değişmez.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form alanı yoktur; oluşturma formu Para birimi sayfasında anlatılır. Kartta görünen bilgiler:

BilgiAçıklama
Ad ve ikonOyuncuların gördüğü ad ve görsel
AnahtarOyunun para birimini bulduğu kimlik
Kuruş hanesiTutarın kaç ondalık haneyle gösterileceği
Arz tavanıToplam basılabilecek miktar; yoksa “Sınırsız”
GönderilebilirOyuncuların birbirine gönderip gönderemediği

Sık karşılaşılan durumlar

DurumNe yapmalı
“Ekonomi bu ortamda kapalı”Ekonomi modülü kapalıdır; Oyun ayarları içindeki modüller sekmesinden açın.
“Henüz para birimi yok”Yeni para birimi ile ilkini oluşturun.
Yeni para birimi düğmesi yokHesabınızın rolünde para birimi yazma yetkisi yoktur.

İlgili dokümanlar

Ödül tabloları

Amaç

Ödül tablosu, bir NPC’nin, yaratığın, turnuvanın ya da görevin hangi ödülü hangi şansla vereceğini tanımlar. Bu sayfa ortamdaki bütün tabloları kart olarak listeler: ad, anahtar, satır sayısı, “her zaman verilen” ödül sayısı ve tablonun kaç yerde kullanıldığı görünür.

Ne zaman kullanılır

  • Yeni bir ödül tablosu oluşturmak için.
  • Bir tablonun nerede kullanıldığına ya da kaç satırı olduğuna hızlıca bakmak için.
  • Tabloyu açıp düzenlemek, denemek ya da silmek için (karta tıklayın; bkz. Ödül tablosu).

Adım adım

  1. Üstteki ortam seçicisinden doğru oyunu ve ortamı seçin.
  2. Yeni ödül tablosu’na basın. Düğmeyi yalnızca ödül tablosu yazma yetkisi olan yöneticiler görür.
  3. Formu Ödül tablosu sayfasındaki yönergeyle doldurun.
  4. Kaydedince tablo listeye eklenir.

Not

Tablo tek başına ödül vermez. NPC, görev ya da kural tabloyu anahtarıyla çağırdığında ödül verilir; ayrıntı için Ödüller rehberine bakın.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form alanı yoktur; oluşturma formu Ödül tablosu sayfasında anlatılır. Kartta görünen bilgiler:

BilgiAçıklama
Ad ve anahtarTablonun adı ve oyunun onu çağırdığı kimlik
SatırlarŞansa bağlı ödül satırı sayısı
Her zaman verilenÇekilişten bağımsız, her seferinde verilen ödül sayısı
KullanımTablonun kaç yerde kullanıldığı; hiç kullanılmıyorsa “Kullanılmıyor”

Sık karşılaşılan durumlar

DurumNe yapmalı
“Ödüller bu ortamda kapalı”Ödüller modülü kapalıdır; Oyun ayarları içindeki modüller sekmesinden açın.
“Henüz ödül tablosu yok”Yeni ödül tablosu ile ilkini oluşturun.
Yeni tablo düğmesi yokHesabınızın rolünde ödül tablosu yazma yetkisi yoktur.

İlgili dokümanlar

Ekonomi raporları

Amaç

Bu sayfa, oyununuzdaki paranın durumunu gösterir. Üç iş yapar: bir oyuncunun, karakterin, klanın ya da sistemin cüzdanını, envanterini ve eşyalarını sorgulatır; gerektiğinde cüzdana bakiye ekler ya da düşer; para biriminin günlük özetini, en çok bakiyesi olanları ve uyarıları, ayrıca oyuncular arası takasları gösterir.

Ne zaman kullanılır

  • Bir oyuncunun parasının nereye gittiğini araştırmak için.
  • Yanlış bakiyeyi düzeltmek için (destek işi).
  • Paranın oyunda çok mu basıldığını ya da çok mu yakıldığını izlemek için.
  • Bir takasın durumuna bakmak için.

Adım adım

Cüzdan sorgulama

  1. Sahip türünü seçin (oyuncu, karakter, klan ya da sistem).
  2. Sahibi adıyla arayıp seçin. Sistem sahibi için kimliği yazıp Sorgula’ya basın.
  3. Sonuçta o sahibin Cüzdanlar, Envanter ve Eşyalar bölümleri görünür. Bir cüzdan satırındaki Hareketler düğmesi o cüzdanın defter hareketlerini açar.

Not

Oyun parası oyuncunun cüzdanındadır. Karakter seçerseniz panel karakteri sahibi olan oyuncuya çözer ve bunu ekranda söyler; oyuncusuz karakter karakter olarak kalır. Oyuncu için asıl yer, oyuncu profilindeki cüzdan bölümüdür; sonuçtaki “Oyuncu profilinde aç” bağlantısı oraya götürür.

Bakiye ekleme ya da düşme

  1. Cüzdan satırındaki bakiye işlemi düğmesine basın. Düğmeyi yalnızca bakiye verme ya da düşme yetkisi olan yöneticiler görür.
  2. Tutar’ı yazın.
  3. İşlem nedenini yazın.
  4. Bakiye ekle ya da Bakiyeden düş’e basın.

Uyarı

Bakiye işlemi parayı gerçekten değiştirir ve defterde kalıcı olarak görünür; geri alınamaz, ancak ters işlemle düzeltilir. Cüzdan siz bakarken başka bir işlemle değişirse işlem reddedilir; sayfayı yenileyip tekrar deneyin.

Günlük rapor

  1. Günlük raporlar bölümünü açın.
  2. Para birimi anahtarını, Başlangıç ve Bitiş günlerini girin.
  3. Günlük özet, En çok bakiyesi olanlar ve Uyarılar kutuları dolar.

Not

Günlük raporlar şimdilik ham veri olarak (anahtar ve değer çiftleri) gösterilir; grafik henüz yoktur. Para birimi başına okunaklı özet için Para birimi sayfasındaki “Son 30 gün” kartına bakın.

Takaslar

Takaslar bölümü, oyuncular arası takasları toplam sayı, durum, taraf sayısı ve bacakları (verilen para ya da eşya) ile listeler.

Alanlar ve seçenekler

AlanNeredeAçıklama
Sahip türüSahip sorgusuOyuncu, karakter, klan ya da sistem
Sahip (arama)Sahip sorgusuAda göre arayıp seçin
Sahip kimliğiSahip sorgusuYalnızca sistem sahibi için
Cüzdan kimliğiSahip sorgusuCüzdanları görme yetkisi yoksa; UUID
TutarBakiye penceresiPara biriminin ondalık haneleriyle
İşlem nedeniBakiye penceresiEn az 8 karakter
Para birimi anahtarıGünlük raporlarÖrneğin gold
Başlangıç, BitişGünlük raporlarKapanmış günler

Sık karşılaşılan durumlar

DurumNe yapmalı
“Henüz bir sahip seçilmedi”Yukarıdan sahip türünü seçip sahibi arayın.
Cüzdanlar bölümü yetki uyarısı gösteriyorHesabınızın rolünde cüzdan okuma yetkisi yoktur. Bakiye yetkiniz varsa cüzdan kimliğini yapıştırarak işlem yapabilirsiniz; hareketler yine de listelenemez.
“Geçerli bir kimlik (UUID) girin”Kimlik tireli 8-4-4-4-12 biçiminde olmalı.
Bakiye işlemi reddedildiCüzdan siz bakarken değişmiş olabilir; sayfayı yenileyin. Tutarın para biriminin hane sayısına uyduğunu da kontrol edin.
Rapor boşPara birimi anahtarını ve kapanmış bir tarih aralığı girdiğinizden emin olun. Yalnızca kapanmış günler raporlanır.
“Takas yok”Bu ortamda takas kaydı bulunamamıştır. Bölüm yetki uyarısı gösteriyorsa hesabınızın rolünde takasları okuma yetkisi yoktur.

İlgili dokümanlar

Kurallar

Amaç

Bu sayfa oyununuzun davranışını kod yazmadan ayarladığınız yerdir. İki tür şey görürsünüz: oyunun eklentisinin sunduğu oyun kuralı kartları (örneğin polis ya da şehir kalabalığı değerleri) ve sizin yazdığınız otomasyonlar (“seviye 10’a çıkınca karaktere güç ekle” gibi). Değişiklikler yayınlayana kadar oyuna geçmez; her yayın geri alınabilir.

Ne zaman kullanılır

  • Oyunun hazır ayarlarını (hız, süre, yoğunluk gibi) kod değiştirmeden ayarlamak için.
  • Bir oyun olayı olduğunda karaktere otomatik ödül, puan ya da bayrak vermek için.
  • Yanlış yayınlanan bir ayarı önceki sürüme döndürmek için (Kural geçmişi).

Adım adım

Bir oyun kuralını değiştirme

  1. Listeden bir karta tıklayın; kartın ayar sayfası açılır (Kural kartı).
  2. Değeri değiştirin. Değişiklik henüz oyuna geçmez; sayfanın altında “N değişiklik yayınlanmadı” çubuğu görünür.
  3. Başka kartlara geçip oradaki ayarları da değiştirebilirsiniz. Bekleyen değişikliklerin hepsi tek yayına girer.
  4. Gözden geçir ve yayınla düğmesine basın, nedeninizi yazın ve onaylayın.

Otomasyon ekleme

  1. Yeni kural düğmesine basın ya da liste boşsa Şablondan başla düğmesini kullanın.
  2. Otomasyon düzenleyicisinde olayı, koşulları ve yapılacak işi seçin (Otomasyon düzenleyici).
  3. Yayınlayın. Kural varsayılan olarak kapalı kaydedilir; hazır olunca listedeki Aç düğmesiyle açarsınız.

Otomasyonu açma ve kapatma

Kartın sağındaki Aç ya da Kapat düğmesi bir onay penceresi açar. Nedeninizi yazıp onaylayın; kapatılan kural oyunda çalışmayı bırakır, silinmez.

Alanlar ve seçenekler

AlanZorunluAçıklama
Kural araHayırKart başlığı, açıklaması ve otomasyon anahtarında arar
Konu düğmeleriHayırListeyi seçtiğiniz konulara daraltır; hiçbiri seçili değilse hepsi görünür
İşlem nedeni (aç/kapat penceresi)Evet8-500 karakter; işlem geçmişine yazılır

Üstteki durum şeridi yayındaki sürümü, yayınlayan kişiyi ve yazdığı nedeni gösterir. Henüz hiçbir şey yayınlanmadıysa “oyun bugünkü (kod) değerlerle çalışıyor” yazar.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu oyunun eklentisi panelden ayarlanabilir kural sunmuyor” yazıyorOyunun eklentisi kural kartı bildirmiyordur. Otomasyonları yine bu sayfadan ekleyebilirsiniz.
“Kuralları görmek için yetkiniz yok” yazıyorRolünüzde kural okuma yetkisi yoktur; bir yöneticiden isteyin.
Yeni kural düğmesi yokDüğmeyi yalnızca kural yazma yetkisi olan yöneticiler görür.
Kartta “Gözlemde” rozeti varKuralın koruması gevşetilmiştir; kural oyuna uygulanmıyor, yalnızca kaydediliyor. Ayrıntı için Güvenlik şalterleri sayfasına bakın.
Kartta “Yeniden başlatınca” rozeti varDeğer yayınlandı ama canlı dünya yeni değeri sunucu yeniden başlayınca alır.
Bildirim kuralları nerede?Bildirim kuralları ayrı bir ekrandadır; sayfanın altındaki Bildirimler kartından açılır.

İlgili dokümanlar

Leaderboard · Sıralama tabloları

Amaç

Leaderboard (sıralama tablosu), oyuncuların puanlarına göre sıralandığı tablodur. Bu sayfa ortamınızdaki tabloları kart olarak listeler: ad, sıfırlama aralığı, oyunda görünüp görünmediği, bu sezonki oyuncu sayısı, lider ve bir sonraki sıfırlamaya kalan süre. Yeni tabloyu da buradan kurarsınız. Puanları oyun sunucunuz gönderir; panel puan yazmaz.

Ne zaman kullanılır

  • Oyun sunucunuz puan göndermeden önce bir tablo kurmak için.
  • Hangi tablonun görünür olduğunu, ne zaman sıfırlanacağını ve liderini görmek için.
  • Bir tablonun ayrıntısına (sıralama, geçmiş sezonlar, ayarlar) geçmek için.

Adım adım

Yeni tablo kurma

  1. Yeni tablo düğmesine basın. Düğmeyi yalnızca tablo yönetme yetkisi olan yöneticiler görür.
  2. Tablonun adı alanına bir ad yazın; anahtar otomatik dolar. İsterseniz anahtarı kendiniz değiştirin.
  3. Hangi puan önde? ve Ne zaman sıfırlansın? seçeneklerini belirleyin. “Özel aralık” seçerseniz gün ve saat yazın.
  4. Puan nasıl gösterilsin? seçeneğini belirleyin; gerekirse bir birim eki yazın.
  5. Oyuncuların tabloyu oyunda görmesini istiyorsanız Oyunda görünsün anahtarını açın.
  6. Altındaki önizleme satırında puanın nasıl görüneceğine bakın ve Tabloyu oluştur düğmesine basın. Başarılı olunca tablonun ayrıntı sayfası açılır.

Uyarı

Anahtar, sıralama yönü ve sıfırlama aralığı tablo kurulduktan sonra değiştirilemez. Değiştirmek için yeni bir tablo kurmanız gerekir; bu yüzden kurmadan önce dikkatle seçin.

Bir tabloyu açma

Kartın adına tıklayın; ayrıntı sayfası açılır (Leaderboard ayrıntısı).

Alanlar ve seçenekler

AlanZorunluAçıklama
Tablonun adıEvet1-400 karakter
AnahtarEvetKüçük harf ya da rakamla başlar; küçük harf, rakam, nokta, tire, alt çizgi; en fazla 200 karakter
Hangi puan önde?EvetYüksek puan ya da düşük puan
Ne zaman sıfırlansın?EvetHiç, her gün, her hafta (Pazartesi), her ay ya da özel aralık
Gün, SaatYalnızca özel aralıktaGün ya da saatten en az biri; toplam en az 1 saat, tam sayı
Puan nasıl gösterilsin?EvetSayı, para ya da süre
Birim ekiHayırEn fazla 8 karakter; süre biriminde yoktur
Oyunda görünsünHayırVarsayılan kapalı

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu anahtar kullanılıyor ya da bir eklentiye ayrılmış”Başka bir anahtar seçin. Bazı anahtar önekleri oyunun eklentisine ayrılmıştır.
Listede tablo yok ve “package-ride eklentisi ‘Günlük kazanç’ tablosunu ilk teslimatta kendiliğinden kurar” yazıyorEklenti tabloyu ilk teslimatta kurar; henüz teslimat olmamıştır. Beklemeniz yeterlidir.
“Sıralama (Competition) modülü bu ortamda kapalı”Oyun ayarları › Modüller sayfasından modülü açın.
“Yeni tablo” düğmesi yokTablo yönetme yetkiniz yoktur; bir yöneticiden isteyin.
Yalnızca ilk tabloları görüyorumListe en fazla belirli sayıda tablo gösterir; sınıra gelince sayfa bunu yazar. Daha fazlası bu sürümde listelenmiyor.
Kartta “Kaynak: … eklentisi (otomatik)” yazıyorTabloyu bir eklenti kurmuştur ve yalnız o eklenti puan yazar.

İlgili dokümanlar

Bildirimler

Amaç

Bu sayfa oyuncuya giden bildirimleri yönettiğiniz yerdir. Bildirimin gidebilmesi için üç şey gerekir: bir sağlayıcı (Firebase, Apple ya da OneSignal gibi anlık bildirim servisi), bir şablon (mesajın metni) ve bir kural ya da elle test gönderimi (mesajın ne zaman gideceği). Sayfa altı sekmeden oluşur: Genel bakış, Şablonlar, Kurallar, Sağlayıcılar, Gönderim geçmişi ve Cihazlar.

Ne zaman kullanılır

  • Bildirim altyapısını ilk kez kurarken (Genel bakış sekmesindeki üç adımı izleyin).
  • Seviye atlama gibi bir olayda otomatik bildirim göndermek için.
  • Bir bildirimin neden gitmediğini araştırmak için (Gönderim geçmişi ve Cihazlar sekmeleri).

Adım adım

1. Sağlayıcı bağlama

  1. Sağlayıcılar sekmesinde kullandığınız servisin kartında Bağla düğmesine basın. Bu işlem yalnızca süper yöneticiye açıktır.
  2. Servisin istediği bilgileri girin ve anahtar dosyasını seçin. Anahtar yalnızca yazılır; kaydedildikten sonra bir daha görüntülenmez, yalnızca parmak izi görünür.
  3. Nedeninizi ve doğrulayıcı uygulamanızdaki 6 haneli kodu yazıp Kaydet düğmesine basın.
  4. Kart birkaç saniye içinde Hazır olur; sunucu ayarı yükler. Hata görürseniz kartta nedeni düz Türkçe yazılır.

2. Şablon yazma

  1. Şablonlar sekmesinde Yeni şablon düğmesine basın (ya da boşsa İlk şablonu oluştur; Örnekle başla hazır bir örnek getirir).
  2. Anahtar, görünen ad ve dili yazın; başlığı ve metni girin. Oyuncuya özel değerler için metne {{ad}} yazın ve aynı adı Değişkenler listesine ekleyin.
  3. Sağdaki canlı önizlemede telefon kilit ekranı ve tarayıcı bildirimi görünümüne bakın, Şablonu oluştur düğmesine basın.
  4. Başka dil eklemek için şablonun ayrıntısında + Dil ekle düğmesini kullanın. Her kayıt yeni bir sürüm olarak saklanır; sürüm geçmişinden eski sürüme dönebilirsiniz.

Not

Oyuncunun dilinde sürüm yoksa yedek dil olan İngilizce (en) sürümü kullanılır. İngilizce sürüm de yoksa bildirim gönderilmez.

3. Kural ekleme ya da test gönderme

  • Kural: Kurallar sekmesinde Yeni kural ile olayı, koşulları, şablonu, dili ve kanalı seçin; Örnek olayla dene bölümünde kuralı hiçbir bildirim göndermeden deneyin.
  • Test: Gönderim geçmişi sekmesinde Test gönder düğmesine basın; oyuncuyu, şablonu, dili ve kanalı seçip gönderin. Sayfa sonucu en fazla 60 saniye izler. Sık denemeler sınırlanır (15 dakikada 10 deneme).

Alanlar ve seçenekler

AlanZorunluAçıklama
Şablon anahtarı, görünen ad, açıklama, dilAnahtar, ad, dil zorunluAnahtar değişmez; ad en fazla 120, açıklama 500 karakter
Başlık, MetinEvetBaşlık için 65, metin için 240 karakterlik yumuşak sınır
Değişken adı, tür, örnek değer, zorunluDeğişken ekleninceEn fazla 32 değişken; sayı değişkenine tam sayı
Sürüm notuHayırEn fazla 200 karakter
Kural anahtarı, kural adıEvetAnahtar en fazla 64 karakter, değişmez; ad en fazla 120
Ne zaman, koşullarNe zaman zorunluEn fazla 20 koşul satırı; koşul yoksa olay her olduğunda çalışır
Şablon, dil, kanalEvetKanal: oyun içi, anlık bildirim ya da ikisi
Sağlayıcı bilgileri ve anahtar dosyasıEvetFirebase: hizmet hesabı JSON; Apple: Team ID, Key ID, Bundle ID, ortam ve .p8; OneSignal: App ID ve REST API anahtarı
İşlem nedeni, doğrulama koduEvetNeden 8-500 karakter; kod 6 hane

Bir ortamda en fazla 50 bildirim kuralı olabilir.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu oyunda bildirimler kapalı”Oyun ayarları › Modüller sayfasından Bildirimler modülünü açın.
Kartta “Bağlı değil” yazıyorSağlayıcı bu ortamda bağlı değildir; Bağla ile bilgileri girin. Bağlamak süper yönetici yetkisi ister.
“Sağlayıcı anahtarı reddetti”Firebase için hizmet hesabı dosyasını, Apple için .p8 anahtarını ve Team ID, Key ID, Bundle ID değerlerini, OneSignal için REST API anahtarını yenileyin.
“Anahtar dosyası biçimi tanınmadı”Firebase için hizmet hesabı JSON dosyası, Apple için .p8 dosyası gerekir; Google OAuth istemci dosyası ya da .p12 sertifikası olmaz.
Test “oyuncunun ulaşılabilir cihazı yok” diyorOyuncu oyunda bildirimleri açmamıştır. Oyun içi kanalı seçin ya da Cihazlar sekmesinde oyuncunun cihazı olup olmadığına bakın.
Kural kaydedilemiyor: “Şablon arşivlenmiş” ya da “bu dilde şablon yok”Şablonu arşivden çıkarın ya da seçtiğiniz dili şablona ekleyin.
Sunucu ayarından gelen kurallar çalışıyorPanelde ilk kuralı eklediğinizde bu ortamda onların yerini panel kuralları alır.
Kural ya da şablon düzenlenemiyorBildirim yönetimi yetkiniz yoktur; görünüm salt okunurdur. Bir yöneticiden isteyin.

İlgili dokümanlar

Genel

Amaç

Oyun ayarları sayfasının Genel sekmesi seçili oyunun kimliğini gösterir: oyun adı, kısa ad, bağlı olduğu stüdyo ve kullandığı eklenti. Yetkili yöneticiler buradan oyunu yeniden adlandırabilir ya da arşivleyebilir. Bu sekme sunucuya istek atmaz; bilgiler seçili oyun ve ortamdan okunur.

Ne zaman kullanılır

  • Hangi oyun ve stüdyonun seçili olduğunu doğrulamak için.
  • Oyunun adını değiştirmek için (süper yönetici).
  • Artık kullanılmayan bir oyunu listelerden ve seçiciden gizlemek için (süper yönetici).

Adım adım

Oyunu yeniden adlandırma

  1. Üstteki ortam seçicisinden oyunu seçin.
  2. Yeniden adlandır düğmesine basın. Düğmeyi yalnızca süper yönetici görür; diğerleri için sayfa salt okunurdur.
  3. Yeni adı yazın (1-160 karakter), nedeninizi yazın ve Adı kaydet düğmesine basın.

Kısa ad değişmez.

Oyunu arşivleme

  1. Sayfanın altındaki Tehlikeli işlemler kutusunda Arşivle düğmesine basın.
  2. Nedeninizi yazıp onaylayın.

Uyarı

Arşiv oyunu listelerden ve seçiciden gizler; veri silinmez. Oyunun canlı yetkisi ya da etkin bir API anahtarı varsa arşivleme reddedilir; pencere nedenini gösterir. Seçili ortam bu oyuna aitken Arşivle düğmesi kapalıdır; önce başka bir oyunun ortamını seçin.

Alanlar ve seçenekler

AlanZorunluAçıklama
Oyun adıSalt okunurYeniden adlandırma penceresinden değişir
Kısa adSalt okunurOyun kurulurken verilir, değişmez
StüdyoSalt okunurOyunun bağlı olduğu stüdyo
EklentiSalt okunurEklenti yoksa “Eklenti yok” yazar; yetkiniz varsa Eklentiler sayfasına bağlantı çıkar
Yeni ad (yeniden adlandırma penceresi)EvetMevcut addan farklı olmalı
İşlem nedeni (pencerelerde)Evet8-500 karakter

Sık karşılaşılan durumlar

DurumNe yapmalı
“Ortam seçilmedi” yazıyorÜst çubuktan bir oyun ve ortam seçin.
Yeniden adlandır ve Arşivle düğmeleri yokBu işlemler yalnızca süper yöneticiye açıktır; “bu sayfa sizin için salt okunurdur” yazısını görürsünüz.
“Önce başka bir ortam seçin” yazıyorArşivlemek istediğiniz oyunun ortamı şu an seçili. Başka bir oyunun ortamını seçip yeniden deneyin.
Arşivleme reddedildiOyunun canlı yetkisi ya da etkin bir API anahtarı vardır. Pencerede yazan nedene göre etkin API anahtarlarını iptal edin ya da Eklentiler sayfasından eklentinin durumunu kapatın, sonra yeniden deneyin.

İlgili dokümanlar

Eklentiler

Amaç

Eklenti, oyunun sunucu tarafı mantığını çalıştıran modüldür: oyuna özel komutlar, sorgular ve panel ekranları sunar. Bu sayfa oyunun eklentisini, seçili ortamda yetkili diğer eklentileri ve her birinin durumunu gösterir. Eklentinin yönetim ekranlarına buradan ve kenar çubuğundan gidilir.

Ne zaman kullanılır

  • Eklentinin bu ortamda açık olup olmadığını görmek için.
  • Bir eklentiyi geçici olarak salt okunur yapmak, boşaltmak ya da kapatmak için (yetkili yönetici).
  • Eklentinin sürümünü, izinlerini, işlemlerini ve bağımlılıklarını incelemek için.
  • Oyuna eklenti bağlamak ya da yeni sürüme geçmek için.

Adım adım

Eklentinin ayrıntılarını görme

Kartın Ayrıntılar düğmesi sürümü, izinleri, bağımlılıkları ve eklentinin sunduğu işlemleri (sorgu ya da komut, kimin erişebildiği) listeler. Bilgiler eklentinin kendi bildiriminden okunur; buradan değiştirilemez.

Eklentinin durumunu değiştirme

  1. Kartın Durumu değiştir düğmesine basın. Düğmeyi yalnızca eklenti yönetme yetkisi olan yöneticiler görür.
  2. Yeni durum listesinden hedefi seçin; seçtiğiniz durumun oyuncu ve yönetici etkisi pencerede yazar.
  3. Nedeninizi yazın ve Durumu değiştir düğmesine basın. Kapalı seçerseniz ayrıca bir onay sorulur.

Geçişler sınırlıdır: Açık durumdan salt okunur, boşaltılıyor ya da kapalıya; salt okunur ve boşaltılıyor durumlarından açık ya da kapalıya; kapalı durumdan salt okunur ya da açığa geçilebilir.

Oyuna eklenti bağlama ve sürüme geçme

Sayfanın başındaki oyun eklentisi kartı bu işlemler içindir:

  1. Eklenti bağlı değilse Eklenti bağla düğmesiyle listeden eklentiyi seçip bağlayın.
  2. Yeni derlenmiş sürüme geçmek için önce Drain başlat (boşaltma) ile oyuncuları boşaltın; onay için eklenti kimliğini yazın.
  3. Geri sayım bitince yeni sürümü dağıtın, sonra Sürüme geç düğmesine basın. Geri sayım bitene kadar düğme kapalıdır.

Uyarı

Kapalı durumunda oyuncular ve yöneticiler bu ortamda eklentiyi kullanamaz ve eklenti kenar çubuğundan kalkar. Geri açmak için durumu ayrıca değiştirmeniz gerekir.

Alanlar ve seçenekler

AlanZorunluAçıklama
Yeni durumEvetAçık, Salt okunur, Boşaltılıyor ya da Kapalı
İşlem nedeniEvet8-500 karakter
Eklenti (bağlama penceresi)EvetSunucuda kurulu eklentilerden biri
Eklenti kimliği (boşaltma penceresi)EvetKimlik aynen yazılmalı

Kartın üstündeki rozet eklentinin bu ortamdaki durumunu gösterir. “Bu ortamda yetkisiz” yazıyorsa eklenti kurulu ama bu ortam için yetki satırı yoktur; eklenti burada kullanılamaz.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Kurulu eklenti yok”Sunucu eklentisiz başlatılmıştır. Eklentiler sunucu derlemesine eklenir ve operatör tarafından etkinleştirilir.
Kartta “Yetki sürümü … kurulu sürüm …” uyarısı varOrtam yetkisi kurulu sürümle eşleşmiyor; sürüm etkinleştirmesi bekleniyor. Bu durumda yalnızca boşaltma ve kapatma seçilebilir.
“Bu durum geçişine izin verilmiyor”Durum siz bakarken değişmiş olabilir; sayfayı yenileyip yeniden deneyin.
Durumu değiştir düğmesi yokEklenti yönetme yetkiniz yoktur; bir yöneticiden isteyin.
Ekranları aç düğmesi yokEklenti panel ekranı tanımlamıyor ya da bu ortamda kapalı.
“Kullanımdan kalkan sorgular” kutusu görünüyorEklentinin bazı sorguları yeni bir yüzeyle değiştirilmiştir. Kutuda eski adı, yerine geçeni ve hangi sürümden beri kalktığını görürsünüz; oyun kodunuzu yeni yüzeye taşıyın.

İlgili dokümanlar

Güvenlik şalterleri

Amaç

Güvenlik şalteri, oyunun bir koruma kuralını açıp gevşetmenizi sağlar. Zorla konumunda kural oyuna uygulanır. Gevşek konumunda kural oyuna uygulanmaz ya da yalnızca kaydedilir; ham ayardaki karşılığı “gözleme alındı”, “kapalı” ya da “devre dışı” olarak yazar. Şalteri sunucuyu yeniden dağıtmadan, ortam başına ve geri alınabilir biçimde değiştirirsiniz.

Ne zaman kullanılır

  • Bir koruma kuralı oyuncuları haksız yere engelliyorsa kuralı geçici olarak gevşetmek için.
  • Bir kuralın yalnızca kaydedildiğini (gözlemde olduğunu) görmek ve yeniden zorlamak için.
  • Hangi korumaların gevşek olduğunu denetlemek için.

Adım adım

  1. Her satırda şalterin adını, ne yaptığını, konumunu (Zorla, Gözleme alındı, Kapalı, Devre dışı), kaynağını ve son değişikliği görürsünüz.
  2. Zorla konumundaki bir korumayı gevşetmek için Gevşet düğmesine basın. Açılan pencerede etkiyi okuyun, nedeninizi yazın ve Gevşet ile onaylayın.
  3. Gevşek bir korumayı geri açmak için Zorla düğmesine basın; onay sormaz.
  4. Panelden değiştirilmiş bir şalteri dağıtım varsayılanına döndürmek için Varsayılana dön düğmesine basın.

Satırın kaynak rozeti şalterin değerinin nereden geldiğini gösterir: panel (siz değiştirdiniz), dağıtım varsayılanı ya da yalnız dağıtım (panelden değiştirilemeyen ayarlar). Yalnız dağıtım satırları salt okunur gösterilir.

Not

Değişiklik en geç birkaç saniye içinde (sayfada yazan süre kadar) her sunucu kopyasında uygulanır; anında etki ve anlık itme yoktur.

Uyarı

Gevşek koruma, ilgili kuralın oyuna uygulanmadığı ya da yalnızca kaydedildiği anlamına gelir. Sayfanın üstündeki uyarı bandı kaç korumanın gevşek olduğunu söyler. Okuma hatası korumayı asla gevşetmez; sayfa hata gösterir.

Alanlar ve seçenekler

AlanZorunluAçıklama
İşlem nedeni (gevşetme penceresi)Evet8-500 karakter

Sayfanın altındaki sayaç kartı çalışma sayaçlarını gösterir; yalnızca bu yetkisi olan yöneticiler görür.

Sık karşılaşılan durumlar

DurumNe yapmalı
Düğmeler yok, “salt okunurdur” yazıyorŞalterleri yalnızca süper yönetici değiştirebilir; siz değerleri okuyabilirsiniz.
“Güvenlik şalterleri okunamadı”Liste yüklenemedi. Korumalar gevşetilmiş sayılmaz; Tekrar dene ile yeniden okuyun.
Bir kural kartında “Gözlemde” rozeti görüyorumO kartın şalteri gevşektir. Bu sayfadan ya da Kurallar sayfasındaki kart sayfasından Zorla diyerek yeniden açın.
Neden sorulmadan değiştiZorla ve Varsayılana dön düğmeleri gerekçe sormaz; denetim kaydına “operatör şalteri Zorla konumuna aldı (panel)” gibi sabit bir gerekçe yazılır.

İlgili dokümanlar

İşlem geçmişi

Amaç

Panelden yapılan her değişiklik değiştirilemeyen bir kayıt bırakır: hangi yönetici, hangi kayıt üzerinde, ne zaman, hangi eylemi ve hangi nedenle yaptı. Bu sayfa o kayıtları tablo olarak gösterir. Kayıtlar yalnızca eklenir; hiçbiri silinmez ya da düzenlenmez.

Ne zaman kullanılır

  • Bir ayarın kim tarafından, ne zaman ve neden değiştirildiğini bulmak için.
  • Bir sorun sonrası “ne değişmişti” sorusunu yanıtlamak için.
  • Bir işlemin önceki ve sonraki durumunu karşılaştırmak için.

Adım adım

  1. Tabloda en yeni kayıtlar üsttedir. Her satırda zaman, yönetici, eylem, hedef türü, hedef ve neden vardır.
  2. Süzgeçler ve arama ile listeyi daraltın: eylem, hedef, yönetici, oyuncu ve zaman aralığı. İstek kimliği, ortam ve oyuncu sütunları varsayılan olarak gizlidir; sütun menüsünden açabilirsiniz.
  3. Bir satırın İncele düğmesine basın. Açılan panelde yönetici, hedef, ortam, istek kimliği, neden ve işlemin Önce ve Sonra durumları görünür.
  4. Kayıt bir oyuncuya bağlıysa panelde oyuncunun sayfasını açan bir bağlantı çıkar.

Süper yöneticiler iki sekme görür: Ortam kayıtları (seçili ortama bağlı işlemler) ve Operatör işlemleri (görünen ad, parola, oturum, giriş ve yönetici yönetimi gibi hiçbir ortama bağlı olmayan işlemler).

Not

Operatör işlemleri sekmesindeki kayıtlar bir oyuncuya bağlı değildir; bu sekmede oyuncu süzgeci sonuç vermez.

Alanlar ve seçenekler

AlanZorunluAçıklama
Süzgeçler ve aramaHayırEylem, hedef, yönetici, oyuncu ve zaman aralığı; sunucuda uygulanır
SütunlarHayırGizlenebilir ve sabitlenebilir; kayıt zamanı gizlenemez
Sayfa boyuHayır15, 25, 50, 100 ya da 500
Kapsam sekmesiHayırYalnızca süper yöneticide: ortam kayıtları ya da operatör işlemleri

Sık karşılaşılan durumlar

DurumNe yapmalı
Operatör işlemleri sekmesi yokBu kayıtları yalnızca süper yönetici görür.
Aradığım kayıt görünmüyorSeçili ortamı ve zaman aralığını kontrol edin. Ortam kayıtları seçili ortama aittir; ortamdan bağımsız işlemler Operatör işlemleri sekmesindedir.
Bir kaydı silmek ya da düzeltmek istiyorumMümkün değil; kayıtlar değiştirilemez.

İlgili dokümanlar

Giriş yöntemleri

Amaç

Bu sekme seçili ortamda oyuncuların hangi sağlayıcılarla giriş yapabileceğini yönettiğiniz yerdir. Bugün panelden yönetilen sağlayıcı Google’dır; misafir girişi ve e-posta ile giriş burada ayarlanmaz. Sayfada ayrıca ortamın oturum politikası (tek aktif oturum) vardır. Her sağlayıcının ayrıntı sayfasında durumu, kapsamları, istemci bilgileri ve güvenli ek bilgileri bulunur.

Ne zaman kullanılır

  • Google ile girişi ilk kez açarken (istemci kimliği, gizli anahtar ve yönlendirme tabanı girmek için).
  • Google girişini geçici kapatmak ya da yeniden açmak için.
  • Oyuncunun yeni girişinde eski oturumlarının kapanmasını istiyorsanız.

Adım adım

Google girişini yapılandırma

  1. Sekmenin üstünden ortamı seçin, listedeki Google satırında Detay düğmesine basın.
  2. OAuth istemci bilgisi kartında OAuth bilgilerini düzenle düğmesine basın. Düğmeyi yalnızca süper yönetici görür.
  3. İstemci kimliğini, istemci sırrını ve yönlendirme tabanını girin.
  4. Nedeninizi ve doğrulayıcı uygulamanızdaki 6 haneli kodu yazıp Kaydet düğmesine basın.
  5. Google Cloud konsolunda Authorized redirect URI olarak yönlendirme tabanınızın sonuna /google eklenmiş adresi kaydedin.
  6. Sağlayıcı Etkin değilse ayrıntı sayfasında Etkinleştir düğmesine basın.

Uyarı

İstemci sırrı yalnızca yazılır. Kaydedildikten sonra panelde bir daha görünmez; kartta yalnızca “yapılandırıldı” durumu ve kısa bir parmak izi yer alır. Kaybederseniz Google’dan yenisini alıp yeniden girin.

Tek aktif oturum

  1. Giriş ayarları kartındaki Tek aktif oturum anahtarını açın ya da kapatın. Anahtarı yalnızca giriş yöntemi yazma yetkisi olan yöneticiler değiştirebilir.
  2. Açılan pencerede nedeninizi yazıp onaylayın.

Kartın altındaki satır değerin kaynağını gösterir: ortam ayarı ya da sunucu varsayılanı.

Panelden girilen bilgiyi kaldırma

Panelden girilmiş Google bilgisi için ayrıntı sayfasında Kaldır düğmesi görünür. Sağlayıcının kimliğini yazarak onaylar, nedeninizi ve doğrulama kodunu girersiniz. Sunucuda varsayılan bir değer varsa ortam ona döner; yoksa sağlayıcı bu ortamda yapılandırılmamış olur.

Alanlar ve seçenekler

AlanZorunluAçıklama
Tek aktif oturumHayırOrtam başına anahtar; açık oturumlara dokunmaz
İstemci kimliği (client id)EvetEn fazla 256 karakter, boşluksuz görünür karakterler
İstemci sırrı (client secret)EvetEn az 8, en fazla 1024 karakter; yalnızca yazılır
Yönlendirme tabanıCanlıda evethttps ile başlar; sorgu, parça ya da kullanıcı bilgisi içermez
İşlem nedeniEvet8-500 karakter
Doğrulama koduEvet6 haneli güncel kod
Onay metni (kaldırma)EvetSağlayıcı kimliği aynen
Çalışma durumu etkin (sağlayıcı ayarları)HayırSağlayıcıyı açar ya da kapatır
Güvenli ek bilgiler (JSON)HayırSır içeren alan adları reddedilir

Sık karşılaşılan durumlar

DurumNe yapmalı
Google düğmesi oyunda görünmüyor ya da başlatma 403 dönüyorGoogle bu ortamda yapılandırılmamış ya da kapalıdır. Bilgileri girin ve sağlayıcıyı etkinleştirin.
Google “redirect_uri_mismatch” diyorGoogle Cloud konsoluna kaydettiğiniz adres, yönlendirme tabanı + /google ile birebir aynı olmalıdır.
“Bilgileri düzenle” düğmesi yokYalnızca süper yönetici istemci bilgilerini yazabilir.
“Kaydet” doğrulama kodu hatası veriyorKurtarma kodu kabul edilmez; doğrulayıcı uygulamanızdaki güncel 6 haneli kodu girin. Her denemeden sonra kod silinir.
Sunucu varsayılanı kaynak olarak görünüyorBilgiler sunucu ayarından gelir. Panelden girdiğiniz değer bu ortamda onu ezer.
Oyuncu iki cihazda aynı anda oturumda kalamıyorTek aktif oturum açıktır. Kapatırsanız oyuncular birden çok cihazda aynı anda oturumda kalabilir.

İlgili dokümanlar

Modüller

Amaç

Open Play özellikleri modüller halinde gelir: karakterler, ekonomi, mağaza, sıralama, bildirimler gibi. Bu sekme seçili ortamda hangi modüllerin açık olduğunu gösterir. Kapalı modülün ekranları panel menüsünden kalkar ve API’si 404 (module_disabled) döner; modülün verisi silinmez ve yeniden açınca geri gelir.

Ne zaman kullanılır

  • Bir ekranın ya da API’nin neden görünmediğini araştırırken.
  • Oyununuzun kullanmadığı bir özelliği kapatmak için.
  • Kapatılmış bir modülü yeniden açmak için.

Adım adım

  1. Sekmenin üstündeki ortam seçicisinden ortamı seçin.
  2. Modülün kartında durumu ( Açık ya da Kapalı ), neye bağlı olduğunu ve varsa engelleyen nedeni görün.
  3. Yetkiniz varsa Aç ya da Kapat düğmesine basın.
  4. Açılan pencerede nedeninizi yazın. Kapatırken ayrıca bir onay sorulur.
  5. Değişiklik yaklaşık iki saniyede yayılır.

Modüller şunlardır: Karakterler ve kurallar, Ekonomi, Eşya ve envanter, Tüccar ve takas, Mağaza ve satın alma, Görevler ve ödüller, Parti ve klan, Sohbet, Sıralama ve turnuva, Bildirimler, Webhook’lar, Sahne ve varlıklar, Eklenti platformu.

Uyarı

Kapalı modüle yapılan API çağrıları 404 ve {"code":"module_disabled"} döner. Oyununuz bir modüle bağlıysa kapatmadan önce oyunun bunu kullanmadığından emin olun.

Alanlar ve seçenekler

AlanZorunluAçıklama
İşlem nedeniEvet8-500 karakter

Sık karşılaşılan durumlar

DurumNe yapmalı
Aç ve Kapat düğmeleri yokModülleri yalnızca süper yönetici açıp kapatabilir; ekran sizin için salt okunurdur.
Düğme kapalı ve “Önce şu modülleri açın” yazıyorModül başka modüllere bağlıdır; önce yazılan modülleri açın.
Düğme kapalı ve “Önce şu modülleri kapatın” yazıyorBaşka modüller bu modüle bağlıdır; önce onları kapatın.
“Etkin eklentiler kullanıyor” yazıyorBir eklenti bu modüle ihtiyaç duyar. Eklentiler sayfasından o eklentileri kapatın, sonra modülü kapatın.
Kapattığım modülün verileri gitti mi?Hayır. Veriler korunur; yalnızca ekranlar ve API durur.

İlgili dokümanlar

API anahtarları

Amaç

API anahtarları, oyununuzun sunucusunun Open Play’e kimliğini kanıtlamasını sağlar. Bu sayfa seçili oyunun ve ortamın anahtarlarını listeler; yeni anahtar oluşturmanızı, süresini ve durumunu yönetmenizi sağlar. Sayfa, Oyun ayarları içindeki API anahtarları sekmesidir.

Ne zaman kullanılır

  • Oyun sunucunuzu Open Play’e ilk kez bağlarken.
  • Bir anahtar sızdıysa ya da artık gerekmiyorsa iptal etmek için.
  • Anahtarın süresini uzatmak ya da adını değiştirmek için.

Adım adım

Yeni anahtar oluşturma

  1. Üstteki ortam seçicisinden anahtarın çalışacağı oyunu ve ortamı seçin.
  2. Anahtar oluştur düğmesine basın. Düğmeyi yalnızca anahtar yazma yetkisi olan yöneticiler görür.
  3. Anahtara bir ad verin ve Anahtar türü seçin: oyun sunucunuz için Sunucu anahtarı, oyun istemcisi için İstemci anahtarı.
  4. Geçerlilik bölümünden bir süre seçin. Emin değilseniz 90 gün gibi bir süre seçip düzenli yenilemek daha güvenlidir.
  5. Not alanına kısa bir gerekçe yazın.
  6. Anahtarı oluştur düğmesine basın.
  7. Sayfada görünen gizli değeri hemen kopyalayıp güvenli bir yere kaydedin.

Uyarı

Gizli değer yalnızca oluşturulurken bir kez gösterilir; sonradan görüntülenemez. Kaybederseniz anahtarı yenilemeniz (rotate) gerekir.

Anahtarı kullanma

Oyun sunucunuz anahtarı her istekte Authorization başlığıyla gönderir:

curl https://<sunucu-adresiniz>/v1/<uç-nokta> \
  -H "Authorization: Bearer crx_live_<önek>_<gizli>"

Yer tutucuları kendi değerlerinizle değiştirin. Gerçek anahtarı kaynak koduna ya da oyun istemcisine yazmayın.

Anahtarı düzenleme, yenileme ve iptal etme

  1. Listeden anahtarın satırına tıklayarak ayrıntı sayfasını açın.
  2. Düzenle ile adı, süreyi ve notları değiştirin; gizli değer değişmez.
  3. Rotate (yenile) eski gizli değeri iptal eder ve yeni değeri bir kez gösterir.
  4. Devre dışı bırak anahtarı geçici durdurur, Etkinleştir geri açar; Revoke (iptal) kalıcıdır.

Alanlar ve seçenekler

AlanZorunluAçıklama
Anahtar adıEvetListede tanımak için ad; en az 2 karakter
Anahtar türüEvetSunucu (gizli) ya da istemci (oyuna gömülebilir)
GeçerlilikEvetSüresiz, 30 gün, 90 gün, 1 yıl ya da özel tarih; düzenlemede ayrıca “Değiştirme”
NotEvetİşlem geçmişine yazılan gerekçe, 8-500 karakter
Anahtarın sahibiHayırVarsayılan “Bu ortam”; “Gelişmiş ayarlar” içindedir
Sahip oyuncu / organizasyonSahip türüne bağlıSahibi seçtiğinizde zorunludur
Kapsam etiketleriHayırYalnızca not amaçlı etiketler
İzinlerHayırAd-değer notları; erişimi kısıtlamaz
Ek bilgilerHayırAd-değer notları; erişimi kısıtlamaz

Not

Kapsam etiketleri, izinler ve ek bilgiler şimdilik yalnızca kayıt amaçlıdır. Anahtarın neye erişebileceğini anahtar türü (sunucu ya da istemci) belirler.

Sık karşılaşılan durumlar

DurumNe yapmalı
Gizli değeri kaybettimAnahtarı Rotate ile yenileyin; yeni değeri kaydedin ve sunucunuzda güncelleyin.
Anahtar bir gün çalışmayı bıraktıSüresi dolmuş, devre dışı bırakılmış ya da iptal edilmiş olabilir; ayrıntı sayfasından durumunu ve bitiş tarihini kontrol edin.
“Anahtar oluştur” düğmesi yokRolünüzde anahtar yazma yetkisi yoktur; bir yöneticiden isteyin.
Sunucu anahtarı istemciye gömüldüAnahtarı hemen iptal edin, yenisini oluşturun ve yalnızca sunucuda kullanın.

İlgili dokümanlar

Webhook ve olay kuyruğu

Amaç

Webhook, Open Play’in bir olay oluştuğunda kendi sunucunuza gönderdiği imzalı HTTP isteğidir. Bu sayfa kayıtlı uç noktaları, her olayın uç noktaya gönderim denemelerini ve gönderilmeyi bekleyen olay kuyruğunu (outbox) gösterir. Sayfa salt okunurdur: uç nokta eklemek, silmek, başarısız teslimatı yeniden göndermek ya da sırrı görmek için panelde düğme yoktur. Uç noktalar REST ile kaydedilir; ayrıntı için Webhook’lar rehberine bakın.

Ne zaman kullanılır

  • Sunucunuzun webhook’ları alıp almadığını doğrulamak için.
  • Bir olayın neden gelmediğini (bekliyor mu, başarısız mı, hangi hata) anlamak için.
  • Bir uç noktanın etkin olup olmadığını ve hangi olay türlerini dinlediğini görmek için.

Not

Webhook modülü kapalıysa webhook uçları yanıt vermez (404, module_disabled). Modülü Oyun ayarları › Modüller sayfasından açın. Sayfayı görmek için rolünüzde webhook okuma yetkisi de gerekir.

Adım adım

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun.
  2. “Webhook endpoints” tablosunda uç noktanızın adresini (URL), dinlediği olay türlerini ve durumunu kontrol edin. Süzgeç boşsa “tümü” yazar, yani uç nokta bütün olayları alır.
  3. “Delivery attempts / DLQ” tablosunda teslimatları durumuna göre süzün. Başarısız (failed) olanlar yeniden deneme hakkını tüketmiş teslimatlardır (DLQ, ölü mektup kuyruğu).
  4. “Hata” sütununda hatanın sınıfını ve varsa sunucunuzun döndüğü HTTP kodunu görürsünüz. Ham hata metni gösterilmez.
  5. “Outbox” tablosunda gönderilmeyi bekleyen ya da denenen olayları görürsünüz.
  6. Yeni verileri görmek için Yenile düğmesine basın.

Alanlar ve seçenekler

Bu sayfada doldurulacak bir form yoktur.

Tablolar

TabloSütunlar
Webhook endpointsURL, Event filtreleri, Durum (enabled ya da disabled), Oluşturulma; Endpoint kimliği sütun menüsünden açılır
Delivery attempts / DLQEndpoint (URL, teslimat ve olay kimliği), Durum (pending, delivered, failed), Attempt (deneme sayısı), Hata, Oluşturma, Teslim; teslimat sırası ve outbox kimliği sütun menüsünden açılır
OutboxEvent (tür ve olay kimliği), Durum, Attempt, Retry (sonraki deneme zamanı), Oluşturma

Yayımlanmış (tamamlanmış) outbox kaydında sonraki deneme zamanı “—” görünür. Olay türü gizli bir iç olaysa “redacted/private” yazar. Panel sırları, ham gövdeyi ve ham hata metnini hiçbir zaman göstermez.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Webhook yüzeyi yetki gerektiriyor” yazıyorRolünüzün webhook okuma yetkisi yok; bir yöneticiden isteyin.
Tablolar boşHenüz uç nokta kaydetmemiş olabilirsiniz; doğru ortamı seçtiğinizi de kontrol edin.
Teslimat “failed”Sunucunuz 2xx dönmüyor ya da ulaşılamıyor. Hata sütunundaki sınıfı ve kodu inceleyin; alıcınızı düzeltin. Panelden yeniden gönderme yoktur.
Aynı olay birden çok kez geldiTeslimat en az bir kez yapılır; alıcınız olay kimliğiyle tekrarı elemelidir. Ayrıntı için rehbere bakın.
Uç noktanın sırrını göremiyorumSır yalnızca kayıt yanıtında bir kez döner; sonra hiçbir yerde gösterilmez. Yenilemek için uç noktayı silip yeniden kaydedin.

İlgili dokümanlar

Arka plan işleri

Amaç

Bu sayfa iki şeyi salt okunur gösterir: (1) eklentilerin zamanlanmış komutlarının arka plan iş kayıtlarını ve (2) seçtiğiniz bir oyuncunun bağlı giriş hesaplarıyla cihaz bilgilerini. Sayfadan iş başlatamaz, yeniden deneyemez, oturum kapatamaz ya da parola değiştiremezsiniz. Bu işlemler için Oyuncu profili sayfasını kullanın.

Ne zaman kullanılır

  • Bir eklentinin zamanlanmış işinin çalışıp çalışmadığını, başarısız olup olmadığını görmek için.
  • Bir oyuncunun hangi giriş yöntemleriyle (Google, parola vb.) hesaba bağlandığını görmek için.
  • Bir oyuncunun hangi cihazlardan giriş yaptığını kontrol etmek için.

Adım adım

İş kayıtları

  1. Doğru oyunu ve ortamı seçtiğinizden emin olun.
  2. “Worker job runs” tablosunda işleri durumlarına göre süzün: pending (henüz alınmadı ya da yeniden deneme için serbest), claimed (bir işçi aldı), done (tamamlandı), dead (kalıcı hata ya da deneme sınırı nedeniyle kapandı).
  3. Belirli bir zaman aralığı için filtre menüsünde “Oluşturulma” sütununu “arasında” ile kullanın.
  4. “Failure” sütunu başarısız işin hata kodunu gösterir. İş içeriği ve ham hata metni gösterilmez.

Oyuncu giriş ilişkileri

  1. “Player auth relations” kartında Oyuncu alanına oyuncuyu yazıp seçin.
  2. İlişkileri getir düğmesine basın.
  3. “Provider accounts” tablosu oyuncunun bağlı giriş hesaplarını, “Devices” tablosu cihaz bilgisini ve durumunu gösterir. İptal edilmiş kayıtlar da listelenir. Uzun listeler sayfalanır.

Not

İş çalıştırıcısının sağlık durumu bu sayfada gösterilmez: kalıcı bir sağlık sinyali kaynağı henüz yoktur, bu yüzden “çevrimiçi” ya da gecikme gibi değerler üretilmez. Sayfadaki “Worker health: gözlemlenemiyor” kartı bunu belirtir.

Alanlar ve seçenekler

AlanZorunluAçıklama
OyuncuEvet (İlişkileri getir için)İlişkileri görülecek oyuncu

İş kayıtları tablosu

Plugin / mesaj (eklenti kimliği, mesaj ve zamanlama kimliği), Durum, Attempt (deneme sayısı), Deadline (son zaman), Failure (hata kodu) ve Oluşturulma varsayılan olarak görünür. Sonraki deneme, Bitiş ve Zamanlama kimliği sütun menüsünden açılır.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Operations yüzeyi yetki gerektiriyor” yazıyorRolünüzün iş kaydı okuma yetkisi yok; bir yöneticiden isteyin.
Oyuncu ilişkileri yüklenmiyorBu kart için oyuncu okuma yetkisi gerekir; yoksa bir not görürsünüz.
İş “dead” durumundaİş kalıcı bir hatayla ya da deneme sınırına ulaşarak kapanmıştır. Failure sütunundaki kodu eklentinin sahibiyle birlikte inceleyin; panelden yeniden başlatılamaz.
İş kayıtları boşSeçili ortamda zamanlanmış eklenti işi yoktur ya da süzgeç çok dar.
Cihazı kapatmak istiyorumBu sayfa salt okunur; oturumları Oyuncu profili sayfasından kapatın.

İlgili dokümanlar

Stüdyolar ve oyunlar

Amaç

Bu sayfa, kurulumdaki stüdyoları ve her stüdyonun oyunlarını ortamlarıyla birlikte gösterir. Buradan yeni stüdyo ve oyun kurar, adları değiştirir, kayıtları arşivler ve bir oyunun ayarlarına geçersiniz. Sayfa yalnızca stüdyo yönetme yetkisi olan yöneticilere açıktır. Stüdyo, oyun ve ortam kavramları için Temel kavramlar sayfasına bakın.

Ne zaman kullanılır

  • Yeni bir stüdyo ya da oyun kurmak için.
  • Bir stüdyonun ya da oyunun adını düzeltmek için.
  • Artık kullanılmayan bir oyunu ya da stüdyoyu listeden kaldırmak (arşivlemek) için.
  • Başka bir oyunun ayarlarına geçmek için.

Adım adım

Yeni stüdyo

  1. Yeni stüdyo düğmesine basın.
  2. Stüdyo adını ve Gerekçe alanını doldurup Stüdyo oluştur düğmesine basın.

Yeni oyun

  1. Stüdyonun kartındaki Yeni oyun düğmesine basın.
  2. Oyun adını yazın. İsterseniz kısa adı (slug) da yazın; boş bırakırsanız addan üretilir.
  3. Gerekçeyi yazıp Oyun oluştur düğmesine basın. Oyun, geliştirme ve canlı ortamlarıyla birlikte kurulur.

Yeniden adlandırma ve arşivleme

  1. Stüdyo ya da oyun satırındaki üç noktalı menüden Yeniden adlandır ya da Arşivle seçin.
  2. Yeni adı ya da yalnızca gerekçeyi yazıp onaylayın.
  3. Arşivlenmiş kaydı aynı menüden Geri aç ile yeniden listelere döndürebilirsiniz.

Not

Arşivleme yalnızca gizler; veri silinmez. Seçili ortamı içeren oyun ya da stüdyo arşivlenemez; önce başka bir ortam seçin. Canlı yetkisi ya da etkin API anahtarı olan kayıtlar için sunucu arşivlemeyi reddeder.

Oyun ayarlarını açma

  1. Oyunun satırındaki Oyun ayarları düğmesine basın. Oyunun etkin bir ortamı yoksa düğme pasiftir.
  2. Başka bir oyunun ortamı seçiliyse panel o oyunun etkin ortamına geçer ve Oyun ayarları sayfasını açar.

Alanlar ve seçenekler

AlanPencereZorunluAçıklama
Stüdyo adıYeni stüdyoEvet1-160 karakter
StüdyoYeni oyunEvetOyunun ait olacağı stüdyo (stüdyo kartından açtıysanız seçilmiş gelir)
Oyun adıYeni oyunEvet1-160 karakter
Kısa adYeni oyunHayırKüçük harf, rakam, tire; en çok 48 karakter
Yeni adYeniden adlandırEvet1-160 karakter
GerekçeHepsiEvet8-500 karakter

Sık karşılaşılan durumlar

DurumNe yapmalı
Sayfayı göremiyorumStüdyo yönetme yetkisi yalnızca en geniş yetkili rolde bulunur; bir süper yöneticiden isteyin.
“Arşivle” pasif, “Seçili ortam” notu varŞu an çalıştığınız ortam bu kaydın içinde; başka bir oyunun ortamına geçip tekrar deneyin.
“Bu oyunun etkin ortamı yok” yazıyorOyunun etkin (arşivlenmemiş) ortamı kalmamıştır; önce ortamı geri açın.
Kısa ad hatasıKüçük harf, rakam ve tire kullanın; tire başta, sonda ya da art arda olamaz.
Arşivleme reddedildiOyunun canlı yetkisi ya da etkin API anahtarı olabilir; önce onları kapatın.

İlgili dokümanlar

Panel yöneticileri

Amaç

Bu sayfa, admin paneline giren yönetici hesaplarını listeler ve yönetir. Yönetici, oyun oyuncusu değil, paneli kullanan kişidir; oyuncu hesapları için Oyuncular sayfasına bakın. Her yöneticinin bir rolü ve erişebildiği ortamların listesi vardır. Sayfa yalnızca yönetici okuma yetkisi olan hesaplara açıktır.

Ne zaman kullanılır

  • Ekibe yeni bir yönetici eklemek için.
  • Bir yöneticinin rolünü ya da ortam erişimini değiştirmek için.
  • Ayrılan bir yöneticinin hesabını kapatmak ve oturumlarını sonlandırmak için.
  • Telefonunu kaybeden yöneticinin iki adımlı doğrulamasını sıfırlamak için.

Adım adım

Yönetici oluşturma

  1. Operatör oluştur düğmesine basın. Düğmeyi yalnızca yönetici yazma yetkisi olan hesaplar görür.
  2. E-posta ya da kullanıcı adından en az birini, görünen adı, rolü ve geçici parolayı girin.
  3. Erişebileceği ortamları seçin. Varsayılan olarak şu an seçili ortam gelir.
  4. İşlem nedeni yazıp kaydedin. Yöneticinin sayfası açılır.

Rol ve ortam değiştirme

  1. Listeden yöneticiyi açın ve Düzenle düğmesine basın.
  2. Görünen adı, rolü ve ortamları değiştirin; nedeni yazıp kaydedin. E-posta, kullanıcı adı ve parola bu formdan değiştirilemez.

Hesabı kapatma, oturum ve 2FA

  1. Yöneticinin sayfasındaki “Güvenlik eylemleri” kartını kullanın.
  2. Devre dışı bırak yeni girişleri reddeder; açık oturumlar ayrıca Oturumları revoke et ile kapatılmalıdır. Etkinleştir hesabı yeniden açar.
  3. 2FA’yı sıfırla yöneticinin authenticator’ını ve kurtarma kodlarını siler, bütün oturumlarını kapatır. Yönetici yalnızca parolasıyla girer ve iki adımlı doğrulamayı yeniden kurmalıdır. Onay için kendi parolanızı yazarsınız.

Uyarı

Kendi hesabınızı devre dışı bırakamaz, oturumlarınızı kapatamaz ya da kendi 2FA’nızı sıfırlayamazsınız; bu güvenlik için kapalıdır.

Alanlar ve seçenekler

Roller

RolYapabilecekleri
super_adminBütün ortamlara ve bütün yönetim işlemlerine erişir; yönetici hesaplarını, stüdyoları, eklentileri ve güvenlik şalterlerini yalnızca bu rol yönetir
game_managerAtanmış ortamlarda oyun ve oyuncu kaynaklarını yönetir; yönetici hesapları, stüdyolar, eklenti yönetimi, güvenlik şalterleri, giriş sağlayıcı kimlik bilgileri ve işlem geçmişindeki yönetici ayrıntıları dışında çoğu işlemi yapar
supportAtanmış ortamlarda destek yapar: oyuncuları, oturumları ve sohbeti okur; oyuncu bilgisini günceller, yasaklar, oturum kapatır, sohbette susturur
read_onlyAtanmış ortamlarda yalnızca okur

Oluşturma ve düzenleme formu

AlanZorunluAçıklama
E-postaHayır**E-posta ya da kullanıcı adından en az biri; yalnız oluştururken
Kullanıcı adıHayır*3-32 karakter; yalnız oluştururken
Görünen adEvetPanelde görünen ad
RolEvetDört rolden biri
Geçici parolaEvet (oluştururken)En az 12 karakter
OrtamlarEvetErişilecek ortamlar
İşlem nedeniEvet8-500 karakter

Listedeki sütunlar

Operatör, Rol, Durum (Etkin ya da Devre dışı), 2FA (Açık ya da Kapalı), Scope (erişilebilen ortam sayısı), Son giriş ve Oluşturulma varsayılan olarak görünür. E-posta, Kullanıcı adı ve Güncellenme sütunlarını sütun menüsünden açabilirsiniz.

Sık karşılaşılan durumlar

DurumNe yapmalı
Sayfayı göremiyorumYönetici hesaplarını okuma yetkisi yalnızca en geniş yetkili roldedir; bir süper yöneticiden isteyin.
Ortam listesinde bir ortam yokListede yalnızca sizin de yetkili olduğunuz ortamlar çıkar; önce kendi erişiminizi genişletin.
“E-posta ya da kullanıcı adı gerekli” hatasıİkisini birden boş bırakamazsınız.
“Parola en az 12 karakter olmalı” hatasıGeçici parolayı 12 karakterden uzun yazın.
“2FA’yı sıfırla” düğmesi yokYöneticinin iki adımlı doğrulaması kapalı ya da kendi hesabınıza bakıyorsunuz.
Devre dışı bıraktığım yönetici hâlâ paneldeDevre dışı bırakma yeni girişleri engeller; açık oturumu Oturumları revoke et ile kapatın.

İlgili dokümanlar

Oyuncu profili

Amaç

Bu sayfa tek bir oyuncunun hesabını gösterir: durum, hesap türü, son giriş, açık oturum sayısı, toplam oyun süresi ve ana para bakiyesi. Altındaki sekmelerde oyuncunun karakterlerini, cüzdanını, oturumlarını, giriş geçmişini, yasaklarını, bağlı hesaplarını ve işlem geçmişini görürsünüz. Sayfaya Oyuncular listesinden gelirsiniz.

Ne zaman kullanılır

  • Bir oyuncunun yardım isteğini incelerken hesabının durumuna bakmak için.
  • Oyuncunun adını, e-postasını ya da ek bilgilerini düzeltmek için.
  • Parolayı sıfırlamak, misafiri kalıcı hesaba çevirmek için.
  • Kural ihlali yapan oyuncuyu banlamak, banı kaldırmak ya da hesabı silmek için.
  • Bir oyuncunun açık oturumlarını kapatmak ya da bağlı bir giriş yöntemini kaldırmak için.

Adım adım

İnceleme

  1. Sekmelerden birini seçin: Genel, Karakterler, Cüzdan ve envanter, Oyun süresi, Oturumlar, Giriş geçmişi, Yasaklar, Hesaplar, Geçmiş. Rolünüzün göremediği ya da modülü kapalı sekmeler görünmez ya da “yetki gerekir” notu gösterir.
  2. Genel sekmesinde profil bilgilerini, son güvenlik olaylarını ve ek bilgileri (metadata) görürsünüz.

Profili düzenleme

  1. Profili düzenle düğmesine basın. Düğmeyi yalnızca düzenleme yetkisi olan yöneticiler görür.
  2. Görünen adı, e-postayı, e-posta doğrulama durumunu, görseli ve ek bilgileri değiştirin. Yalnızca değiştirdiğiniz alanlar kaydedilir.
  3. Sayfanın altındaki İşlem nedeni alanını doldurup kaydedin. Kaydetmeden başka sayfaya geçmeye çalışırsanız panel sizi uyarır.

Parola yönetimi

  1. Kayıtlı hesapta Diğer işlemler menüsünden Parola yönet ile yeni parola verin (en az 12 karakter) ya da Rastgele parola üret ile sistemin parola üretmesini isteyin.
  2. Misafir hesapta Hesaba çevir işlemi misafire e-posta ve tek seferlik rastgele parola verir.
  3. Üretilen parola yalnızca bir kez gösterilir ve yeniden getirilemez. Oyuncuya güvenli bir kanaldan iletin; Gizle düğmesi, sekme ya da sayfa değişimi parolayı bellekten siler. Parola üretme ve dönüştürme açık oturumları kapatır.

Banlama ve ban kaldırma

  1. Diğer işlemler menüsünden Banla seçin (zaten yasaklıysa Yasağı düzenle ve Banı kaldır görünür).
  2. Süreyi seçin, isteğe bağlı yasak gerekçesini ve işlem nedenini yazın, Banla düğmesine basın.
  3. Oyuncunun bütün oturumları kapanır ve yeniden giriş yapamaz. Banı kaldırınca oyuncu yeniden giriş yapabilir ama kapatılan oturumlar geri gelmez.

Oturum, hesap bağı ve silme

  1. Oturumlar sekmesinden tek bir oturumu iptal edebilir, Diğer işlemler menüsünden Tüm oturumları kapat diyebilirsiniz.
  2. Hesaplar sekmesinden bir giriş yöntemi bağını kaldırabilirsiniz. Son giriş yöntemini kaldırırsanız oyuncu giremez; önce diğer yöntemleri kontrol edin.
  3. Oyuncuyu sil işlemi hesabı silinmiş olarak işaretler ve erişime kapatır; kayıt tamamen yok edilmez. Panelde silmeyi geri alan bir düğme yoktur. Onay için oyuncunun adını (yoksa e-postasını) yazarsınız.

Uyarı

Tüm bu işlemler değiştirilemeyen işlem geçmişine kaydedilir. Silme ve oturum kapatma işlemleri oyuncuyu hemen etkiler.

Alanlar ve seçenekler

Profil düzenleme formu

AlanZorunluAçıklama
Görünen adEvetOyuncunun görünen adı
E-postaHayırBoşsa e-posta kaldırılır; ayrı yetki gerekir
E-posta doğrulandıHayırAdresin doğrulanmış sayılması; ayrı yetki gerekir
Görsel URLHayırOyuncu görselinin adresi
Metadata JSONHayırJSON nesnesi
İşlem nedeniEvet8-500 karakter

Yasak penceresi

AlanZorunluAçıklama
SüreEvet1, 3, 7, 30 gün, süresiz ya da özel tarih ve saat (en çok 365 gün)
Yasak gerekçesiHayırEn çok 200 karakter; yalnız yönetici ekranlarında görünür
İşlem nedeniEvet8-500 karakter

Parola pencereleri

AlanPencereAçıklama
Yeni parolaParola yönetEn az 12 karakter
E-postaHesaba çevirHesabın giriş e-postası
İşlem nedeniHepsiParola ve dönüştürme pencerelerinde en çok 200 karakter

Sık karşılaşılan durumlar

DurumNe yapmalı
E-posta alanı pasifRolünüzde e-posta ve doğrulama değiştirme yetkisi yoktur; bir yöneticiden isteyin. Görünen adı yine de değiştirebilirsiniz.
“Profili düzenle”, “Banla” ya da “Parola yönet” yokİlgili yetki rolünüzde yok. Düğmeler yalnızca yetkili yöneticilere görünür.
Üretilen parolayı kaybettimParola yeniden gösterilmez; işlemi yeniden başlatıp yeni parola üretin.
“Parola daha önce gösterildi” notu çıktıAynı işlem zaten tamamlanmıştı; yeni bir parola için işlemi yeniden başlatın.
Banladığım oyuncunun oturumu görünüyorBan bütün oturumları kapatır; Oturumlar sekmesini yenileyip durumlarına bakın.
Metadata hatasıGeçerli bir JSON nesnesi yazın; dizi ya da düz metin kabul edilmez.

İlgili dokümanlar

Karakter profili

Amaç

Bu sayfa tek bir karakterin ayrıntısını gösterir: seviye, XP, kayıt sürümü, hangi oyuncuya ait olduğu, özellikleri, cüzdanı ve olay geçmişi. Üstte en çok altı temel bilgi, altında bölüm sekmeleri bulunur. Sayfaya Oyuncu karakterleri listesinden satıra tıklayarak gelirsiniz.

Ne zaman kullanılır

  • Bir oyuncunun karakter durumuyla ilgili şikâyeti incelemek için.
  • Bir değişikliğin nasıl ve ne zaman olduğunu olay geçmişinden görmek için.
  • Hatalı bir değişikliği düzeltmek için komut çalıştırmak ya da önceki sürüme dönmek için.

Adım adım

İncelemek

  1. Sekmelerden birini seçin: Genel, İstatistikler, Cüzdan ve envanter, Geçmiş, Gelişmiş. Yetkiniz olmayan ya da modülü kapalı bölüm görünmez ya da “yetki gerekir” notu gösterir.
  2. Oyuncuya geçmek için temel bilgilerdeki Oyuncu profiline git bağlantısını kullanın.

Komut çalıştırmak

  1. Gelişmiş sekmesinde Komut çalıştır düğmesine basın. Düğmeyi yalnızca komut yetkisi olan yöneticiler görür.
  2. Komut listesinden komutu seçin; seçime göre ilgili alanlar görünür.
  3. Değerleri girin, İşlem nedeni yazın ve komutu uygulayın.
  4. Komut karakterin o anki sürümü üzerinde çalışır. Siz formu doldururken karakter başka bir yerden değiştiyse işlem sürüm çakışmasıyla reddedilir; profili yenileyip tekrar deneyin.

Önceki sürüme dönmek

  1. Gelişmiş sekmesinde Geri al düğmesine basın. Düğmeyi yalnızca geri alma yetkisi olan yöneticiler görür.
  2. Hedef sürüm alanına dönmek istediğiniz sürüm numarasını yazın (en az 1) ve nedeni ekleyin.

Uyarı

Önceki sürüme dönmek geçmişi silmez. Karakteri o sürümdeki duruma getiren yeni bir olay yazılır; eski olaylar yerinde kalır.

Geçmişteki hâlini görmek

  1. Gelişmiş sekmesinde “Geçmişteki hâli” bölümüne zamanı yazın ve Hâlini göster düğmesine basın. Karakterin o andaki durumu olay geçmişinden yeniden hesaplanır.

Alanlar ve seçenekler

Komut penceresi

AlanHangi komutlardaAçıklama
KomutHepsiBayrak ayarla, Özellik değeri ata, Kaynak ekle ya da çıkar, Değiştirici ekle, Değiştirici kaldır, Deneyim puanı (XP) ekle, Önceki sürüme dön
Bayrak anahtarı, Bayrak değeriBayrak ayarlaBayrağın adı ve açık/kapalı durumu
AnahtarÖzellik değeri ata, Kaynak ekle ya da çıkarÖzelliğin ya da kaynağın anahtarı
Değer / Miktar / XP miktarı / Hedef sürümBayrak dışındaki komutlar (Değiştirici kaldır hariç)Tam sayı
Etkilenen istatistik, Değiştirici türüDeğiştirici ekleHangi istatistik, nasıl etkilenecek
Kaynak türü, Kaynak kimliğiDeğiştirici ekle ve kaldırDeğiştiricinin kaynağı
İşlem nedeniHepsiEn az 8 karakter

Geçmişteki hâli

AlanZorunluAçıklama
Tarih ve saatEvetRFC 3339 zamanı; gelecek ya da geçersiz zaman reddedilir

Sık karşılaşılan durumlar

DurumNe yapmalı
“Komut çalıştır” düğmesi pasifKarakter bilgisi yüklenememiştir; profili yenileyin.
Komut sürüm çakışmasıyla reddedildiKarakter başka bir yerden değişmiştir; profili yenileyip komutu tekrar çalıştırın.
“tanımsız attribute” ya da “tanımsız resource” hatasıAnahtar karakterin tanımında yoktur; tanımdaki yazımla birebir aynı olmalı. Hesaplanan alanlar da elle atanamaz.
Değer “min altında” ya da “max üstünde” hatasıTanımdaki alt ya da üst sınırı aşıyorsunuz; değeri sınırlar içinde seçin. Kaynak eklerken miktar 0 olamaz.
Bir sekme görünmüyorRolünüzün o bölümü görme yetkisi yok ya da ilgili modül kapalı.
Geçmiş boş“Bu karakter için olay kaydı yok.” yazar; karakter henüz komut almamıştır.

İlgili dokümanlar

Model

Amaç

Bu sayfa tek bir 3D modelin ayrıntısıdır: önizleme, dosyadan hesaplanan değerler, sürümler, modeli kullanan tanımlar ve yayın geçmişi. Modelin hangi sürümünün oyuna gideceğini buradan seçersiniz.

Ne zaman kullanılır

  • Dönüştürülen modeli oyuna yayınlamak için.
  • Yanlış çıkan bir yayını önceki sürüme geri almak için.
  • Modele animasyon klipleri eklemek için.
  • Modeli arşivlemek, yeniden adlandırmak ya da kalıcı olarak silmek için.

Adım adım

Sürüm yayınlama

  1. Sürümler tablosunda durumu Hazır olan sürümün Yayınla düğmesine basın.
  2. Yayın, yayındaki sürümün klip ya da boyutlarında büyük bir değişiklik getirmiyorsa hemen yapılır.
  3. Sürüm yayındaki bir klibi kaldırıyorsa ya da sınır kutusunu belirgin biçimde değiştiriyorsa bir onay penceresi açılır. Pencere kaldırılan ve eklenen klipleri ve boyut farkını gösterir. Farkı gördüm, yine de yayınla kutusunu işaretleyin, bir neden yazın ve onaylayın.

Geri alma

Yayındakinden eski bir sürümde düğme Geri al olarak görünür ve her zaman onay penceresi açar. Geri alma yalnızca hâlâ saklanan sürümlere yapılabilir.

Uyarı

Yayından kalkan sürümler, varsayılan ayarda 14 gün sonra silinir; saklama süresi dolmuş sürüme geri dönülemez. Tabloda her sürümün saklama tarihi görünür.

Klip ekleme

  1. Klip ekle düğmesine basın. Düğme, iskeletli bir fbx ya da blend kaynaklı hazır sürüm yoksa kapalıdır ve nedeni yanında yazar.
  2. Taban sürümü seçin, klip dosyasını ya da zip paketi seçin ve Yükle’ye basın.
  3. Sunucu klipleri taban sürümün iskeletine bağlayıp yeni bir sürüm üretir; yeni sürümü sonra yayınlayabilirsiniz.

Yeniden adlandırma, arşivleme, silme

Üstteki düğmelerle yapılır; her biri bir neden ister.

  • Yeniden adlandır: Yeni ad kütüphanede görünür; mevcut başvurular etkilenmez.
  • Arşivle: Model yeni seçimlere kapanır; onu kullanan tanımlar çalışmaya devam eder. Arşivden çıkar ile geri açılır.
  • Kalıcı sil: Yalnızca arşivlenmiş modelde ve hiçbir tanım kullanmıyorsa yapılır. Geri alınamaz; modelin bütün sürümleri silinir.

Alanlar ve seçenekler

AlanNeredeAçıklama
Önizlenen sürümÖnizlemeHangi sürümün gösterileceği
Taban sürümKlip ekleKlipleri alacak sürüm
Klip dosyası ya da zipKlip eklefbx ya da zip
Yeni adYeniden adlandırEn çok 200 karakter
Farkı gördüm, yine de yayınlaYayın onayıZorunlu onay kutusu
İşlem nedeniYeniden adlandır, arşiv, silme, onaylı yayın8-500 karakter

Sayfadaki bölümler

  • Önizleme: 3D görüntüleyici; seçilen sürümün kliplerini oynatabilirsiniz.
  • Olgular: Üçgen, köşe, kemik, malzeme, doku sayıları, boyut, glb dosya boyu ve klip listesi. Sunucu bunları glb dosyasından kendisi hesaplar.
  • Uyarılar: Dönüştürme sırasındaki ufak sorunlar (örneğin eksik doku ya da atlanan klip). Yayını engellemez.
  • Sürümler: Her sürümün kaynağı, durumu, kaynak dosyaları (indirilebilir), saklama süresi ve yayın düğmesi.
  • Kullanan tanımlar: Bu modeli kullanan eşya, karakter ve içerik kaydı tanımları.
  • Yayın geçmişi: Hangi sürümün ne zaman, hangi nedenle yayınlandığı.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Kalıcı sil” düğmesi kapalı, “bu modeli N tanım kullanıyor” yazıyorÖnce modeli kullanan tanımlardan çıkarın; sonra silebilirsiniz.
Yayınlarken “siz bakarken başka bir yayın yapıldı” notu çıktıFark yenilendi; yeni farkı inceleyip yeniden onaylayın.
“Klip ekle” kapalıKlip yalnızca iskeletli, hazır fbx ya da blend kaynaklı sürümlere eklenebilir.
Eski sürüme geri dönemiyorumSürümün saklama süresi dolmuştur; modeli yeniden yükleyin.
Önizleme açılmıyorÖnizleme için hazır ve saklanan bir sürüm gerekir; dönüştürme bitmemiş ya da başarısız olabilir.

İlgili dokümanlar

İçerik grubu düzenle

Amaç

Bu ekran bir içerik grubunu iki adımda kurar: önce adı, simgesi ve açıklaması; sonra grubun alanları. Alanlar, bu gruptaki her kaydın dolduracağı değerlerdir (azami hız, renk, 3D model gibi). Aynı ekran var olan bir grubun şemasını değiştirmek için de kullanılır; o durumda başlığı “Grubu düzenle” olur.

Ne zaman kullanılır

  • Yeni bir içerik grubu kurarken.
  • Mevcut gruba alan eklemek, alan sırasını değiştirmek ya da bir alanın sınırlarını güncellemek için.

Adım adım

Yeni grup kurma

  1. İçerik sayfasında Yeni grup’a basın; isterseniz bir şablon seçin.
  2. 1 / 2 adımında grup adını yazın. Yeni grupta anahtar addan önerilir; istediğiniz gibi değiştirebilirsiniz, kaydettikten sonra değişmez.
  3. Devam: alanlar ile ikinci adıma geçin.
  4. Alan ekle ile her alan için ad, tür ve gerekirse sınırları girin. Alanları yukarı ve aşağı oklarıyla sıralayın.
  5. Sayfadaki canlı önizlemeyle kayıt formunun nasıl görüneceğini kontrol edin ve Grubu oluştur deyin.

Var olan grubun şemasını değiştirme

  1. Grubun kayıt listesinde Şemayı düzenle’ye basın.
  2. Kaydedilmiş alanların anahtarı ve türü kilitlidir. Farklı bir tür gerekiyorsa Yeni alan olarak ekle ile yeni bir alan açın.
  3. Kaydet’e basın. Şema değişikliği de, kayıtlar gibi taslaktır; oyuna yayınlayınca gider.

Not

Kaydedilmemiş değişiklik varken Vazgeç’e basarsanız panel çıkmadan önce onay ister.

Alanlar ve seçenekler

Grup bilgileri

AlanZorunluAçıklama
Grup adıEvetPanelde görünen ad
AnahtarEvetOyunun grubu bulduğu kimlik; kayıttan sonra sabit
SimgeHayırKartta görünen simge
Kısa açıklamaHayırEn çok 500 karakter

Alan türleri

TürNe tutarEk ayarlar
SayıTam sayıBirim, ondalık hane, en az, en çok, varsayılan
YüzdeYüzde değeriEn az, en çok, varsayılan
MetinMetinEn çok karakter, çok satırlı
Evet / HayırMantıksal değerVarsayılan
Seçenek listesiListeden seçimSeçenekler (en çok 64), birden fazla seçilebilir
Renk#rrggbb renkYok
3D modelModel kütüphanesinden bir modelYok; Sahneler modülü kapalıysa seçilemez
GörselYüklenmiş bir görselYok
Başka bir kayıtBaşka bir kayda bağlantıHedef, hedef grup
TabloTekrarlanan satırlar, örneğin seviye-hasar tablosuEn az satır, en çok satır, sütunlar (en çok 8)

Her alan için ortak ayarlar

AlanZorunluAçıklama
Alan adıEvetFormda görünen ad
AnahtarEvetOyunun okuduğu kimlik; kayıttan sonra sabit
TürEvetKaydedilmiş alanda değişmez
GörünürlükEvetOyuncu görebilir ya da yalnız oyun sunucusu
Yardım metniHayırFormda alanın altında görünür
ZorunluHayırBoş bırakılırsa kayıt kaydedilemez
Listede gösterHayırListe ve özette de gösterir

Bir grupta en çok 48 alan olur.

Sık karşılaşılan durumlar

DurumNe yapmalı
Anahtarı yanlış yazdım ve kaydettimAnahtar değişmez. Doğru anahtarla yeni bir alan ekleyin; eskisi artık gerekmiyorsa kayıtlardan kaldırın.
“3D model” türü seçilemiyorSahneler modülü bu ortamda kapalıdır; modülü açın.
Bir alanın türünü değiştiremiyorumKaydedilmiş alanın türü sabittir. Yeni alan olarak ekle ile yeni tür için ayrı bir alan açın.
Bazı alanlarda kilit simgesi varBir eklenti (örneğin Package & Ride) o alanı kullanıyor; silinemez ve türü değişmez.
Kayıtlı tabloya zorunlu metin sütunu ekleyemiyorumÖnce isteğe bağlı olarak ekleyin; mevcut satırlarda değer olmadığı için yeni zorunlu sütun reddedilir.
“Devam: alanlar” sonraki adıma geçmiyor1. adımda ad ya da anahtar eksik ya da geçersizdir; kırmızı mesajları düzeltin.

İlgili dokümanlar

İçerik kaydı

Amaç

Bu ekran, bir içerik grubundaki tek kaydı gösterir ve düzenler. Kaydın alanlarını grubun şeması belirler; yani formdaki alanlar gruptan gruba değişir. Burada yaptığınız değişiklik taslaktır: oyuna ancak grubun kayıt listesindeki Yayınla ile gider.

Ne zaman kullanılır

  • Bir kaydın değerlerini (örneğin bir aracın hızını) güncellemek için.
  • Gruba yeni kayıt eklemek için.
  • Kaydı arşivlemek ya da arşivden çıkarmak için.

Adım adım

Kaydı düzenleme

  1. İçerik sayfasından grubu, listeden de kaydı açın.
  2. Düzenle’ye basın. Düğmeyi yalnızca içerik yazma yetkisi olan yöneticiler görür.
  3. Değerleri değiştirin. Zorunlu alanlar adlarının yanında “(zorunlu)” ile işaretlidir.
  4. Sayfanın altındaki Taslağı kaydet düğmesine basın. Değişiklik taslağa yazılır; liste ve kayıtta “Taslak değişikliği var” görünür.
  5. Oyuna göndermek için kayıt listesinde Yayınla’yı kullanın.

Yeni kayıt oluşturma

  1. Grubun kayıt listesinde Yeni kayıt’a basın.
  2. Kayıt adı’nı yazın; anahtar addan önerilir, isterseniz değiştirin.
  3. Alanları doldurun ve Taslağı kaydet deyin. Kayıt taslağa eklenir.

Not

Bir eklentinin kullandığı kapalı gruplarda (örneğin Package & Ride araçları) yeni kayıt eklenemez; panel nedenini sayfada yazar.

Arşivleme

Daha fazla menüsünden Arşivle ya da Arşivden çıkar’ı seçin. Arşiv de bir taslak değişikliğidir; yayınlanınca oyuna yansır.

Alanlar ve seçenekler

Her kayıtta bulunan alanlar

AlanZorunluAçıklama
Kayıt adıEvetPanelde görünen ad
AnahtarYeni kayıttaOyundaki kimlik; kayıttan sonra sabit

Grubun alanları

Geri kalan alanlar grubun şemasından gelir. Her alanın altındaki gri metin, grubu kuran kişinin yazdığı yardım metnidir. Türlere göre giriş şöyledir:

Alan türüGirişi
Sayı, YüzdeSınırlar içinde sayı; ondalık haneler grubun ayarına göredir
MetinSerbest metin; üst karakter sınırı grubun ayarındadır
Evet / HayırOnay kutusu
Seçenek listesiTek ya da çoklu seçim
RenkRenk seçici
3D modelModel kütüphanesinden seçim; kaydın üstünde önizleme görünür
GörselGörsel yükleme
Başka bir kayıtHedef gruptan kayıt seçimi
TabloSatır ekleme, silme ve taşıma; kayıt başına tablo verisi toplamda 128 KiB’ı aşamaz

“Yalnız oyun sunucusu” olarak işaretli alanlar oyuncuya gitmez; panelde rozetle görünür.

Bölümler

  • Özellikler: Değerler.
  • Bağlantılar: Bu kaydın bağlandığı kayıtlar ve bu kayda bağlanan kayıtlar.
  • Kullanan: Bir eklenti bu grubu kullanıyorsa hangi özelliğin kullandığı.
  • Geçmiş: Kaydın revizyon sayısı, son değişiklik zamanı ve son yayınlar.

Sık karşılaşılan durumlar

DurumNe yapmalı
“N sorun var; alanların altındaki mesajlara bakın”Kırmızı mesajı olan alanları düzeltin; mesajlar değer göstermeden neyin yanlış olduğunu söyler.
Anahtar hatası alıyorumAnahtar küçük harf ya da rakamla başlamalı; yalnız küçük harf, rakam, alt çizgi ve tire içermeli.
Fiyat alanı düzenlenemiyor, “Mağazada yönetilir” yazıyorAraç kaydının mağazada ürünü varsa fiyatın tek kaynağı üründür; Ürün sayfasından değiştirin.
Sayfadan çıkarken onay soruyorKaydedilmemiş değişiklik var; Düzenlemeye dön ile kaydedin ya da Değişiklikleri at deyin.
Başkası aynı anda kaydı değiştirdiKayıt sürüm kontrolüyle yazılır; sayfayı yenileyip değişikliği yeniden yapın.

İlgili dokümanlar

Mağaza ürünü

Amaç

Bu sayfa tek bir mağaza ürününün ayrıntısını gösterir; aynı ürünü düzenlemek ya da yeni ürün oluşturmak için de kullanılır. Ürün, bir vitrinde duran ve bir eşyayı satan kayıttır. Fiyat, stok, kişi başı sınır, rozet ve satış zamanı buradan belirlenir.

Ne zaman kullanılır

  • Vitrine yeni ürün eklemek için.
  • Ürünün fiyatını, stoğunu ya da görselini değiştirmek için.
  • Ürünü yayından kaldırmak, geri almak ya da silmek için.
  • Ürünün satış geçmişine ve değişiklik geçmişine bakmak için.

Adım adım

Yeni ürün ekleme

  1. Mağaza sayfasında vitrini seçip Yeni ürün’e basın.
  2. Ne satılıyor? alanında eşyayı adıyla arayıp seçin.
  3. İsterseniz vitrindeki adı, açıklamayı ve görseli yazın.
  4. Nasıl ödenir? bölümünde oyun parası ya da gerçek parayı seçin; fiyatı ve para birimini ya da bağlanacak uygulama içi ürünü girin.
  5. Rozeti, stoğu ve kişi başı sınırı ayarlayın.
  6. Durumu (taslak ya da yayında), satış zamanını ve vitrini seçin.
  7. Bir neden yazın (panel değişiklikten bir öneri doldurur) ve Ürünü oluştur’a basın. Sağda ürünün oyunda nasıl görüneceğini gösteren bir önizleme bulunur.

Ürünü değiştirme

  1. Ürünün sayfasında Düzenle’ye basın.
  2. Alanları değiştirip Değişiklikleri kaydet’e basın.

Ürün sayfasındaki öteki düğmeler:

  • Yayından kaldır ve Yayına al: Ürünü hemen taslağa çevirir ya da yayına alır.
  • Sil: Ürünü siler; bir neden ister. Geçmiş satışlar korunur.

Not

Fiyatı, stoğu ya da zamanı değiştirmek için ürünü silmenize gerek yoktur; düzenleme mevcut ürünü günceller.

Alanlar ve seçenekler

AlanZorunluAçıklama
Ne satılıyor?EvetSatılacak eşya
Vitrindeki adıHayırEn çok 120 karakter; boşsa eşya adı
AçıklamaHayırEn çok 2000 karakter
Ürün görseliHayırPNG, JPEG ya da WebP
Nasıl ödenir?EvetOyun parası ya da gerçek para
Para birimi ve FiyatOyun parasındaFiyat 0’dan büyük
Uygulama içi ürünGerçek paradaApp Store ya da Google Play ürünü
RozetHayırYok, Yeni, İndirim, Sınırlı, Popüler
StokEvetSınırsız ya da sınırlı; sınırlıda adet
Bir oyuncu en çok kaç tane alabilir?HayırBoşsa sınırsız
DurumEvetTaslak ya da Yayında
Ne zaman satışta?EvetHemen ya da tarih aralığı
VitrinEvetOluşturduktan sonra değişmez
İşlem nedeniEvet8-500 karakter

Ürün sayfasındaki sekmeler

  • Ayrıntılar: Satış (fiyat, stok, kişi başı sınır, rozet), yayın durumu ve zamanı, “Ne verir” (satılan eşya).
  • Satışlar: Toplam adet ve gelir ile satış tablosu; durum, kaynak (mağaza ya da oyun içi) ve oyuncuya göre süzülür.
  • Geçmiş: Ürünün değişiklik olayları, en yeni üstte.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Satılacak bir eşya seçin”Önce eşyayı seçin; liste boşsa İçerik sayfasından eşya ekleyin.
“Fiyat 0’dan büyük bir sayı olmalı”Fiyatı para biriminin kuruş hanesine uygun yazın; sıfır ya da boş olamaz.
“Stok, ayrılmış N adetten az olamaz”Bu kadar adet zaten satın alma için ayrılmıştır; stoğu bunun altına indiremezsiniz.
“Bitiş, başlangıçtan sonra olmalı”Tarih aralığını düzeltin; en az bir uç yazmalısınız.
“Bu ürünün API ile verilmiş bir görünürlük koşulu var”Koşul panelden değiştirilemez; kaydedince olduğu gibi korunur.
Araç ürününün fiyatını İçerik’ten değiştiremiyorumMağazada ürünü olan aracın fiyatının tek kaynağı üründür; fiyatı burada değiştirin.

İlgili dokümanlar

Para birimi

Amaç

Bu sayfa tek bir para biriminin ayrıntısıdır; aynı sayfa para birimi oluşturma ve düzenleme formu olarak da çalışır. Ayrıntı görünümünde para biriminin ikonu, anahtarı, sürümü, kuruş hanesi, arz tavanı ve gönderilebilirliği ile son 30 günün basılan ve yakılan miktarı, en çok bakiyesi olanlar, cüzdanlar, hareketler ve bu parayla fiyatlanan ürünler görünür.

Ne zaman kullanılır

  • Yeni para birimi oluşturmak için.
  • Kuruş hanesini, ikonu, arz tavanını ya da gönderilebilirliği değiştirmek için.
  • Paranın oyunda nasıl dolaştığına bakmak için.

Adım adım

Yeni para birimi oluşturma

  1. Para birimleri sayfasında Yeni para birimi’ne basın.
  2. Ad’ı yazın. Anahtarı küçük harf, rakam, tire ve alt çizgiyle yazın; kaydettikten sonra değişmez.
  3. İsterseniz ikon görseli yükleyin.
  4. Kuruş hanesi’ni seçin. Sağdaki “Oyunda böyle görünür” önizlemesi örnek bir tutarı bu haneyle gösterir.
  5. Arz tavanını seçin ve Oyuncular arası gönderilebilir kutusunu gerektiği gibi işaretleyin.
  6. Bir neden yazın ve Para birimini oluştur’a basın.

Para birimini düzenleme

  1. Ayrıntı sayfasında Düzenle’ye basın.
  2. Alanları değiştirip Yeni sürüm olarak kaydet’e basın. Her kayıt yeni bir sürümdür; eski sürümler değişmez.

Uyarı

Kuruş hanesini değiştirmek yalnızca gösterimi değiştirir; saklanan bakiyeler ve fiyatlar aynı kalır. Bu yüzden hane değişince oyunculara gösterilen tutarlar farklı görünebilir. Değiştirmeden önce emin olun.

Alanlar ve seçenekler

AlanZorunluAçıklama
AdEvetEn çok 120 karakter
AnahtarOluştururkenKüçük harf, rakam, tire, alt çizgi; sonradan değişmez
İkon görseliHayırPNG, JPEG ya da WebP
Kuruş hanesiEvet0 (tam sayı), 1, 2 (kuruş), 3 ya da 4
Arz tavanıEvetSınırsız ya da sınırlı
Toplam arz en çokSınırlıdaKuruş haneli tutar
Oyuncular arası gönderilebilirHayırSeçiliyse oyuncular birbirine gönderebilir
İşlem nedeniEvet8-500 karakter

Ayrıntı sayfasındaki kartlar

  • Son 30 gün: Günlük basılan ve yakılan miktar.
  • En çok bakiyesi olanlar: Dünün kapanışına göre bakiye sıralaması.
  • Cüzdanlar ve Hareketler: Bu para birimindeki cüzdanlar (bakiyeye göre sıralanabilir) ve defter hareketleri. İlgili okuma yetkiniz yoksa görünmez.
  • Bu parayla fiyatlanan ürünler: Mağazada bu para birimiyle fiyatlanan ürünler; ürün sayfasına bağlantı verir.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu anahtarla bir para birimi zaten var”Başka bir anahtar yazın; mevcut para birimini değiştirmek için onu açıp Düzenle’yi kullanın.
“İkon görseli bu ortamda bulunamadı”Görseli yeniden yükleyin.
Anahtarı değiştiremiyorumAnahtar yalnızca oluştururken yazılır; yanlışsa yeni bir para birimi oluşturun.
“Bunu görme yetkiniz yok” yazan kartlarHesabınızın rolünde o bilgiyi okuma yetkisi yoktur.
Hane değiştirdim, tutarlar bozuk göründüHane yalnız gösterimdir; eski haneye dönüp yeni sürüm olarak kaydedin.

İlgili dokümanlar

Ödül tablosu

Amaç

Bu sayfa tek bir ödül tablosunu gösterir; aynı sayfa tabloyu oluşturma ve düzenleme formu olarak da çalışır. Tablo iki parçadan oluşur: şans satırları (her çekilişte biri seçilir) ve her zaman verilenler (çekilişten bağımsız, her seferinde verilir). Tabloyu kaydetmeden önce deneyebilirsiniz.

Ne zaman kullanılır

  • NPC, yaratık, turnuva ya da görev için yeni ödül tablosu kurmak için.
  • Şansları ya da miktarları değiştirmek için.
  • Tablonun nerede kullanıldığını ve son verilen ödülleri görmek için.

Adım adım

Yeni tablo oluşturma

  1. Ödül tabloları sayfasında Yeni ödül tablosu’na basın.
  2. Ad’ı ve Anahtar’ı yazın; anahtar kaydettikten sonra değişmez.
  3. Kaç çekiliş seçin.
  4. Satır ekle ile ödül satırlarını girin: tür, ne verdiği, en az ve en çok miktar ve şans. Satırları yukarı ve aşağı oklarıyla sıralayabilirsiniz.
  5. Şansların toplamını %100’e tamamlayın. Toplam, satırların altında canlı görünür; Kalanı ‘Ödül yok’ yap ile geri kalanı “ödül yok” satırına verebilirsiniz.
  6. İsterseniz Her zaman verilenler bölümüne çekilişten bağımsız ödüller ekleyin.
  7. Sağdaki Dene panelinden tabloyu kaydetmeden deneyin.
  8. Bir neden yazın ve Ödül tablosunu oluştur’a basın.

Tabloyu düzenleme

  1. Tablonun sayfasında Düzenle’ye basın.
  2. Satırları değiştirip Yeni sürüm olarak kaydet’e basın. Her kayıt yeni bir sürümdür; eski sürümler silinmez ve sürüm geçmişinde görünür.

Tabloyu deneme

Dene düğmesi tabloyla kaydetmeden 100 örnek çekiliş yapar ve hangi ödülün kaç kez çıktığını özetler. Hiçbir ödül verilmez ve her denemede sonuç değişir. Form eksikse (örneğin şanslar %100 değilse) düğme kapalıdır.

Tabloyu silme

Sil’e basın ve bir neden yazın. Tablo bir NPC kuralı, görev ya da içerik girdisi tarafından kullanılıyorsa silinemez; pencere kullanan yerleri listeler. Geçmişte verilen ödüller silinmez.

Alanlar ve seçenekler

Tablo bilgileri

AlanZorunluAçıklama
AdEvetEn çok 120 karakter
AnahtarOluştururkenKüçük harf, rakam, tire, alt çizgi; sonradan değişmez
Kaç çekilişEvet1-10

Şans satırları (en çok 50)

AlanAçıklama
Ödül türüPara, Eşya ya da Ödül yok
NePara birimi ya da eşya
En az, En çokMiktar aralığı; en çok boşsa en az ile aynı
Şans %Satırın çıkma şansı; toplam tam %100 olmalı

Her zaman verilenler (en çok 10)

Ödül türü (Para ya da Eşya), Ne, En az ve En çok alanlarından oluşur; şans yoktur.

Profil sayfasındaki kartlar

  • Şans çubukları, Her zaman verilenler, Kaç çekiliş.
  • Nerede kullanılıyor: Tabloyu kullanan NPC kuralları, görevler ve içerik girdileri.
  • Son verilen ödüller: Tablodan verilen son ödüller; oyuncu, kaynak ve ödül.
  • Sürüm geçmişi: Her sürümün tarihi ve satır sayısı; güncel olan rozetlidir.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Kaydetmek için toplam %100 olmalı”Şansların toplamını tam %100’e getirin; eksik kalanı için “Ödül yok” satırı kullanabilirsiniz.
“Kaydetmek için en az bir ödül ekleyin”En az bir şans satırı ya da bir “her zaman verilen” ödül gerekir.
“Bu anahtarla bir ödül tablosu zaten var”Başka bir anahtar seçin; var olan tabloyu değiştirmek için onu açıp Düzenle’yi kullanın.
Tabloyu silemiyorumTablo kullanımdadır; önce kullanan yerlerden ayırın. Silme penceresi nerelerde kullanıldığını gösterir.
Miktar hatası alıyorumPara miktarını para biriminin kuruş haneli biçimde yazın; en az 1, en çok 1.000.000 en küçük birim olabilir.
“Dene” kapalıFormdaki eksikleri tamamlayın; şanslar %100 olmalıdır.

İlgili dokümanlar

Leaderboard ayrıntısı

Amaç

Bu sayfa tek bir sıralama tablosunu profil sayfası düzeniyle gösterir. Üstte tablonun adı, kopyalanabilir anahtarı ve dört özet kutusu (bu sezon oyuncu sayısı, lider, son puan zamanı, sonraki sıfırlama) vardır. Altında üç sekme bulunur: Bu sezon, Geçmiş sezonlar ve Ayarlar.

Ne zaman kullanılır

  • Tablonun en üst sıralarına ve belirli bir oyuncunun yerine bakmak için.
  • Kapanmış sezonların kazananlarını ve kapanış tablosunu görmek için.
  • Tablonun oyunda görünürlüğünü ya da puan biçimini değiştirmek için.
  • Oyun kodunuzda tabloyu hangi anahtarla okuyacağınızı kopyalamak için.

Adım adım

Bu sezonun sırasını görme

  1. Bu sezon sekmesinde en üst sıralar listelenir. Her satırda sıra, oyuncu, puan ve son güncelleme zamanı vardır; oyuncunun adına tıklarsanız oyuncunun sayfası açılır.
  2. Belirli bir oyuncuyu görmek için Oyuncu bul alanına adını ya da e-postasını yazıp seçin. Oyuncunun çevresindeki satırlar ayrı bir kartta gösterilir.

Yasaklı ya da silinmiş oyuncular soluk gösterilir ve “oyunda gizli” diye etiketlenir; oyuncular bu satırları oyunda görmez.

Geçmiş sezonlara bakma

  1. Geçmiş sezonlar sekmesinde kapanmış sezonlar listelenir; her satır tarihi, oyuncu sayısını ve birinciyi gösterir.
  2. Bir satıra tıklayınca sezonun kapanış tablosu (ilk 100 satır) açılır. Kapanmış sezon değiştirilemez.

Ayarları değiştirme

  1. Ayarlar sekmesinde Oyunda görünsün, Puan nasıl gösterilsin? ve Birim eki alanlarını değiştirin.
  2. Kaydet düğmesine basın. Yalnızca değişen alanlar gönderilir.

Alttaki Tablo tanımı kartı anahtarı, sıralama yönünü, sıfırlama aralığını ve kaynağı salt okunur gösterir; bunlar kurulurken seçilir ve değiştirilemez. Geliştirici bilgisi bölümü oyuncu oturumuyla okuma ucunu ve SDK çağrısını gösterir.

Alanlar ve seçenekler

AlanZorunluAçıklama
Oyuncu bulHayırYalnızca görüntüleme; oyuncunun çevresini gösterir
Oyunda görünsünHayırOyuncuların tabloyu görmesini açar ya da kapatır
Puan nasıl gösterilsin?EvetSayı, para ya da süre
Birim ekiHayırEn fazla 8 karakter; süre biriminde yoktur

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu sezon henüz puan yok”Oyun sunucunuz bu sezon puan göndermemiştir. İlk puan gelince satır görünür.
“Tablo bulunamadı”Bu anahtarla bir tablo yok ya da silinmiş; Leaderboard listesinden yeniden açın.
Ayarlar sekmesi kapalıAlanları değiştirmek için tablo yönetme yetkisi gerekir; bir yöneticiden isteyin.
“Bu tabloya yalnız … eklentisi puan yazar”Tabloyu bir eklenti kurmuştur. Puanları oyun sunucunuz değil eklenti yazar.
Sıralama yönünü ya da sıfırlamayı değiştirmek istiyorumBunlar kurulduktan sonra değişmez. Leaderboard sayfasından yeni bir tablo kurun.
Listede yalnızca ilk satırlar varSayfa ilk satırları gösterir; devamı bu sürümde listelenmiyor.

İlgili dokümanlar

Kural kartı

Amaç

Bu sayfa tek bir oyun kuralının ayrıntısını gösterir: kural ne zaman çalışır, hangi koşullar vardır, ne yapar ve hangi değerleri siz ayarlarsınız. Burada yaptığınız değişiklikler hemen oyuna geçmez; sayfanın altında birikir ve tek bir yayınla birlikte uygulanır. Kuralın ne işe yaradığı oyunun eklentisi tarafından yazılmıştır; sayfanın başlığı ve metinleri eklentiden gelir.

Ne zaman kullanılır

  • Bir kuralın değerini (süre, mesafe, hız eşiği gibi) değiştirmek için.
  • Yayınlamadan önce değişikliğin sonucunu Ne değişir? önizlemesiyle görmek için.
  • Kuralın korumasını geçici olarak gözleme almak ya da yeniden açmak için.

Adım adım

  1. Kurallar sayfasında bir karta tıklayın.
  2. Ne zaman, Koşullar ve Ne yap bölümlerini okuyun. Kilit simgeli koşullar değiştirilemez.
  3. Ne yap bölümünde bir değeri değiştirin. Alan, değeri birimiyle gösterir (örneğin saniye ya da km/sa); ondalık sayıyı virgülle yazabilirsiniz. Altında izin verilen aralık ve varsayılan değer yazar; Varsayılana dön düğmesi değeri eski haline getirir.
  4. İsterseniz sağdaki Ne değişir? kartında bir senaryo seçip Önizle düğmesine basın. Kart “şu an” ve “yeni” değerlerle olayların sırasını yan yana gösterir.
  5. Sayfanın altındaki çubukta Gözden geçir ve yayınla düğmesine basın. Açılan pencerede değişikliklerin listesini kontrol edin, nedeninizi yazın ve Yayınla düğmesine basın.

Not

Bekleyen değişiklikler yalnızca bu tarayıcı sekmesinde durur. Sayfayı yenilerseniz ya da sekmeyi kapatırsanız yayınlanmamış değişiklikler kaybolur. Başka kartlara geçmek onları silmez.

Uyarı

Bazı değerler yayından sonra hemen, bazıları ise sunucu yeniden başlayınca uygulanır. Kartta “Yayında: … · Bekliyor: …” satırı görürseniz değer yayınlanmıştır ama canlı dünya henüz eski değeri kullanıyordur.

Alanlar ve seçenekler

AlanZorunluAçıklama
Ayar değeriHayırKartın her ayarı için bir satır; aralık dışı değer kabul edilmez
Kural şalteri (“Bu kural açık”)HayırYalnızca şalteri olan kartlarda görünür; yazma yetkisi ister
Senaryo (önizleme)HayırKartın sunduğu örnek durumlardan biri
Senaryo sayıları (önizleme)HayırSenaryoya göre değişir; sınırlar alanın altında yazar
Neden değiştiriyorsunuz? (yayın penceresi)Evet8-500 karakter

Karttaki rozetler: Açık (koruma uygulanıyor), Gözlemde (kural oyuna uygulanmıyor), Değiştirildi (yayındaki değer varsayılandan farklı), Yayınlanmadı (bekleyen düzenleme var), Yeniden başlatınca (canlı dünya değeri yeniden başlatınca alır).

Sık karşılaşılan durumlar

DurumNe yapmalı
Ayar alanları kapalı ve “yetkiniz yok” yazıyorDeğerleri okuyabilirsiniz ama değiştirmek için oyun kuralı yayın yetkisi gerekir; bir yöneticiden isteyin.
Yayın “başka bir değişiklik yayınlandı” diyorSiz bakarken başka biri yayınlamıştır. Son durumu yükle düğmesine basın; düzenlemeleriniz korunur ve fark yeniden hesaplanır.
Yayın “bazı değerler kabul edilmedi” diyorListedeki ayar adına tıklayın; sayfa o alana gider. Mesajdaki sınıra göre düzeltip yeniden yayınlayın.
“Bu kural bulunamadı” yazıyorKart kimliği bu oyunun kataloğunda yoktur; Kurallar sayfasından yeniden açın.
Önizleme “yok” diyorBu kartın önizleme senaryosu bulunmuyor; yayınlamadan önce değerleri dikkatle kontrol edin.

İlgili dokümanlar

Kural geçmişi

Amaç

Her oyun kuralı yayını yeni bir sürümdür. Bu sayfa sürümleri en yeniden eskiye sıralar: kim yayınladı, ne zaman, hangi nedenle ve hangi ayarlar değişti. Eski bir sürüme dönmek de yeni bir sürüm yazar; geçmiş hiçbir zaman silinmez.

Ne zaman kullanılır

  • Yanlış yayınlanan bir ayarı hızla geri almak için.
  • Bir ayarın ne zaman ve kim tarafından değiştirildiğini bulmak için.
  • Bütün ayarları oyunun hazır (kod) değerlerine döndürmek için.

Adım adım

Bir sürümün değişikliklerini görme

  1. Sürümün altındaki Değişen ayarlar satırına tıklayın.
  2. Her ayar için “önceki değer → yeni değer” görürsünüz. Hiçbir ayarı değiştirmeyen sürüm bunu açıkça söyler.

Eski bir sürüme dönme

  1. Dönmek istediğiniz sürümün satırında Bu sürüme dön düğmesine basın. Yayında olan sürümde bu düğme görünmez.
  2. Açılan pencerede nedeninizi yazın ve Geri al düğmesine basın.
  3. O sürümün değerleri yeni bir sürüm olarak yayınlanır. Polis ayarları en geç 6 saniye içinde uygulanır.

Hazır (kod) değerlere dönme

  1. Sayfanın üstündeki Bugünkü (kod) değerlere döndür düğmesine basın.
  2. Nedeninizi yazıp Kod değerlerine döndür düğmesiyle onaylayın. Bütün ayarlar oyunun varsayılanlarına döner; bu da yeni bir sürümdür.

Not

Geri alma, “sürüm 7’ye geri alındı” gibi bir rozetle listede görünür. Hiçbir sürüm silinmez, yalnızca yeni bir sürüm eklenir.

Alanlar ve seçenekler

AlanZorunluAçıklama
İşlem nedeni (geri alma penceresi)Evet8-500 karakter

Satırdaki rozetler: Yayında (şu an uygulanan sürüm), Geri alındı ya da Sürüm N’e geri alındı, Kod değerlerine döndürüldü.

Sık karşılaşılan durumlar

DurumNe yapmalı
Geri alma düğmeleri yokGeri almak için oyun kuralı yayın yetkisi gerekir; geçmişi okuyabilirsiniz. Bir yöneticiden isteyin.
“Henüz yayın yok” yazıyorAyarlar oyunun varsayılan değerlerinde; ilk yayından sonra sürümler burada görünür.
Geri alma “başka bir değişiklik yayınlandı” ile reddedildiSiz bakarken başka biri yayınlamıştır. Sayfa güncel durumu yeniden okur; güncel sürümü görüp yeniden onaylayın.
Eski sürümleri göremiyorumListe sayfa sayfa yüklenir; sonda Daha eski sürümler düğmesine basın.

İlgili dokümanlar

Otomasyon düzenleyici

Amaç

Otomasyon, “şu olay olunca, şu koşul doğruysa, şunu yap” biçiminde bir kuraldır. Bu sayfa kuralı üç adımda kurmanızı sağlar: Ne zaman (hangi olay), Koşullar (hangi durumda) ve Ne yap (karaktere ne yapılacağı). Kod yazmanız gerekmez; panel kuralı arkada sizin için hazırlar.

Ne zaman kullanılır

  • Seviye atlayınca ödül, belirli bir yetenek açılınca kalıcı güç gibi otomatik sonuçlar tanımlamak için.
  • Var olan bir kuralın koşulunu ya da eylemini değiştirip yeni sürüm yayınlamak için.
  • Kuralı yayınlamadan önce bir karakter ve örnek olayla denemek için.

Adım adım

  1. Kurallar sayfasında Yeni kural düğmesine basın. Yeni kuralda önce Şablondan başla bölümünden hazır bir örnek seçebilir ya da Boş kural ile başlayabilirsiniz.
  2. Kural bölümüne bir ad yazın; anahtar otomatik üretilir.
  3. Ne zaman bölümünden olayı seçin.
  4. Koşullar bölümünde Koşul ekle ile koşul ekleyin. Hiç koşul eklemezseniz kural olay her olduğunda çalışır. Birden fazla koşulda “hepsi” ya da “herhangi biri” seçin.
  5. Ne yap bölümünde en az bir eylem bırakın; Eylem ekle ile ekleyebilir, okla sırasını değiştirebilirsiniz.
  6. İsterseniz Kuralı dene düğmesiyle bir karakter ve örnek olay seçip sonucu görün. Deneme hiçbir kayıt oluşturmaz.
  7. Dene ve yayınla bölümünde nedeninizi yazın. Yeni kural için Hemen açılsın kutusunu isteğe bağlı işaretleyin ve Yayınla düğmesine basın.

Var olan bir kuralı düzenlerken Yeni sürüm yayınla düğmesi görünür; anahtar değişmez.

Not

Yayınlanan kural varsayılan olarak kapalı kaydedilir. Kurallar listesindeki Aç düğmesi ya da Hemen açılsın kutusu onu çalıştırır.

Uyarı

Koşullarda ve eylemlerde yazdığınız özellik, kaynak, bayrak ve yetenek adları oyunun kendi adlarıyla birebir aynı olmalıdır. Yanlış yazılan ad kuralın beklediğiniz gibi çalışmasını engeller; yayınlamadan önce Kuralı dene ile gerçek bir karakterde sonucu görün.

Alanlar ve seçenekler

AlanZorunluAçıklama
Kural adıEvet1-120 karakter
AnahtarEvetAddan üretilir, en fazla 64 karakter; yayından sonra değişmez
AçıklamaHayırEn fazla 500 karakter
Ne zamanEvetOlay seçimi (seviye atlama, kaynak değişimi, bayrak değişimi gibi)
Koşulların bağlacıBirden fazla koşuldaHepsi / herhangi biri
Neye bakılıyor, hangi bilgi, ad, karşılaştırma, değer, durumKoşul ekleninceEn fazla 20 koşul; sayı alanlarında tam sayı
Ne yapılsın, ad, miktar, durumEvet (en az bir eylem)En fazla 10 eylem; miktar sıfır olamaz
İşlem, değer, kaynak türü, kaynak adıGüçlendirme eylemlerindeYüzde işlemlerinde değer yüzdenin 100 katıdır
ÖncelikEvet1-1000 arası tam sayı, varsayılan 100; “Gelişmiş” bölümündedir
Karakter, örnek olay bilgileriDenemedeYalnızca denemeyi etkiler
Neden değiştiriyorsunuz?Evet8-500 karakter
Hemen açılsınHayırYalnızca yeni kuralda görünür

Gelişmiş bölümü kuralın koşulunun CEL (koşul dili) karşılığını salt okunur gösterir; panel bunu koşul satırlarından kendisi üretir ve buradan düzenlenmez.

Sık karşılaşılan durumlar

DurumNe yapmalı
“Yayınlamadan önce tamamlayın” listesi çıkıyorListedeki eksik alanları doldurun; yayınla düğmesi bunlar bitene kadar kapalı kalır.
“Bu anahtarla bir kural zaten var”Kural adını ya da anahtarı değiştirin.
“Bu arada başka bir değişiklik yayınlandı”Düzenlemeleriniz korunur. Son durumu yükle düğmesine basın ve yeniden yayınlayın.
“Kural kabul edilmedi”Sunucu kuralı reddetmiştir; listedeki mesajlara göre düzeltin. Girdiğiniz değerler korunur.
Kural koşulunu değiştiremiyorum, “kodla (CEL) yazılmış” yazıyorKural panel dışından kodla yazılmıştır. Panelde görüntülenir, açılıp kapatılır ve eski sürüme döndürülür ama düzenlenemez.
Düzenleme alanları kapalıKural yazma yetkiniz yoktur; bir yöneticiden isteyin.
Deneme “karakter seçin” diyorDenemek için karakter aramasından bir karakter seçmelisiniz.

İlgili dokümanlar

Ortamlar ve alan adları

Amaç

Bir oyunun birden çok ortamı olabilir: geliştirme (dev), hazırlık (staging) ve canlı (prod). Her ortamın kendi oyuncuları, verisi ve API anahtarları vardır. Bu sekme oyunun ortamlarını kart olarak listeler; her ortamı kendi alan adlarına bağlamanızı, yeni ortam eklemenizi ve ortamı arşivlemenizi sağlar. Sekme yalnızca süper yöneticiye açıktır.

Ne zaman kullanılır

  • Oyununuz tarayıcıda yeni bir adresten açılacaksa o adresi ortama bağlamak için.
  • Hazırlık ya da geliştirme ortamı eklemek için.
  • Kullanılmayan bir ortamı gizlemek için.

Adım adım

Alan adı bağlama

  1. Ortamın kartındaki Alan adları kutusuna her satıra bir alan adı yazın (örneğin oyun.example.com). Adlar küçük harfe çevrilir; boş satırlar ve tekrarlar atılır.
  2. Gerekçe alanına nedeninizi yazın.
  3. Alan adlarını kaydet düğmesine basın.

Ortam ekleme

  1. Sekmenin üstündeki Ortam ekle düğmesine basın.
  2. Ortam türünü seçin: Geliştirme (dev), Hazırlık (staging) ya da Canlı (prod). Pencere varsayılan olarak Hazırlık’ı seçer.
  3. Nedeninizi yazıp onaylayın. Alan adları ve Google girişi ortam eklendikten sonra ayrıca bağlanır.

Ortamı arşivleme ya da geri açma

Kartın altındaki Arşivle düğmesi ortamı gizler; veri silinmez. Arşivli ortamda Geri aç düğmesi görünür ve alan adı alanları kapalıdır.

Uyarı

Eski adresi, yeni adres çalışmaya başlayıp eski adres yönlendirilene kadar listede tutun. Bir adres listeden çıkarılınca o adresten açılan oyun hemen giriş yapamaz ve kayıt yazamaz. Kaydet’e basınca kaldırılan adresler bir pencerede listelenir; Yine de kaldır ve kaydet demeden değişiklik uygulanmaz. Liste tamamen boşalırsa sunucunun genel izinli listesi geçerli olur.

Not

Alan adları, tarayıcıdan gelen isteklerin kaynak (origin) kontrolünde izinli liste olarak kullanılır. Oyununuzu sunduğunuz her adresi ilgili ortama bağlayın; başka ortama bağlı bir alan adı kabul edilmez.

Alanlar ve seçenekler

AlanZorunluAçıklama
Alan adlarıHayırSatır başına bir alan adı; en fazla 10
GerekçeEvet8-500 karakter
Ortam türü (ekleme penceresi)EvetGeliştirme, Hazırlık ya da Canlı
İşlem nedeni (pencerelerde)Evet8-500 karakter

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu alan adı başka bir ortama bağlı”Aynı alan adı iki ortama bağlanamaz. Önce diğer ortamdan kaldırın.
“En fazla 10 alan adı bağlanabilir”Listeyi kısaltın; bir ortama en fazla 10 alan adı bağlanır.
“Önce başka bir ortam seçin”Şu an seçili olan ortam arşivlenemez; üst çubuktan başka bir ortam seçin.
Sekme “yetki gerektiriyor” diyorBu sekme yalnızca süper yöneticiye açıktır.
Eski adresteki oyun giriş yapamıyorAdres listeden çıkarılmış. Adresi listeye geri ekleyip kaydedin; ardından yeni adrese geçişi tamamlayın.
Arşivleme reddedildiOrtamın canlı yetkisi ya da etkin bir API anahtarı vardır; pencere nedenini gösterir.

İlgili dokümanlar

Eklenti sayfası

Amaç

Oyunun eklentisi, kendi yönetim ekranlarını panelde tanımlayabilir: oyuncu listesi, filtreli tablolar, ayrıntı sayfaları, grafikler ve eylem düğmeleri. Bu sayfa bu ekranların hepsi için ortak yardımdır; eklentiye özel içerik eklentinin kendi tanımından gelir ve burada anlatılmaz.

Ne zaman kullanılır

  • Eklentinin bir ekranında arama, sayfalama ya da eylem yaparken ne olacağını anlamak için.
  • Ekranın üstündeki durum uyarılarının ne anlama geldiğini öğrenmek için.

Adım adım

Ekranı tanıma

  1. Sayfanın başlığı eklentinin adıdır; altındaki ikinci başlık ekranın adıdır. Eklentinin ekranları başlığın altındaki sekmelerde listelenir.
  2. Yenile düğmesi yalnızca bu ekranın verisini yeniden okur.
  3. Başlığın altındaki Son okuma satırı verinin ne zaman okunduğunu gösterir.

Liste ve tablolarda gezinme

  • Arama kutusuna yazdıkça liste daralır; arama metni ve sayfa numarası adres çubuğunda durur, bu yüzden bağlantıyı paylaşabilirsiniz.
  • Sayfalayıcıda Sayfa başına listesinden 12, 25 ya da 50 satır seçebilirsiniz.
  • CSV indir düğmesi varsa ekrana yüklenmiş satırları dosya olarak indirir; sayfalı tabloda yalnızca yüklü sayfayı içerir.

Eylem yapma

  1. Ekrandaki eylem düğmesine basın; bir pencere açılır. Düğmeyi yalnızca eklenti işletme yetkisi olan yöneticiler görür ya da kullanabilir.
  2. Alanları doldurun, nedeninizi İşlem nedeni alanına yazın ve onaylayın.
  3. Sonuç ekranda görünür; eylem işlem geçmişine yazılır.

Not

Eklentinin durumu ekranın üstünde bir uyarıyla görünür. Salt okunur durumda veriler görünür ama değişiklik yapılamaz; Boşaltılıyor durumunda yeni işlem kabul edilmez, açık işlemler tamamlanır. Durumu Eklentiler sayfasından yönetici değiştirir.

Alanlar ve seçenekler

AlanZorunluAçıklama
AramaHayırEklentinin tanımladığı alanlarda arar
Sayfa başınaHayır12, 25 ya da 50
İşlem nedeni (eylem penceresi)EvetSınırlar alanın altında yazar
Eylem alanlarıEylemine göreEklenti tanımlar; her alanın açıklaması pencerede yazar

Sık karşılaşılan durumlar

DurumNe yapmalı
“Bu eklenti bu ortamda salt okunur”Eklenti yalnızca okumaya açık. Değişiklik gerekiyorsa bir yöneticiden Eklentiler sayfasında durumu açık yapmasını isteyin.
“Bu eklenti boşaltılıyor”Sürüm geçişi öncesinde eklenti yeni işlem almıyor; boşaltma bitince açık duruma dönecektir.
Eylem düğmesi yok ya da kapalıEklenti işletme yetkiniz yoktur ya da eklenti salt okunurdur.
Ekranın üstünde “kullanımdan kalkıyor” bandı varEkran yeni bir ekranla değiştiriliyor; bandaki bağlantı yerine geçen ekrana götürür.
Eylem “başka bir değişiklik yapıldı” diyorKayıt siz bakarken değişmiştir. Güncel hâlini görüp yeniden onaylayın.

İlgili dokümanlar