# Open Play > Open Play bir oyun arka uç (backend) platformudur: oyuncu kimliği ve oturumları, karakterler, envanter, ekonomi, mağaza, ödüller, sosyal özellikler, sıralama tabloları, bildirimler, webhook'lar ve gerçek zamanlı bağlantılar tek sunucuda. Oyunlar TypeScript ya da Unity SDK'sıyla HTTP (REST) ve WebSocket üzerinden bağlanır; oyun ekibi aynı sunucuyu yönetim panelinden ayarlar. --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/overview.html # 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](concepts.md#modüller)). Modüller panelde **Oyun ayarları › Modüller** sekmesinden yönetilir. | Modül | Oyununuza ne verir? | Rehber | |---|---|---| | `characters` | Karakter tanımları, karakterler, komutlar, kurallar, özellikler (trait), seviye eğrileri | [Karakterler](../guides/characters.md) | | `economy` | Para birimleri, cüzdanlar, transferler, defter (ledger) | [Ekonomi](../guides/economy.md) | | `items` | Eşya tanımları, eşya örnekleri, envanter | [Eşyalar ve envanter](../guides/inventory-items.md) | | `trading` | Tüccar ve oyuncular arası takas | Ham REST: [uç noktalar](../reference/endpoints.md) | | `store` | Mağaza kataloğu, satın alma, uygulama içi satın alma (IAP) doğrulaması | [Mağaza ve IAP](../guides/store-iap.md) | | `rewards` | Ödül tabloları, görev, NPC ödülü, VIP, günlük giriş ödülü | [Ödüller](../guides/rewards.md) | | `social` | Klan ve parti | [Sosyal](../guides/social.md) | | `chat` | Sohbet | [Sohbet](../guides/chat.md) | | `competition` | Skor tabloları ve turnuvalar | [Rekabet](../guides/competition.md) | | `notifications` | Bildirim kanalları, şablonlar, oyuncu gelen kutusu, web push | [Bildirimler](../guides/notifications.md) | | `webhooks` | Oyun olaylarını kendi sunucunuza imzalı olarak iletme | [Webhook'lar](../guides/webhooks.md) | | `scenes` | Sahneler ve görsel varlıklar | [Sahneler, varlıklar, modeller](../guides/scenes-assets-models.md) | | `plugins` | Eklenti platformu ve gerçek zamanlı yüzey | [Eklentiler](../plugins/overview.md), [Gerçek zamanlı](../realtime/overview.md) | 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](../auth/overview.md). - **İçerik grupları ve katalog:** panelden yönetilen, oyunun okuduğu veri. Bkz. [İçerik grupları](../guides/content-groups.md). - **Oyun kuralları ve otomasyonlar.** Bkz. [Oyun kuralları](../guides/game-rules.md). - **API anahtarları, ortamlar ve alan adları.** Bkz. [Kavramlar](concepts.md). ## Hangi kanaldan konuşursunuz? | Kanal | Kim kullanır? | Kimlik | Ne için? | |---|---|---|---| | **REST** (`/v1/...`) + sunucu anahtarı | Oyun sunucunuz (güvenilen arka uç) | `Authorization: Bearer crx_live_<önek>_` | Yönetim ve parayı/eşyayı değiştiren işlemler | | **REST** + istemci anahtarı | Oyun istemciniz | aynı biçimde istemci anahtarı | Sınırlı, hassas olmayan işlemler | | **REST** + oyuncu oturumu | Oyuncunun kendi istemcisi (tarayıcı, Unity) | çerez ya da oturum belirteci + `x-crx-env` | Oyuncunun kendi verisi: giriş, katalog, mağaza, sıralama, eklenti komutları | | **WebSocket** (`/v1/realtime/ws` vb.) | Oyuncunun istemcisi | tek kullanımlık bilet | Eklenti durumu, anlık girdi, kesintisiz akış | | **Webhook** (Open Play → sizin sunucunuz) | Sizin sunucunuz | `X-Crx-Signature` imzası | Olay bildirimi | | **SDK** | TypeScript ve Unity | yukarı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](concepts.md): stüdyo, oyun, ortam, modül, anahtar türleri, oyuncu oturumu. 2. [Hızlı başlangıç](quickstart.md): ilk sunucu çağrınız ve ilk oyuncu girişiniz. 3. [Mimari ve barındırma](architecture-and-hosting.md): aynı köken (same-origin) kuralı, alan adları, neyin nerede çalıştığı. 4. [Kimlik doğrulama](../auth/overview.md) ve [SDK'lar](../sdk/typescript.md). ## 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](architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil). - **Üçüncü taraf eklenti yükleme desteklenmez.** Eklentiler sunucuya derlenmiş olarak gelir; panelden eklenti yüklenmez. Bkz. [Eklentiler](../plugins/overview.md). - **SDK kapsamı kısmidir.** TypeScript SDK'sında tipli istemci olmayan modüller ham REST ile kullanılır. Bkz. [TypeScript SDK](../sdk/typescript.md#kapsam-hangi-modülün-tipli-istemcisi-var). - **Unity'de yalnız Unity 6000.6.7f1 doğrulanmıştır.** Bkz. [Unity SDK](../sdk/unity.md#doğrulanan-platformlar). - **Hata gövdeleri çoğunlukla düz metindir.** Yalnız birkaç yerde JSON hata zarfı vardır. Bkz. [Hatalar](../reference/errors.md). --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/concepts.html # 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. | Katman | Anlamı | Not | |---|---|---| | **Stüdyo** | Oyunlarınızı bir arada tutan kuruluş | Bir sunucuda birden çok stüdyo olabilir | | **Oyun** | Tek bir ürün | Yeni oyun oluşturulunca `dev` ve `prod` ortamları otomatik açılır | | **Ortam** | Oyunun yalıtılmış bir kopyası: `dev`, `prod`, istenirse `staging` | Oyuncular, 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: ` 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ıç](quickstart.md#1-oyun-ve-ortam-oluşturun) ve [Oyun ayarları](../panel/game-settings-environments.md). ## 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":""}`. 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](../reference/errors.md). ## API anahtarları: sunucu ve istemci API anahtarı, bir **ortama** bağlı gizli bir dizgedir ve `Authorization: Bearer` başlığıyla gönderilir. Biçimi: ```text crx_live_<önek>_ ``` 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ç kodunuz | Oyun istemcisi (tarayıcı, mobil) | | Gömülebilir mi? | **Hayır**, gizli tutulur | Evet | | Ne yapabilir? | Tüm işlemler: tanım oluşturma, para verme, eşya basma, webhook kaydı, yönetim | Yalnız izin verilen, hassas olmayan işlemler | | Örnek | `add_xp` komutu, para verme, eşya basma | karakteri 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](../reference/endpoints.md) listelenir. Anahtar türü yetersizse çağrı `403` alır. Panelde anahtar oluşturma: **Oyun ayarları › API anahtarları › Yeni API anahtarı** (bkz. [API anahtarları](../panel/game-settings-api-keys.md)). 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](../operations/security.md). ## 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 ` 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: - [Kimlik doğrulamaya genel bakış](../auth/overview.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) 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](../auth/server-verification.md). ## 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 (`.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ış](../plugins/overview.md). ## 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](../reference/idempotency-and-concurrency.md). - **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](../reference/numbers-and-encoding.md). Terimlerin kısa tanımı için bkz. [Sözlük](../reference/glossary.md). --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/quickstart.html # 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](concepts.md) 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](../panel/tenancy.md). ## 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](../panel/game-settings-modules.md). ## 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>_`. Bu sayfada ortam değişkeni olarak kullanacağız: ```bash export OPEN_PLAY_URL="https://oyun.example.com/open-play" export OPEN_PLAY_SERVER_KEY="crx_live_<önek>_" ``` > **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. ```bash curl -sS "$OPEN_PLAY_URL/v1/characters?limit=1" \ -H "Authorization: Bearer $OPEN_PLAY_SERVER_KEY" ``` Henüz karakter yoksa beklenen yanıt (`200`): ```json {"items":[],"next_cursor":null} ``` Aynı çağrı TypeScript SDK'sıyla (kurulum için [TypeScript SDK](../sdk/typescript.md#kurulum)): ```ts 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 { 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: | Belirti | Neden | Çözüm | |---|---|---| | `401` | Anahtar yanlış, silinmiş, süresi dolmuş ya da `Bearer` öneki eksik | Anahtarı 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 | | `403` | Anahtar türü bu işlem için yetersiz (istemci anahtarı) | Sunucu anahtarı kullanın | | Bağlantı/TLS hatası | Adres yanlış ya da HTTPS değil | Genel 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](architecture-and-hosting.md#alan-adları-ve-güvenilir-origin). ## 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](architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil)). Aşağıdaki kod kayıtlı oturumu geri yükler; yoksa misafir oturumu açar. `environmentId`, ortamın UUID'sidir. ```ts 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 { 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): ```bash export OPEN_PLAY_ENV="" 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`): ```json { "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": "", "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: ```bash 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](../auth/sessions-and-csrf.md), [Misafir girişi](../auth/guest.md). ## 7. Unity'den ilk oyuncu girişi Unity paketini kurduktan sonra ([Unity SDK](../sdk/unity.md#kurulum)) aşağıdaki bileşeni bir `GameObject`'e ekleyin. `baseUrl` ve `environmentId` alanlarını doldurun. WebGL derlemesinde `baseUrl` sayfayla aynı origin olmalıdır. ```csharp using System; using OpenPlay; using OpenPlay.Session; using OpenPlay.Unity; using UnityEngine; /// /// 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. /// 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](../sdk/unity.md#doğrulanan-platformlar). ## Sırada ne var? - [Kimlik doğrulama](../auth/overview.md): e-posta, Google, hesap bağlama, oturum yönetimi. - [Karakterler](../guides/characters.md), [Ekonomi](../guides/economy.md), [Mağaza ve IAP](../guides/store-iap.md): ilk özelliğinizi bağlayın. - [Webhook'lar](../guides/webhooks.md): olayları sunucunuza imzalı alın. - [Üretim kontrol listesi](../operations/production-checklist.md): canlıya çıkmadan önce. --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/architecture-and-hosting.html # 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ça | Görevi | Dışarı açık mı? | |---|---|---| | **api** | REST, WebSocket ve gerçek zamanlı yüzey; tüm iş mantığı | Yalnız ağ geçidi üzerinden | | **worker** | Arka plan işleri: olay kuyruğu (outbox), webhook teslimatı, zamanlanmış işler, sıralama projeksiyonları | Hayır | | **PostgreSQL** | Kalıcı veri | Hayır | | **Redis** | Oran sınırları, CSRF kullanım kaydı, gerçek zamanlı anahtarlar | Hayır | | **converter** | 3D model dönüştürme (yalnız `amd64`) | Hayır | | **public-gateway** | Oyuncuya ve oyun sunucusuna açılan Caddy ağ geçidi; `/v1/panel/*` yolunu `404` ile keser | Evet (TLS'i önündeki katman sonlandırır) | | **admin-gateway** | Yönetim paneli | Varsayılan olarak yalnız loopback | | **grpc-proxy** | Güvenilen servisler için gRPC | Varsayılan olarak yalnız loopback | ```text 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://` 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:` ve `http://127.0.0.1:` 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): ```caddyfile 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: | Ayar | Değer | |---|---| | TypeScript `baseUrl` | `https://oyun.example.com/open-play` (SDK'lar yol önekini kabul eder) ya da önek kullanmıyorsanız `window.location.origin` | | Unity `BaseUrl` | aynı 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](../sdk/unity.md)) | | `x-crx-env` | ortam 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ı](../reference/rate-limits.md)). - **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ışı | Çerez | Amaç | Özellikler | |---|---|---| | `__Secure-crx.session` | Oyuncu oturumu | `HttpOnly; Secure; SameSite=Lax; Path=/` | | `__Secure-crx.refresh` | Oturum yenileme | aynı | | `__Secure-crx.csrf` | CSRF çift gönderim (double-submit) çerezi | aynı; 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](../sdk/unity.md). Alan adı bağlama adımları: [Ortamlar ve alan adları](../panel/game-settings-environments.md). ## 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](../auth/server-verification.md). ## 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](../guides/webhooks.md) ve [Güvenlik](../operations/security.md). ## Yayın öncesi kontrol [Üretim kontrol listesi](../operations/production-checklist.md) alan adı bağlama, anahtar yönetimi, TLS ve izleme maddelerini toplar. --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/ai-tools.html # 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): ```bash 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: ```json { "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](https://github.com/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: ```bash 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_docs` | Belgelerde 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_doc` | Bir sayfanın tam markdown metnini verir (en çok 40 000 karakter) | `{"page": "auth/guest"}` | | `list_docs` | Sayfa kimliklerini ve başlıklarını listeler; `section` ile tek bölüm | `{"section": "auth"}` | | `search_sdk` | TypeScript ve Unity SDK'larında tür, işlev ve üye adı arar | `{"query": "signInGuest", "sdk": "typescript"}` | | `get_sdk_symbol` | Bir 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:///` adresli bir **kaynak**tı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](../sdk/unity.md) 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](https://llmstxt.org) biçiminde düz metin olarak da yayımlanır: | Dosya | İçerik | |---|---| | [`https://open-play.b4klabs.dev/llms.txt`](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`](https://open-play.b4klabs.dev/llms-full.txt) | İngilizce belgelerin tamamı tek dosyada | | [`https://open-play.b4klabs.dev/docs/tr/llms.txt`](https://open-play.b4klabs.dev/docs/tr/llms.txt) | Türkçe dizin | | [`https://open-play.b4klabs.dev/docs/tr/llms-full.txt`](https://open-play.b4klabs.dev/docs/tr/llms-full.txt) | Tü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ıç](quickstart.md) 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](agents-md.md). --- Source: https://open-play.b4klabs.dev/docs/tr/getting-started/agents-md.html # 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`](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 ``, ``, ``, ``, `` ve `` 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](ai-tools.md)): `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üm | Neyi önler | |---|---| | MCP önce | Tahminle yazılmış endpoint, header ve alan adları | | Anahtarlar ve ortamlar | Sunucu anahtarının istemciye sızması, dev ile prod karışması | | Giriş ve oturum | Misafir 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 verisi | Bilinmeyen 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ım | CDN'in `no-cache` başlığını ezip eski JavaScript'i tutması | | Hatalar ve sınırlar | Hata metnini ayrıştırmak, paylaşılan IP kovası | | Test ve Yap / Yapma | Bitti 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ı ````markdown # AGENTS.md — integrating this game with Open Play > Drop this file in the root of your game repository. Replace every `` 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 ``, prod ``. One environment per stage; data is never shared between them. - Open Play base URL: `` (the API address the browser calls). - Game origins registered per environment: ``, ``. - SDK version (exact, no `^`, no `latest`): ``. ## 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__`) 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 `/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: ""` (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//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ıç](quickstart.md) ve [Üretim kontrol listesi](../operations/production-checklist.md). --- Source: https://open-play.b4klabs.dev/docs/tr/auth/overview.html # 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öntem | Ne zaman? | SDK | Ayrıntı | |---|---|---|---| | **Misafir** | Hesapsız hızlı başlangıç; ilerleme aynı cihazda saklanır | TS: `signInGuest` · Unity: `SignInGuestAsync` | [Misafir girişi](guest.md) | | **E-posta ve parola** | Kalıcı hesap; parola sıfırlama ve değiştirme dahil | TS ve Unity: kayıt, giriş, terfi, parola işlemleri | [E-posta ve parola](email-password.md) | | **Google** (OAuth) | Tek tıkla web girişi; doğrulanmış e-posta varsa mevcut hesaba otomatik bağlanır | TS: `startGoogleOAuth`, `completeGoogleOAuth` · Unity: yok | [Google ile giriş](oauth.md) | | **Yerel platform kanıtı** (Apple, Game Center, Play Games) | Mobil yerel girişler | **SDK yardımcısı henüz yok**; yalnız HTTP ucu | [Google ile giriş › Yerel kanıtlar](oauth.md#yerel-platform-kanıtları) | | **Magic link** | E-postaya tek kullanımlık bağlantı | **SDK yardımcısı henüz yok**; sunucu ucu var | [E-posta ve parola › Magic link](email-password.md#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](sessions-and-csrf.md), [Hesap bağlama](account-linking.md), [Sunucuda doğrulama](server-verification.md). ## 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: ` 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](sessions-and-csrf.md#csrf-çift-gönderim). **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](../getting-started/architecture-and-hosting.md#alan-adları-ve-güvenilir-origin). **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](../reference/errors.md). ## 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): ```json { "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": "", "expires_in_secs": 900 }, "is_new_player": true } ``` | Alan | Anlamı | |---|---| | `player.id` | Kalıcı oyuncu kimliği (UUID). Misafir terfisinde değişmez | | `player.is_guest` | Hesap hâlâ misafir mi | | `tokens.session_token` | Opak oturum belirteci. Çerez yerine `Authorization: Bearer` ile de kullanılabilir | | `tokens.refresh_token` | Yalnız `/v1/auth/refresh` ve `/v1/auth/logout` için | | `tokens.access_token` | Kısa ömürlü (15 dk) EdDSA JWT; oyun sunucunuzun çevrimdışı doğrulaması içindir. Bkz. [Sunucuda doğrulama](server-verification.md) | | `tokens.expires_in_secs` | Eriş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ır | Anahtar | Değer | |---|---|---| | Misafir açma (yeniden giriş dahil) | IP | 30 / 60 sn | | E-posta girişi | IP | 5 / 60 sn | | Başarısız e-posta girişi kilidi | adres + IP | 10 / 15 dk (kilitliyken doğru parola da `429`; başarılı giriş hakkı iade eder) | | Kayıt | IP · adres | 10 / 60 sn · 5 / 15 dk | | Misafir terfisi | IP · misafir oyuncu · adres | 10 / 60 sn · 30 / 60 sn · 5 / 15 dk | | Parola değiştirme | IP · hesap | 10 / 60 sn · 6 / 60 sn | | Parola sıfırlama isteği (forgot) ve magic link | IP · adres | 5 / 60 sn · 5 / 15 dk | | Parola sıfırlama (reset) | IP | 5 / 60 sn (forgot ve magic link ile ortak kova) | | OAuth başlatma | IP | 30 / 60 sn | | OAuth callback | IP | 60 / 60 sn | | Görünen ad değiştirme | oyuncu | 10 / 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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil). ## Uç nokta özeti | Yöntem ve yol | Oturum | Açıklama | Sayfa | |---|---|---|---| | `GET /v1/auth/csrf` | gerekmez | CSRF belirteci üretir | [Oturumlar ve CSRF](sessions-and-csrf.md) | | `POST /v1/auth/sign-in/guest` | gerekmez | Misafir girişi | [Misafir](guest.md) | | `POST /v1/auth/sign-up/email` | gerekmez | Kayıt | [E-posta](email-password.md) | | `POST /v1/auth/sign-in/email` | gerekmez | Giriş | [E-posta](email-password.md) | | `POST /v1/auth/guest/upgrade/email` | misafir oturumu | Misafiri e-postaya terfi ettirir | [E-posta](email-password.md) | | `POST /v1/auth/change-password` | var | Parola değiştirir; tüm oturumlar kapanır | [E-posta](email-password.md) | | `POST /v1/auth/forgot-password` | gerekmez | Sıfırlama e-postası ister | [E-posta](email-password.md) | | `POST /v1/auth/reset-password` | gerekmez | Token ile yeni parola | [E-posta](email-password.md) | | `POST /v1/auth/sign-in/magic-link` · `GET /v1/auth/magic-link/verify` | gerekmez | Magic link | [E-posta](email-password.md#magic-link) | | `GET /v1/auth/oauth/providers` | gerekmez | Ortamda etkin sağlayıcılar | [OAuth](oauth.md) | | `POST /v1/auth/sign-in/oauth/{provider}` | gerekmez | OAuth başlatır (`{url, state}`) | [OAuth](oauth.md) | | `GET /v1/auth/callback/{provider}` · `POST` (form) | gerekmez ya da link için var | OAuth tamamlar | [OAuth](oauth.md) | | `POST /v1/auth/native/{provider}` | gerekmez | Yerel platform kanıtı | [OAuth › Yerel](oauth.md#yerel-platform-kanıtları) | | `POST /v1/auth/link/{provider}` | var | Kimlik bağlamayı başlatır | [Hesap bağlama](account-linking.md) | | `POST /v1/auth/unlink/{provider}` | var | Kimlik bağını çözer | [Hesap bağlama](account-linking.md) | | `GET /v1/auth/identities` | var | Bağlı kimlikler | [Hesap bağlama](account-linking.md) | | `GET /v1/auth/session` | var | Oturum ve profil | [Oturumlar](sessions-and-csrf.md) | | `POST /v1/auth/session/display-name` | var | Görünen ad değiştirir | [Oturumlar](sessions-and-csrf.md) | | `POST /v1/auth/account/delete` | var | Oyuncunun kendi hesabını siler (geri alınamaz) | [Hesabı silme](account-deletion.md) | | `POST /v1/auth/refresh` | yenileme belirteci | Oturumu yeniler (rotasyon) | [Oturumlar](sessions-and-csrf.md) | | `POST /v1/auth/logout` | yenileme belirteci | Oturumu kapatır | [Oturumlar](sessions-and-csrf.md) | | `GET /v1/auth/jwks` | gerekmez | JWT doğrulama anahtarları | [Sunucuda doğrulama](server-verification.md) | `/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](../reference/endpoints.md) 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. --- Source: https://open-play.b4klabs.dev/docs/tr/auth/guest.html # 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](sessions-and-csrf.md#csrf-çift-gönderim)); sonra: ```http POST /v1/auth/sign-in/guest content-type: application/json x-crx-env: x-csrf-token: cookie: __Secure-crx.csrf= {"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ı](overview.md#başarılı-giriş-yanıtı): ```json { "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": "", "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. | Durum | Anlamı | |---|---| | `400` | `bootstrap_token` biçimi bozuk (64 küçük harfli onaltılık karakter değil) ya da JSON bozuk | | `401` | `x-crx-env` yok ya da UUID değil | | `403` | Sı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"}` | | `429` | IP 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](overview.md#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çenek | Davranış | |---|---| | Varsayılan | Tarayıcıda `localStorage` (erişilemezse bellek); SSR/Node'da bellek | | `false` | Hiç saklanmaz; her `signInGuest` yeni bir misafir doğurur | | `GuestBootstrapStore` | Kendi 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. ```ts 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 { 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](../sdk/unity.md#sır-depolama)). `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: - E-posta ve parola: [`upgradeGuest`](email-password.md#misafir-terfisi) - Google: [`linkOAuth`](oauth.md#misafir-ilerlemesini-googlea-bağlama) Terfiden sonra misafir sırrı emekliye ayrılır; cihazdaki sır artık işe yaramaz. --- Source: https://open-play.b4klabs.dev/docs/tr/auth/email-password.html # 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ı](overview.md#her-çağrıda-geçerli-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](guest.md#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 ```http POST /v1/auth/sign-up/email content-type: application/json x-crx-env: x-csrf-token: {"email":"oyuncu@example.com","password":"","display_name":"Ayşe"} ``` `201` ve [ortak giriş zarfı](overview.md#başarılı-giriş-yanıtı) (`is_new_player: true`, `player.is_guest: false`, `player.email` dolu) + oturum çerezleri. | Durum | Anlamı | |---|---| | `400` | E-posta biçimi ya da parola politikası ihlali | | `409` | Adres bu ortamda kayıtlı (gövde adresi tekrar etmez) | | `429` | IP başına 10 / 60 sn ya da adres başına 5 / 15 dk aşıldı | ## Giriş ```http POST /v1/auth/sign-in/email content-type: application/json x-crx-env: x-csrf-token: {"email":"oyuncu@example.com","password":""} ``` `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 `). ```http POST /v1/auth/guest/upgrade/email content-type: application/json x-crx-env: x-csrf-token: {"email":"oyuncu@example.com","password":"","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. | Durum | Anlamı | |---|---| | `400` | E-posta, parola ya da ad doğrulaması | | `401` | Ortam başlığı ya da oturum yok | | `403` | Oturum etkin değil, başka ortama ait ya da CSRF reddi | | `409` | Adres bu ortamda başka bir hesapta kayıtlı; işlem geri alınır, misafir oturumu bozulmaz | | `422` | Oyuncu artık misafir değil (yinelenen ya da eşzamanlı istek) | | `429` | IP 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. ```http POST /v1/auth/change-password content-type: application/json x-crx-env: x-csrf-token: {"old_password":"","new_password":""} ``` `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**. ```http POST /v1/auth/forgot-password content-type: application/json x-crx-env: x-csrf-token: {"email":"oyuncu@example.com"} ``` ```http POST /v1/auth/reset-password content-type: application/json x-crx-env: x-csrf-token: {"token":"<64 hex>","new_password":""} ``` `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 ```ts 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 { 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 { 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 { 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 { 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 { 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 { await client.session.changePassword({ currentPassword, newPassword }); } ``` | TypeScript | Unity | Uç | |---|---|---| | `signUpEmail` | `SignUpEmailAsync` | `sign-up/email` | | `signInEmail` | `SignInEmailAsync` | `sign-in/email` | | `upgradeGuest` | `UpgradeGuestAsync` | `guest/upgrade/email` | | `changePassword({ currentPassword, newPassword })` | `ChangePasswordAsync` | `change-password` (gövdede `old_password`) | | `requestPasswordReset` | `RequestPasswordResetAsync` | `forgot-password` | | `resetPassword` | `ResetPasswordAsync` | `reset-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: ```csharp using System.Threading.Tasks; using OpenPlay; using OpenPlay.Session; using OpenPlay.Support; /// E-posta ve parola akışları. Parola SecretText ile taşınır; günlüğe ve ToString()'e girmez. public static class EmailAuth { public static Task 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 Login(OpenPlayClient client, string email, string password) => client.Session.SignInEmailAsync(new SignInEmailInput { Email = email, Password = new SecretText(password) }); /// Misafir ilerlemesini korur: aynı oyuncu, yeni oturum. Adres kayıtlıysa HTTP 409 gelir. public static async Task 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 }); /// E-postadaki token ile yeni parola; başarıda tüm oturumlar kapanır. public static Task FinishReset(OpenPlayClient client, string token, string newPassword) => client.Session.ResetPasswordAsync(new ResetPasswordInput { Token = new SecretText(token), NewPassword = new SecretText(newPassword) }); /// Görünen ad kuralı: normalize sonrası 2..24 karakter, saatte 10 değişiklik. public static Task Rename(OpenPlayClient client, string displayName) => client.Session.RenameAsync(displayName); } ``` ## Magic link 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. ```http POST /v1/auth/sign-in/magic-link content-type: application/json x-crx-env: x-csrf-token: {"email":"oyuncu@example.com","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=` 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. --- Source: https://open-play.b4klabs.dev/docs/tr/auth/oauth.html # 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: `/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](../panel/game-settings-sign-in.md). ## 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 `/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](overview.md#başarılı-giriş-yanıtı) ve oturum çerezlerini yazar. ### 1. Başlatma ```http POST /v1/auth/sign-in/oauth/google content-type: application/json x-crx-env: x-csrf-token: {"callback_url":"https://oyun.example.com/auth/callback/google"} ``` ```json {"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 ```http GET /v1/auth/callback/google?code=&state= x-crx-env: cookie: ``` 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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil)). `GET` olduğu için CSRF belirteci istemez. | Durum | Anlamı | |---|---| | `403` | `state` 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 | | `409` | `already exists: account_already_linked`: Google kimliği başka bir oyuncuya bağlı; birleştirme yoktur | | `422` | `unprocessable: provider_already_linked`: oyuncuda bu sağlayıcı zaten bağlı | | `429` | Callback 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. ```ts 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 { 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 { 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 { 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 { 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](account-linking.md). ## 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. ```http POST /v1/auth/native/{provider} content-type: application/json x-crx-env: x-csrf-token: ``` | `provider` | Gövde | |---|---| | `apple`, `google` | `{"id_token":"","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. --- Source: https://open-play.b4klabs.dev/docs/tr/auth/sessions-and-csrf.html # 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ığı: | Kanal | Kim kullanır? | Biçim | |---|---|---| | **Çerez** | Tarayıcı oyunları | `__Secure-crx.session=<64 hex>` (`HttpOnly; Secure; SameSite=Lax; Path=/`) | | **Bearer** | Çerezsiz istemciler (komut satırı, bazı yerel istemciler) | `Authorization: Bearer ` (giriş yanıtındaki opak belirteç) | Her oyuncu isteği ayrıca `x-crx-env: ` taşır. Bearer yolunda taşınan **opak oturum belirteci**dir; erişim JWT'si değildir (o, oyun sunucunuzun çevrimdışı doğrulaması içindir: [Sunucuda doğrulama](server-verification.md)). ### Oturumu okuma ```http GET /v1/auth/session x-crx-env: ``` ```json { "environment_id": "0192…", "player_id": "0192…", "session_id": "0192…", "is_guest": false, "active_organization_id": null, "displayName": "Ayşe", "email": "ayse@example.com" } ``` `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 | Öğe | Süre | |---|---| | Oturum ve yenileme penceresi | **30 gün, kayan** | | Mutlak üst sınır | **180 gün** (oturum ailesinin başlangıcından; hiçbir yolla aşılmaz) | | Erişim JWT'si | 15 dakika | | Çerez `Max-Age` | 15 552 000 sn (180 gün); yalnız üst sınırdır, karar sunucudadır | | CSRF çerezi | 15 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) ```http POST /v1/auth/refresh content-type: application/json x-crx-env: x-csrf-token: {} ``` 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ış ```http POST /v1/auth/logout content-type: application/json x-crx-env: x-csrf-token: {} ``` `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":""}`, başlık `x-csrf-token: ` ve `Set-Cookie: __Secure-crx.csrf=`. 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ı). ```bash # 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](../getting-started/architecture-and-hosting.md#alan-adları-ve-güvenilir-origin). - Ç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: | Durum | Anlamı | |---|---| | `signedOut` | Oturum yok | | `authenticating` | Giriş ya da geri yükleme sürüyor | | `authenticated` | Oturum açık; `playerId`, `sessionId`, `isGuest`, `displayName`, `email` dolu | | `refreshing` | Yenileme sürüyor | | `reauthRequired` | Oturum kaybedildi ya da yenilenemedi; yeniden giriş gerekir | | `signingOut` / `logoutUncertain` | Çıkış sürüyor / sonucu belirsiz (yeniden denenebilir) | | `banned` | Hesap 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. ```ts 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 { 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. --- Source: https://open-play.b4klabs.dev/docs/tr/auth/account-linking.html # 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](oauth.md#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 parola | `POST /v1/auth/guest/upgrade/email` · TS `upgradeGuest` · Unity `UpgradeGuestAsync` | [E-posta ve parola](email-password.md#misafir-terfisi) | | Google | `POST /v1/auth/link/google` + callback · TS `linkOAuth` + `completeOAuth` | [Google ile giriş](oauth.md#misafir-ilerlemesini-googlea-bağlama) | 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 ```http GET /v1/auth/identities x-crx-env: ``` ```json { "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](oauth.md#2-tamamlama) 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 ```http POST /v1/auth/unlink/google x-crx-env: x-csrf-token: ``` `204`. Oturum gerekir. Hatalar: | Durum | Anlamı | |---|---| | `400` | Son kalan sağlayıcı kimliği çözülemez (hesap kilitlenmesin diye) | | `404` | Bu 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 | Durum | Sonuç | |---|---| | Misafir, adresi başka hesapta kayıtlı bir e-postayla terfi etmeye çalışır | `409`; misafir oturumu bozulmaz | | Misafir, Google'a bağlanır ve Google kimliği başka oyuncuda | `409 account_already_linked` | | Misafir, Google'a bağlanır ve Google'ın doğruladığı e-posta başka oyuncunun e-postasıyla eşleşir | `409 account_already_linked` (birleştirme yok) | | Kayıtlı oyuncu aynı sağlayıcıyı ikinci kez bağlamaya çalışır | `422 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ğlanma | Reddedilir (`403`) | --- Source: https://open-play.b4klabs.dev/docs/tr/auth/account-deletion.html # 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](sessions-and-csrf.md#csrf-çift-gönderim)); sonra: ```http POST /v1/auth/account/delete content-type: application/json x-crx-env: x-csrf-token: { "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: ```json { "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: | Veri | Sonuç | |---|---| | Oturumlar | Hepsi 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 | Durum | Anlamı | |---|---| | `204` | Hesap silindi. | | `401` | Oturum 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övdesiz | CSRF reddi. | | `403` `recent_sign_in_required` | Yeniden giriş gerekli; hiçbir şey silinmedi. | | `422` | `confirm` eksik ya da `delete_my_account` değil. | | `429` | Oyuncu başına saatte 5 deneme aşıldı. | ## SDK TypeScript SDK'sında: ```ts 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 - [Oturumlar ve CSRF](sessions-and-csrf.md) - [Misafir girişi](guest.md) - [Oran sınırları](../reference/rate-limits.md) --- Source: https://open-play.b4klabs.dev/docs/tr/auth/server-verification.html # 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":""}`. Yük: | Alan | Anlamı | |---|---| | `sub` | Oyuncu kimliği (UUID) | | `env` | Ortam kimliği (UUID) | | `sid` | Oturum kimliği (UUID) | | `org` | Etkin 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) ```bash curl -sS "$OPEN_PLAY_URL/v1/auth/jwks" ``` ```json {"keys":[{"kty":"OKP","crv":"Ed25519","kid":"","use":"sig","alg":"EdDSA","x":""}]} ``` 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): ```ts 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; } 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 { 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. ```bash 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 istek | Yok (JWKS önbellekte) | Her doğrulamada bir istek | | İptal/ban görür | Hayı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ız | Ortam, istekteki `x-crx-env` ile sınanır | | Ne zaman? | Çok yüksek istek hacmi, iptal gecikmesi kabul edilebilir | Varsayılan öneri; oyuncu kimliği kritik işlemler | Sunucu-sunucu çağrılarında oyuncu yerine **sunucu anahtarı** kullanılır ([Kavramlar](../getting-started/concepts.md#api-anahtarları-sunucu-ve-istemci)). --- Source: https://open-play.b4klabs.dev/docs/tr/sdk/typescript.html # 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/](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: ```ini @ferdiunal:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} ``` 2. **Paketi kurun:** ```bash export GITHUB_TOKEN="" npm install @ferdiunal/open-play-ts@0.1.15 ``` `pnpm` aynı `.npmrc` ile çalışır. Belirteci depoya, imaja ya da CI günlüğüne koymayın. 3. **İçe aktarın:** ```ts 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 | İçerik | Ortam | |---|---|---| | `@ferdiunal/open-play-ts` | `CrxClient`, `createPlayerClient`, `createGameClient`, hata ve sayı yardımcıları, katalog/mağaza/sıralama tipleri | Tarayıcı ve Node | | `.../player` | Oyuncu istemcisi ve eklenti bağlama oluşturma tipleri | Tarayıcı ve Node | | `.../game` | `GameClient` | Tarayıcı ve Node | | `.../realtime` | Gerçek zamanlı istemci tipleri | Tarayıcı ve Node | | `.../browser` | IndexedDB günlüğü ve sekme koordinasyonu (`createBrowserAdapter`) | Tarayıcı | | `.../codegen` | Bağlama ve tip üreticisi | **Yalnı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. ```ts 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. ```ts 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 { 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 { 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](../reference/idempotency-and-concurrency.md)). - 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: | Üye | Ne yapar? | |---|---| | `session` | Giriş, oturum durumu, yenileme, çıkış: [Kimlik doğrulama](../auth/overview.md) | | `plugin(binding)` · `publicPlugin(binding)` | Eklenti komut/sorgu/modülleri: [Eklenti bağlamaları](../plugins/bindings.md) | | `realtime` | Gerçek zamanlı soket (aşağıda) | | `catalog` | Panelden 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](../guides/player-data.md) | | `notifications` | Cihaz kaydı, gelen kutusu, web push | | `dispose()` | Yerel kaynakları bırakır (sunucu oturumunu kapatmaz) | İlk kullanım için [Hızlı başlangıç](../getting-started/quickstart.md#6-tarayıcıdan-ilk-oyuncu-girişi-misafir). Oturum durumları için [Oturumlar ve CSRF](../auth/sessions-and-csrf.md#sdk-ve-oturum-durumları). `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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil)). ### `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. ```ts 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 } { 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ı](../realtime/overview.md) bölümündedir. ```ts 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) => 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 { 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](../reference/endpoints.md) bulur, HTTP çağrısını kendiniz yaparsınız (anahtar ve başlık kuralları aynıdır). | Modül / alan | Tipli istemci | Nerede | |---|---|---| | Karakter tanımları, özellikler (trait), seviye eğrileri, karakterler, komutlar, geçmiş, istatistik | **Var** | `CrxClient` (sunucu anahtarı) | | Kurallar | **Var** | `CrxClient` (`createRule`, `listRules`, `dryRunRule`, …) | | Webhook uç kaydı (oluştur/listele/oku/sil) | **Var** | `CrxClient`; imza doğrulaması sizin kodunuzdur ([Webhook'lar](../guides/webhooks.md)) | | Klanlar | **Var** | `CrxClient` (20 işlem) | | Katalog (eşya/veri/model) ve içerik grupları | **Var** | `PlayerClient.catalog` (oyuncu), `CrxClient.catalog` (sunucu) | | Ödül tablosu kaydı ve verme | **Var** | `CrxClient.createRewardTable`, `getRewardTable`, `grantRewardTable` | | Mağaza vitrini ve satın alma | **Var** | `PlayerClient.store` | | Çekirdek skor tablosu okuma | **Var** | `PlayerClient.leaderboard` | | Bildirimler: cihaz kaydı, gelen kutusu, web push | **Var** | `PlayerClient.notifications` | | Eklentiler ve gerçek zamanlı | **Var** (şemadan üretilen bağlamayla) | `PlayerClient.plugin`, `GameClient` | | Eşyalar ve envanter | Yok | Ham REST | | Para birimleri, cüzdanlar, transferler, defter | Yok | Ham REST | | Takas (trading), tüccarlar | Yok | Ham REST | | Mağaza yönetimi, IAP doğrulama | Yok | Ham REST | | Görev, VIP, günlük giriş ve NPC ödülleri | Yok | Ham REST | | Partiler | Yok | Ham REST | | Sunucu tarafı skor tabloları, turnuvalar | Yok | Ham REST | | Sohbet | Yok | Ham REST/WebSocket | | Sahneler, varlıklar | Yok | Ham REST | | Magic link, hesap bağını çözme, yerel platform girişleri | Yok | Ham REST ([Kimlik doğrulama](../auth/overview.md)) | `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**. | `ErrorKind` | Anlamı | |---|---| | `Http` | Sunucu hata durumu döndürdü (`status` dolu) | | `Network` | Bağlantı koptu; mutasyon uygulanmış olabilir | | `Timeout` | Toplam süre sınırı doldu | | `Cancelled` | `signal` ile iptal | | `Contract` | Girdi ya da yanıt sözleşmeye uymuyor | | `Configuration` | Güvensiz ya da bozuk ayar | | `Auth` | Oyuncu oturumu yeniden doğrulama istiyor | | `UnknownOutcome` | İstek gönderildi ama sonuç bilinmiyor; kör tekrar yapmayın, sonucu sorgulayın | | `Storage` | Günlük, tarayıcı koordinasyonu ya da OAuth bağ deposu kullanılamıyor | | `Realtime` · `Backpressure` · `UpgradeRequired` · `ScopeChanged` · `Disposed` | Gerç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ış](../auth/overview.md#banlı-hesaplar)). ## 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](../reference/numbers-and-encoding.md). ## 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): ```bash # 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ı](../plugins/bindings.md) 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](../auth/server-verification.md)). - 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. --- Source: https://open-play.b4klabs.dev/docs/tr/sdk/unity.html # 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](#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/](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. ```toml [npmAuth."https://npm.pkg.github.com/@ferdiunal"] token = "" alwaysAuth = true ``` 2. **Manifest.** Oyun projenizin `Packages/manifest.json` dosyasına kapsamlı kayıt defterini ve bağımlılığı ekleyin: ```json { "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:/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](../getting-started/quickstart.md#7-unityden-ilk-oyuncu-girişi) bileşen kayıtlı oturumu geri yükler, yoksa misafir açar. `OpenPlayOptions` ayarları: | Ayar | Anlamı | |---|---| | `BaseUrl` | API kökü. Mutlak `https` (geliştirmede loopback `http`); sorgu, parça ve kullanıcı bilgisi içeremez | | `EnvironmentId` | Ortam kimliği (`x-crx-env`); boş olamaz | | `AllowInsecureTransport` | Yalnız geliştirme: loopback adreslerinde (`localhost`, `127.0.0.1`, `::1`) düz `http`/`ws`. Varsayılan `false` | | `NativeOrigin` | Yerel 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 | | `PageOrigin` | Yalnız WebGL: sayfanın adresi; `BaseUrl` ile aynı origin olmalıdır (`OpenPlayUnity.Options` doldurur) | | `SecretStore` | Misafir sırrı ve yenileme belirtecinin kalıcı deposu (aşağıya bakın) | | `TimeoutMs` | Toplam istek süresi: 1–120 000, varsayılan 15 000 | | `MaxResponseBytes` | En büyük yanıt: 1 bayt–16 MiB, varsayılan 2 MiB | | `UserAgent` | Boşsa hiç gönderilmez | | `Realtime` | Gerç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öntem | Ne yapar? | |---|---| | `RestoreAsync()` | Kayıtlı oturumu geri yükler; yoksa misafir **yaratmaz** | | `SignInGuestAsync()` | Misafir girişi ([Misafir](../auth/guest.md)) | | `SignUpEmailAsync`, `SignInEmailAsync`, `UpgradeGuestAsync` | E-posta kayıt/giriş/terfi ([E-posta](../auth/email-password.md)) | | `ChangePasswordAsync`, `RequestPasswordResetAsync`, `ResetPasswordAsync` | Parola işlemleri | | `RenameAsync(displayName)` | Görünen ad değiştirme (2–24 karakter, saatte 10) | | `RefreshAsync()` · `LogoutAsync()` | Yenileme ve çıkış | | `StateChanged` olayı · `State` | `SessionState`: `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](../auth/email-password.md#sdk) 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: ```csharp using System.Threading; using System.Threading.Tasks; using OpenPlay.Support; /// Platformunuzun güvenli deposunu (iOS Keychain, Android Keystore, masaüstü kimlik deposu) temsil eden kendi arayüzünüz. public interface IPlatformKeystore { string Read(string key); void Write(string key, string value); void Delete(string key); } /// /// 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); /// public sealed class KeystoreSecretStore : ISecretStore { private readonly IPlatformKeystore _keystore; public KeystoreSecretStore(IPlatformKeystore keystore) { _keystore = keystore; } public Task 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. ```csharp using System.Threading.Tasks; using OpenPlay; using OpenPlay.PackageRide; using OpenPlay.Plugins; /// /// 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. /// 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ı](../realtime/overview.md). ```csharp using System; using System.Threading.Tasks; using OpenPlay; using OpenPlay.Realtime; using OpenPlay.Unity; /// /// 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. /// 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); } /// 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. public static Task 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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil)). 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. | Hedef | Düzey | Not | |---|---|---| | Unity 6000.6.7f1 Editör (Linux) | R | Mono ve IL2CPP ölçüldü | | Windows | B | doğrulanmadı | | macOS | B | doğrulanmadı | | Android | B | doğrulanmadı (telefon ölçümü yapılmadı) | | iOS | — | doğrulanmadı | | WebGL (aynı origin) | R | BasicLogin 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](typescript.md#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](../auth/server-verification.md)). - 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. --- Source: https://open-play.b4klabs.dev/docs/tr/guides/characters.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Karakter şablonu, özellik (trait) ve seviye eğrisi tanımlamak | [Karakter şablonları](../panel/character-templates.md) | | Karakterleri listelemek, birini açıp durumunu ve geçmişini görmek | [Karakterler](../panel/characters.md), [Karakter profili](../panel/character-profile.md) | | Oyuncunun hesabını ve karakterlerini birlikte görmek | [Oyuncular](../panel/players.md), [Oyuncu profili](../panel/player-profile.md) | | Olay tetikli otomasyon yazmak | [Oyun kuralları](game-rules.md) | ## 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](../reference/numbers-and-encoding.md). ## 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>_ ``` Anahtarın türü önemlidir (bkz. [Kavramlar](../getting-started/concepts.md)): **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 yol | Anahtar | Ne yapar | |---|---|---| | `POST /v1/definitions` | herhangi (aşağıdaki not) | Şablonu yeni sürüm olarak kaydeder | | `GET /v1/definitions/{key}` | herhangi | Güncel şablonu ve çözülmüş şemayı okur | | `POST /v1/traits` | herhangi (not) | Özellik kaydeder | | `POST /v1/level-curves` | herhangi (not) | Seviye eğrisi kaydeder | | `GET /v1/level-curves/{key}` | herhangi | Eğriyi okur | | `POST /v1/characters` | herhangi | Şablondan karakter oluşturur | | `GET /v1/characters` | herhangi | Karakterleri listeler (`definition`, `cursor`, `limit`) | | `GET /v1/characters/{id}` | herhangi | Karakteri durumuyla okur | | `DELETE /v1/characters/{id}` | herhangi | Karakteri siler (yumuşak silme, `204`) | | `POST /v1/characters/{id}/commands` | komuta göre | Komut çalıştırır | | `GET /v1/characters/{id}/stats` | herhangi | Etkin değerleri ve hesap dökümünü verir | | `GET /v1/characters/{id}/events` | herhangi | Olay günlüğü (`cursor`, `limit`) | | `GET /v1/characters/{id}/timeline` | herhangi | Okunur zaman çizelgesi | | `GET /v1/characters/{id}/state?at=` | herhangi | Geçmiş bir andaki durumu yeniden kurar | Kimlik ve modül kuralları ile hata gövdeleri: [Hatalar](../reference/errors.md). Sayfalama: [Sayfalama](../reference/pagination.md) (`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>_ Content-Type: application/json { "definition": "warrior", "external_id": "oyuncu-1234" } ``` ```json { "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>_ Content-Type: application/json Idempotency-Key: gorev-17-oyuncu-1234 If-Match: 1 { "type": "add_xp", "amount": 250 } ``` ```json { "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: | `type` | Gövde alanları | Anahtar | |---|---|---| | `set_flag` | `key`, `value` (bool) | istemci veya sunucu | | `set_attribute` | `key`, `value` (tam sayı) | sunucu | | `add_resource` | `key`, `delta` (tam sayı, eksi olabilir) | sunucu | | `add_modifier` | `stat`, `operation` (`flat_add`, `flat_sub`, `percent_add`, `percent_mul`, `clamp`), `value`, `priority`, `source_kind` (`item`, `buff`, `skill`, `curse`, `quest`), `source_id`, `expires_at` | sunucu | | `remove_modifier` | `source_kind`, `source_id` | sunucu | | `add_xp` | `amount` | sunucu | | `rollback` | `to_version` | sunucu | İ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](../reference/idempotency-and-concurrency.md). ### 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](webhooks.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `403 permission denied` | İstemci anahtarıyla `set_flag` dışında komut | Komutu sunucudan, sunucu anahtarıyla gönderin | | `409 version conflict: expected 3, actual 4` | `If-Match` bayat | Karakteri yeniden okuyun, yeni sürümle tekrar gönderin | | `409 already exists` | Aynı `external_id` var | Var olan karakteri `GET /v1/characters` ile bulun | | `422 idempotency mismatch` | Aynı anahtar, farklı gövde | Yeni 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önderildi | Yalnız sayı gönderin | | `404` + `module_disabled` | `characters` 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. ```ts /** 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 { 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 - [Kavramlar: anahtar türleri ve oturumlar](../getting-started/concepts.md) - [Oyun kuralları ve otomasyonlar](game-rules.md) - [Envanter ve eşyalar](inventory-items.md) - [Hatalar](../reference/errors.md), [Idempotency ve eşzamanlılık](../reference/idempotency-and-concurrency.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/inventory-items.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Eşya tanımlarını ve oyun verilerini (yerleşik eşya grubu) düzenlemek | [İçerik](../panel/content.md) | | Eşyaya 3B model bağlamak | [3D modeller](../panel/models.md), [Model profili](../panel/model-profile.md) | | Bir oyuncunun envanterini ve cüzdanını görmek | [Oyuncu profili](../panel/player-profile.md) | | Eşyayı mağazada satmak | [Mağaza](../panel/store.md) | ## 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`):** `:` 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>_ ``` Oyuncu tarafı iki okuma ucu sunar; bunlar **oyuncu oturumuyla** çağrılır (çerez ya da oturum belirteci, `x-crx-env: ` başlığıyla): | Yöntem ve yol | Kimlik | Ne yapar | |---|---|---| | `POST /v1/item-definitions` | sunucu 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/items` | sunucu anahtarı | Eşya üretir (mint) | | `GET /v1/items?owner=player:` | API anahtarı | Sahibin eşyalarını listeler (`cursor`, `limit`) | | `GET /v1/items/{id}` | API anahtarı | Tek örneği okur | | `POST /v1/items/{id}/consume` | API anahtarı | Adet tüketir | | `POST /v1/items/{id}/destroy` | sunucu anahtarı | Eşyayı yok eder | | `POST /v1/items/{id}/transfer` | API anahtarı | Sahipliği devreder | | `GET /v1/inventories/{owner}` | API anahtarı | Envanteri ve dolu yuvaları okur | | `POST /v1/inventories/{owner}/move` | API anahtarı | Eşyayı yuvaya yerleştirir ya da taşır | | `POST /v1/inventories/{owner}/split` | API anahtarı | Yığından adet ayırıp yeni yuvaya koyar | | `POST /v1/inventories/{owner}/stack` | API anahtarı | İki yığını birleştirir | | `POST /v1/inventories/{owner}/equip` | API anahtarı | Eşyayı kuşanma yuvasına koyar | | `POST /v1/inventories/{owner}/unequip` | API anahtarı | Kuşanılmış eşyayı başka yuvaya indirir | | `GET /v1/player/inventory` | oyuncu oturumu | Oyuncunun kendi envanteri (salt okunur) | | `GET /v1/player/items` | oyuncu oturumu | Oyuncunun kendi eşyaları (salt okunur) | | `GET /v1/catalog/items`, `GET /v1/catalog/items/{key}` | sunucu anahtarı ya da oyuncu oturumu | Eşya kataloğu ([aşağıda](#eşya-kataloğu)) | > **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](../sdk/typescript.md). > **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>_ 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" } ``` ```json { "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övde | Not | |---|---|---| | `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:"}` | `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>_ 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 | |---|---| | `move` | `item_instance_id`, `to_slot` | | `equip`, `unequip` | `item_instance_id`, `to_slot` | | `split` | `from_instance_id`, `amount`, `to_slot` | | `stack` | `from_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ı](content-groups.md#katalog-eşya-ve-karakter) ve [Scene, görsel ve 3B modeller](scenes-assets-models.md). ## 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](../reference/idempotency-and-concurrency.md). ## 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ı](../reference/webhook-events.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `400` (anahtar yok) | `Idempotency-Key` başlığı eksik | Her 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 | | `409` | `If-Match` bayat ya da eşya başka envanterde | Yeniden okuyup tekrar deneyin | | `422` yetersiz adet | `amount` mevcut adetten büyük | Adedi düşürün | | `422` takas | `tradeable: false` | Tanımda takası açın | | `422` yuva dolu | Hedef yuva dolu | Boş 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): ```ts /** 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 { const headers: Record = { 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 { 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: cüzdanlar, para birimleri, tüccar ve takas](economy.md) - [Mağaza ve uygulama içi satın alma](store-iap.md) - [İçerik grupları](content-groups.md) - [Hatalar](../reference/errors.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/economy.html # 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ız | Panel sayfası | |---|---| | Para birimi oluşturmak, ikon, kuruş hanesi ve arz tavanı ayarlamak | [Para birimleri](../panel/currencies.md), [Para birimi profili](../panel/currency-profile.md) | | Günlük özet, bakiye sıralaması, takaslar ve cüzdan sorgusu görmek | [Ekonomi raporları](../panel/economy.md) | | Oyuncunun cüzdanını ve hareketlerini görmek | [Oyuncu profili](../panel/player-profile.md) | | Ödül tablosu kurmak | [Ödül tabloları](../panel/reward-tables.md) | | Ürünü paraya bağlayıp satmak | [Mağaza](../panel/store.md) | ## 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 `:` 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: | Yol | Ne 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](rewards.md) | | **Günlük giriş ödülü, görev, NPC ödülü** | [Ödüller](rewards.md) | | **Uygulama içi satın alma** (IAP) | Gerçek parayla alınan oyun parası — bkz. [Mağaza](store-iap.md) | | **Tüccara eşya satmak** (`POST /v1/merchants/{key}/sell`) | Oyuncu eşyayı NPC'ye satar | | **Parti ödül dağıtımı, klan hazinesi** | [Sosyal](social.md) | | **Panelden elle verme** | Operatö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>_` 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 yol | Anahtar | Ne yapar | |---|---|---| | `POST /v1/currencies` | sunucu | Para birimini yeni sürüm olarak kaydeder | | `GET /v1/wallets?owner=player:` | API anahtarı | Sahibin cüzdanları (para birimi başına bir satır) | | `POST /v1/transfers` | API anahtarı | Cüzdandan cüzdana transfer (`Idempotency-Key` zorunlu) | | `GET /v1/ledger?wallet_id=` | API anahtarı | Cüzdanın defter akışı (`cursor`, `limit`) | | `POST /v1/merchants` | sunucu | Tü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}/buy` | API anahtarı | Oyuncu tüccardan eşya alır | | `POST /v1/merchants/{key}/sell` | API anahtarı | Oyuncu tüccara eşya satar | | `POST /v1/trades` | API 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}/offer` | API anahtarı | Bacakları kilitler | | `POST /v1/trades/{id}/accept` | API anahtarı | Son onay; atomik takas | | `POST /v1/trades/{id}/cancel` | API anahtarı | İptal; kilitler açılır, para iade edilir | | `GET /v1/economy/faucet-sink`, `/flags`, `/merchant-gap`, `/top-holders` | sunucu | Günlük ekonomi raporları | | `GET /v1/auth/admin/player-wallet-movements?player_id=` | sunucu | Oyuncunun 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](../sdk/typescript.md). > **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>_ Content-Type: application/json { "key": "gold", "name": "Altın", "transferable": true, "max_supply": 1000000000 } ``` ```json { "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>_ Content-Type: application/json Idempotency-Key: transfer-2026-11-03-0001 { "from_wallet": "0192f1…", "to_wallet": "0192f2…", "amount": 500, "reference": { "neden": "hediye" } } ``` ```json { "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>_ 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](webhooks.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `400` (`Idempotency-Key`) | Başlık eksik | Her yazıda anahtar gönderin | | `403` | İstemci anahtarıyla para birimi/tüccar kaydı | Sunucu anahtarı kullanın | | `404` cüzdan | Transferde verilen cüzdan kimliği yok | Kimliğ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 bakiye | Bakiye tutardan az | Bakiyeyi `GET /v1/wallets` ile okuyun | | `422` `transferable=false` | Para birimi transfer edilemez | Para biriminde transferi açın ya da başka yol kullanın | | `409` stok | Tüccar stoğu yetmiyor | Adedi 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ı](../panel/reward-tables.md)) ya da `POST /v1/reward-tables` ile önceden kurulmuş olmalıdır. ```ts /** 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 { 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 { 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 - [Ödüller](rewards.md) · [Mağaza ve IAP](store-iap.md) · [Envanter ve eşyalar](inventory-items.md) · [Sosyal](social.md) - [Idempotency ve eşzamanlılık](../reference/idempotency-and-concurrency.md) · [Sayılar ve kodlama](../reference/numbers-and-encoding.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/store-iap.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Vitrin ve ürün oluşturmak, fiyat, stok, görünürlük penceresi ve oyuncu limiti ayarlamak | [Mağaza](../panel/store.md), [Ürün](../panel/store-product.md) | | Fiyat için para birimi tanımlamak | [Para birimleri](../panel/currencies.md) | | Satılacak eşyayı tanımlamak | [İçerik](../panel/content.md) | 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](#sınırlar-ve-bilinen-kısıtlar). ## 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: `): 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](../auth/sessions-and-csrf.md). - **Sunucu anahtarı** (`Authorization: Bearer crx_live_<önek>_`): katalog yönetimi, oyuncu önizlemesi, rezervasyon ve ödeme tamamlama. | Yöntem ve yol | Kimlik | Ne yapar | |---|---|---| | `GET /v1/store/pages[?page=&at=now]` | oyuncu oturumu | Oyuncunun görebildiği vitrinler; `page` tek vitrine daraltır | | `POST /v1/player/store/listings/{listing_id}/buy` | oyuncu oturumu | Oyuncu kendi adına satın alır | | `GET /v1/store/catalog` | sunucu 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/reorder` | sunucu anahtarı | Vitrin yönetimi | | `POST /v1/store/pages/{page_id}/listings`, `PUT/DELETE /v1/store/listings/{id}`, `POST …/listings/reorder` | sunucu anahtarı | Ürün yönetimi | | `POST /v1/store/listings/{listing_id}/reserve` | sunucu anahtarı | Oyuncu için hak ayırır (ödeme değildir) | | `POST /v1/store/reservations/{id}/checkout/currency` | sunucu anahtarı | Oyun parasıyla ödemeyi tamamlar | | `POST /v1/store/reservations/{id}/checkout/iap` | sunucu anahtarı | Doğrulanmış IAP ile ödemeyi tamamlar | | `POST /v1/store/reservations/{id}/cancel` | sunucu anahtarı | `reserved` rezervasyonu iptal eder (iade değildir) | | `POST /v1/iap/verify` | API 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](../reference/errors.md). ### Vitrini okumak ``` GET /v1/store/pages?page=garaj x-crx-env: 0192f000-0000-7000-8000-00000000e001 ``` ```json { "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: Idempotency-Key: buy-oyuncu-1234-0001 Content-Type: application/json { "quantity": 1, "expected_version": 3 } ``` ```json { "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": "..."}`: | `code` | HTTP | Anlamı | |---|---|---| | `not_visible` | 404 | Ürün bu oyuncuya görünmüyor ya da yok | | `listing_changed` | 409 | Ürün siz bakarken değişti (`expected_version` bayat); vitrini yenileyin | | `out_of_stock` | 422 | Stok bitti | | `limit_reached` | 422 | Oyuncu başına limit doldu | | `insufficient_funds` | 422 | Bakiye yetersiz | | `inventory_full` | 422 | Envanter 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>_ 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>_ Content-Type: application/json { "player_id": "0192f0f2-…" } ``` ```json { "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>_ Idempotency-Key: iap-oyuncu-1234-tx-abc Content-Type: application/json { "platform": "app_store", "product_sku": "gems_500", "credential": "", "player_id": "0192f0f2-…" } ``` ```json { "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](../reference/idempotency-and-concurrency.md). ## 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](webhooks.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `409 listing_changed` | Vitrinde okuduğunuz ürün sürümü eskidi | Vitrini 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_funds` | Bakiye yetersiz | Bakiye gösterin; ödül ya da IAP ile para verin | | `400` (bilinmeyen alan) | Satın alma gövdesine fazladan alan eklendi | Yalnız `quantity` ve `expected_version` gönderin | | `400` (checkout) | `Idempotency-Key`/`If-Match` gönderildi | Checkout uçlarında bu başlıkları kaldırın | | `403` (satın alma) | CSRF çifti ya da `Origin` reddi | CSRF belirtecini yenileyin; bkz. [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) | | `403` (iap/verify) | Sahte ya da imzası geçersiz makbuz | Makbuzu platformdan yeniden alın | | `422` (iap) | Platform yapılandırılmamış ya da `product_sku` uyuşmuyor | Operatörle iletişime geçin; sku'yu kontrol edin | ## Çalışan örnek Vitrini okuyup ilk ürünü satın alma örneği [Ekonomi](economy.md#çalışan-örnek-para-ver-sonra-ürün-satın-al) 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 - [Ekonomi](economy.md) · [Envanter ve eşyalar](inventory-items.md) · [Ödüller](rewards.md) · [Webhook'lar](webhooks.md) - [TypeScript SDK](../sdk/typescript.md) · [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/rewards.html # Ö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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Ödül tablosu kurmak, satır şanslarını ve garanti ödülleri düzenlemek, sürümleri görmek | [Ödül tabloları](../panel/reward-tables.md), [Ödül tablosu profili](../panel/reward-table-profile.md) | | Ödülde kullanılacak para birimini tanımlamak | [Para birimleri](../panel/currencies.md) | | Ödülde kullanılacak eşyayı tanımlamak | [İçerik](../panel/content.md) | | Olay tetikli otomasyonla ödül vermek | [Oyun kuralları](game-rules.md) | ## 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>_ ``` | Yöntem ve yol | Ne 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}/grant` | Tablodan idempotent ödül verir | | `GET /v1/login-rewards/{player_id}` | Bugünün giriş ödülü durumunu okur | | `POST /v1/login-rewards/{player_id}/claim` | Günlük giriş ödülünü verir | | `POST /v1/quests` | Görev tanımı kaydeder | | `GET /v1/quests`, `GET /v1/quests/{key}` | Görevleri listeler / okur | | `POST /v1/quests/{key}/claim` | Görev dönemi ödülünü verir | | `GET /v1/npc-rewards/{key}/eligibility` | NPC ödülü uygunluğunu okur (yazmaz) | | `POST /v1/npc-rewards/{key}/claim` | NPC ödülünü verir | | `GET /v1/vip/{player_id}` | VIP katmanını okur | | `POST /v1/vip/{player_id}/cards/{item_instance_id}/use` | VIP 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](../sdk/typescript.md). ### Ödül tablosu kaydetmek ``` POST /v1/reward-tables Authorization: Bearer crx_live_<önek>_ 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>_ Idempotency-Key: welcome-oyuncu-1234 Content-Type: application/json { "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "source": { "type": "manual", "operator_id": "game-server" } } ``` ```json { "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: | `type` | Ek alanlar | |---|---| | `manual` | `operator_id` | | `content_entry` | `group`, `entry` | | `quest` | `quest_key` | | `npc_reward` | `npc_key` | | `tournament` | `tournament_id`, `placement` | | `leaderboard_season` | `leaderboard_key`, `season` | | `rule` | `rule_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ı](../panel/game-settings-environments.md)) 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: ```json { "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](../reference/idempotency-and-concurrency.md). ## 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](webhooks.md). ## 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 | Belirti | Neden | Çö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 dolu | Verilecek eşya için boş yuva yok | Oyuncuya 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 mismatch` | Aynı anahtar, farklı gövde | Yeni anahtar kullanın | | `404` tablo yok | `key` yanlış ya da tablo henüz kaydedilmedi | `GET /v1/reward-tables/{key}` ile doğrulayın | ## Çalışan örnek Hoş geldin tablosundan para verme ve ardından mağazadan alma akışı [Ekonomi](economy.md#çalışan-örnek-para-ver-sonra-ürün-satın-al) 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 - [Ekonomi](economy.md) · [Envanter ve eşyalar](inventory-items.md) · [Karakterler](characters.md) · [Oyun kuralları](game-rules.md) - [Hatalar](../reference/errors.md) · [Webhook'lar](webhooks.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/social.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Klanları listelemek, üyelik, davet, başvuru, kasa ve komut geçmişini görmek | [Klanlar](../panel/clans.md) | | Partileri, üyelerini, ganimet kuralını ve ödül dağıtımlarını görmek | [Partiler](../panel/parties.md) | | Klan ve parti sohbeti | [Sohbet](chat.md), [Şehir sohbeti](../panel/scene-chat.md) | | Klan hazinesinde kullanılan para birimi | [Para birimleri](../panel/currencies.md) | ## 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](characters.md)). - **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](economy.md) `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>_ ``` İstemci anahtarı ve oyuncu oturumu reddedilir (`403`). ### Klan | Yöntem ve yol | Gövde | Ne yapar | |---|---|---| | `GET /v1/clan-settings`, `PATCH /v1/clan-settings` | `max_members`, `request_ttl_seconds`, `officer_can_withdraw`, `level_curve_key` | Ortam ayarlarını okur / günceller | | `GET /v1/clans` | — | Klanları listeler | | `POST /v1/clans` | `leader_player_id`, `name`, `tag` | Klan kurar | | `GET /v1/clans/{clan_id}` | — | Klanı üyeleriyle okur | | `PATCH /v1/clans/{clan_id}` | `actor_player_id`, `name`, `tag` | Ad ve etiketi günceller | | `POST /v1/clans/{clan_id}/invites` | `inviter_player_id`, `invitee_player_id` | Davet gönderir | | `GET /v1/clan-invites/{invite_id}`, `POST …/respond` | `actor_player_id`, `decision` (`accept`, `decline`, `revoke`, `expire`) | Daveti okur / yanıtlar | | `POST /v1/clans/{clan_id}/applications` | `applicant_player_id` | Başvuru açar | | `GET /v1/clan-applications/{application_id}`, `POST …/resolve` | `actor_player_id`, `decision` (`accept`, `reject`, `cancel`, `expire`) | Başvuruyu okur / çözer | | `POST /v1/clans/{clan_id}/leave` | `actor_player_id` | Üye ayrılır | | `POST /v1/clans/{clan_id}/members/kick` | `actor_player_id`, `target_player_id` | Üyeyi çıkarır | | `POST /v1/clans/{clan_id}/members/role` | `actor_player_id`, `target_player_id`, `role` | Rol değiştirir | | `POST /v1/clans/{clan_id}/leadership` | `actor_player_id`, `target_player_id` | Liderliği devreder | | `POST /v1/clans/{clan_id}/disband` | `actor_player_id` | Klanı dağıtır | | `POST /v1/clans/{clan_id}/treasury/deposit`, `…/withdraw` | `actor_player_id`, `currency_id`, `amount` | Hazineye yatırır / çeker | | `POST /v1/clans/{clan_id}/xp` | `amount` | Klana XP ekler | Klan yazma komutlarının hepsinde `Idempotency-Key` **zorunludur**. Yanıt hep aynı zarftadır: `{ "type": "clan" | "invite" | "application" | "settings" | "treasury" | "xp", "": { … }, "replayed": false }`. Örneğin klan kurma: ``` POST /v1/clans Authorization: Bearer crx_live_<önek>_ 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](../sdk/typescript.md). ### Parti | Yöntem ve yol | Gövde | Ne yapar | |---|---|---| | `POST /v1/parties` | `leader_player_id`, `capacity`, `loot_rule` | Parti 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`, `capacity` | Kapasiteyi günceller | | `POST /v1/parties/{party_id}/invites` | `inviter_player_id`, `invitee_player_id`, `share_bp` | Davet gönderir | | `POST /v1/party-invites/{invite_id}/accept` | `actor_player_id` | Daveti kabul eder | | `POST /v1/parties/{party_id}/leave` | `actor_player_id` | Üye ayrılır | | `POST /v1/parties/{party_id}/disband` | `actor_player_id` | Partiyi dağıtır | | `POST /v1/parties/{party_id}/rewards` | `currency_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](../reference/idempotency-and-concurrency.md). ## 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ı](../reference/webhook-events.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `403` | Oyuncu 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 | | `400` | `Idempotency-Key` eksik; klan adı/etiketi uzunluk ya da karakter kuralına uymuyor; XP ya da tutar pozitif değil; parti kapasitesi pozitif değil | Girdiyi düzeltin | | `409` | Aynı 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 | | `422` | Parti kapasitesi/pay kuralları (kapasite aşımı, `weighted` pay toplamı 10000 değil, yinelenen oyuncu) ya da aynı anahtar farklı gövde | Girdiyi düzeltin; yeni anahtar kullanın | | `404` | Klan, davet ya da parti yok | Kimlikleri `GET` ile doğrulayın | ## Çalışan örnek TypeScript'te klan kurup davet gönderme (SDK'nın tipli klan istemcisiyle): ```ts /** 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 { 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](chat.md) · [Ekonomi](economy.md) · [Karakterler](characters.md) · [Oyun kuralları](game-rules.md) - [TypeScript SDK](../sdk/typescript.md) · [Hatalar](../reference/errors.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/chat.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Modülü açmak ya da kapatmak | [Oyun ayarları › Modüller](../panel/game-settings-modules.md) | | Şehir (sahne) eklentisinin sohbetini okumak, mesajı silmek, oyuncuyu susturmak | [Şehir sohbeti](../panel/scene-chat.md) | > **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: | `kind` | `reference_id` | Kim girebilir | |---|---|---| | `global` | yok | Ortamdaki herkes | | `world` | gerekli (dünya/harita UUID'si) | Referansı bilen oyuncular | | `clan` | gerekli (klan UUID'si) | Klanın üyeleri | | `party` | gerekli (parti UUID'si) | Partinin üyeleri | | `direct` | yok; `direct_player_id` gerekli | Yalnı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 `) ve ortam başlığı: ``` x-crx-env: ``` 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](../auth/sessions-and-csrf.md). | Yöntem ve yol | Ne yapar | |---|---| | `POST /v1/chat/channels` | Kanal kurar ya da var olanı döndürür | | `GET /v1/chat/channels` | Oyuncunun yetkili olduğu kanalları listeler | | `POST /v1/chat/channels/{channel_id}/messages` | Mesaj gönderir | | `GET /v1/chat/channels/{channel_id}/messages` | Arşivi geriye doğru okur (`before_ms`, `limit` 1–100, varsayılan 50) | | `GET /v1/chat/ws` | Canlı 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](../sdk/typescript.md). ### Kanal kurmak ``` POST /v1/chat/channels x-crx-env: Content-Type: application/json { "kind": "clan", "reference_id": "0192f0aa-…" } ``` ```json { "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: Content-Type: application/json { "client_message_id": "c-0001", "body": "Merhaba!" } ``` ```json { "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): | `type` | Alanlar | Anlamı | |---|---|---| | `subscribe` | `channel_id` | Kanalın canlı akışına abone ol | | `unsubscribe` | — | Aboneliği bırak | | `send` | `channel_id`, `client_message_id`, `body` | REST'teki gibi mesaj gönder | | `ping` | `nonce` | Canlılık yoklaması | Sunucu çerçeveleri: | `type` | Alanlar | |---|---| | `subscribed` | `channel_id` | | `unsubscribed` | — | | `message` | `message`, `duplicate` | | `pong` | `nonce` | | `error` | `code` (`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ış](../realtime/overview.md). ## 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 | Belirti | Neden | Çözüm | |---|---|---| | `401` | Oturum yok, süresi dolmuş ya da `x-crx-env` eksik | Oturumu yenileyin; başlığı ekleyin | | `403` (gönderim) | Oyuncu susturulmuş ya da kanalda yetkisi yok | Kullanıcıya bilgi verin | | `400` | Boş ya da çok uzun gövde / `client_message_id` | Girdiyi düzeltin | | `422` | Küfür süzgeci mesajı reddetti | Mesajı değiştirin | | `429` | 10 saniyede 20 mesaj aşıldı | Bekleyip yeniden deneyin | | `error: forbidden_channel` (WS) | Kanal için yetki yok | Kanalı ö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 - [Klanlar ve partiler](social.md) · [Gerçek zamanlı: genel bakış](../realtime/overview.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) · [Hatalar](../reference/errors.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/competition.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Tablo tanımlamak, sezon, birim ve görünürlük ayarlamak | [Leaderboard](../panel/leaderboards.md) | | Bir tablonun en yüksek kayıtlarını ve sezon geçmişini görmek | [Leaderboard ayrıntısı](../panel/leaderboard-detail.md) | | Turnuva için ödül para birimi | [Para birimleri](../panel/currencies.md) | 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 yol | Kimlik | Ne yapar | |---|---|---| | `POST /v1/leaderboards` | sunucu anahtarı | Tablo tanımı oluşturur | | `GET /v1/leaderboards`, `GET /v1/leaderboards/{key}` | sunucu anahtarı | Tanımları listeler / okur | | `POST /v1/leaderboards/{key}/scores` | sunucu 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 oturumu | Tablonun ilk sayfası ve oyuncunun kendi satırı | | `POST /v1/tournaments` | sunucu anahtarı | Turnuva oluşturur | | `GET /v1/tournaments/{id}` | sunucu anahtarı | Turnuva durumunu okur | | `POST /v1/tournaments/{id}/participants` | sunucu anahtarı | Katılımcı kaydeder (`Idempotency-Key`) | | `POST /v1/tournaments/{id}/start` | sunucu anahtarı | Bracket üretip başlatır | | `POST /v1/tournaments/{id}/matches/{match_id}/result` | sunucu anahtarı | Maç sonucu bildirir ya da itirazı çözer (`Idempotency-Key`) | Sunucu uçları `Authorization: Bearer crx_live_<önek>_` ister; istemci anahtarı ve oyuncu oturumu reddedilir. Oyuncu ucu çerez ya da oturum belirteci ve `x-crx-env: ` 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](../sdk/typescript.md). ### Tablo tanımı ``` POST /v1/leaderboards Authorization: Bearer crx_live_<önek>_ 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>_ Idempotency-Key: score-oyuncu-1234-2026-11-03 Content-Type: application/json { "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "score": 4200 } ``` ```json { "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 ``` ```json { "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 metin**dir (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>_ 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](../reference/idempotency-and-concurrency.md). ## 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](rewards.md) 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 | Belirti | Neden | Çö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 ucu | Sunucu anahtarını sunucudan kullanın | | `400` (`Idempotency-Key`) | Puan yazarken başlık yok | Anahtar gönderin | | Puan yazıldı ama sıralama değişmedi | Oyuncunun sezondaki en iyi puanı daha iyi | Yalnız daha iyi puan sıralamayı değiştirir | | Puan yazıldı ama tablo hemen güncellenmedi | Projeksiyon gecikmesi | Birkaç 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 ```ts /** 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 { 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 { 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 - [Ödüller](rewards.md) · [Klanlar ve partiler](social.md) · [Karakterler](characters.md) - [TypeScript SDK](../sdk/typescript.md) · [Sayılar ve kodlama](../reference/numbers-and-encoding.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/notifications.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Push sağlayıcısı (FCM, APNs, OneSignal) kimlik bilgilerini girmek | [Bildirimler](../panel/notifications.md) › Sağlayıcılar | | Şablon yazmak (dil başına başlık ve gövde, yer tutucular) | [Bildirimler](../panel/notifications.md) › Şablonlar | | Olay tetikli kural kurmak, kuralı canlı önizlemek | [Bildirimler](../panel/notifications.md) › Kurallar | | Oyuncuya test bildirimi göndermek, gönderim geçmişini ve cihazları görmek | [Bildirimler](../panel/notifications.md) › 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: `) ve **sunucu anahtarı** (`Authorization: Bearer crx_live_<önek>_`). | Yöntem ve yol | Kimlik | Ne yapar | |---|---|---| | `POST /v1/notifications` | sunucu anahtarı | Bir oyuncuya bildirim kuyruğa yazar | | `GET /v1/notifications` | oyuncu oturumu | Gelen kutusu (`status`, `cursor`, `limit`) | | `POST /v1/notifications/{id}/read` | oyuncu oturumu | Okundu işaretler | | `POST /v1/devices` | oyuncu oturumu | Push cihazını kaydeder | | `DELETE /v1/devices/{token}?provider=` | oyuncu oturumu | Cihaz kaydını iptal eder (`204`) | | `GET /v1/push/config` | kimliksiz (`x-crx-env` zorunlu) | Web push için istemciye açık ayarlar | | `GET /v1/ws` | oyuncu oturumu | Canlı 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](../sdk/typescript.md). ### Sunucudan bildirim göndermek ``` POST /v1/notifications Authorization: Bearer crx_live_<önek>_ 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" } ``` ```json { "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 ``` ```json { "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": "", "device_meta": { "model": "Pixel 8" } } ``` ```json { "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 | Belirti | Neden | Çö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: true` | Aynı `dedup_key` zaten yazıldı | Beklenen davranış; yeni bildirim için yeni anahtar | | Bildirim `queued` kalıyor | Sağ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ı/platform | Girdiyi düzeltin | ## Çalışan örnek Oyuncu tarafında gelen kutusunu boşaltma: ```ts /** 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 { 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 - [Karakterler](characters.md) · [Ödüller](rewards.md) · [Mağaza ve IAP](store-iap.md) · [Oyun kuralları](game-rules.md) - [TypeScript SDK](../sdk/typescript.md) · [Hatalar](../reference/errors.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/webhooks.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Kayıtlı uç noktaları, teslimat denemelerini, başarısız (DLQ) teslimatları ve olay kuyruğunu görmek | [Webhook ve olay kuyruğu](../panel/webhooks.md) | | Modülü açmak | [Oyun ayarları › Modüller](../panel/game-settings-modules.md) | 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: | Alan | Türler | |---|---| | Karakter | `flag_set`, `attribute_set`, `resource_changed`, `modifier_added`, `modifiers_removed`, `xp_added`, `level_up`, `ability_unlocked`, `event_emitted`, `character_rolled_back` | | Mağaza | `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` | | Ödül | `reward_granted` | Bilinmeyen bir tür `400` döner. Tam liste ve veri alanları: [Webhook olayları](../reference/webhook-events.md). > **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ı: ```json { "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>_ ``` | Yöntem ve yol | Ne yapar | |---|---| | `POST /v1/webhooks` | Uç nokta kaydeder (`201`; sır yalnız burada) | | `GET /v1/webhooks` | Uç 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](../sdk/typescript.md)). ### Uç nokta kaydetmek ``` POST /v1/webhooks Authorization: Bearer crx_live_<önek>_ Content-Type: application/json { "url": "https://hooks.ornek-oyun.com/open-play", "event_types": ["level_up", "store_purchase_completed"] } ``` ```json { "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ık | Anlamı | |---|---| | `X-Crx-Signature` | `t=,v1=` | | `X-Crx-Event-Id` | Olay kimliği; tekrar eleme anahtarınız | | `X-Crx-Event-Type` | Zarftaki `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 | Belirti | Neden | Çözüm | |---|---|---| | Kayıtta `400` | URL `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 `409` | Aynı URL zaten aktif | Var olanı silin ya da kullanın | | İmza doğrulanmıyor | Gö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 geliyor | At-least-once teslimat | `X-Crx-Event-Id` ile eleyin | | Teslimatlar durdu | 10 deneme doldu (DLQ) ya da uç nokta silindi | Alıcıyı düzeltin; kaçan olaylar için kendi kaynağınızdan okuyun | | Teslimat hiç gelmiyor | Uç adı özel/loopback IP'ye çözümleniyor ya da yanıt 10 saniyeyi aşıyor | Genel 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. ```ts /** 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(); // örnek amaçlı bellek; üretimde kalıcı depo kullanın /** `X-Crx-Signature: t=,v1=` 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 - [Webhook olayları](../reference/webhook-events.md) · [Karakterler](characters.md) · [Mağaza ve IAP](store-iap.md) · [Ödüller](rewards.md) - [Güvenlik](../operations/security.md) · [Hatalar](../reference/errors.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/scenes-assets-models.html # 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](../panel/game-settings-modules.md) sayfasından açın. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | 3B model yüklemek, önizlemek, yayınlamak, arşivlemek, klip eklemek | [3D modeller](../panel/models.md), [Model profili](../panel/model-profile.md) | | Modeli eşyaya ya da karaktere bağlamak | [İçerik](../panel/content.md) (yerleşik eşya/karakter grupları) | | Mağaza afişi ve ürün görseli, para birimi ikonu yüklemek | [Mağaza](../panel/store.md), [Para birimleri](../panel/currencies.md) | | Şehir sohbetini denetlemek (sahne eklentisi) | [Şehir sohbeti](../panel/scene-chat.md) | | Scene eklentisinin kurulu olduğunu ve yetkisini görmek | [Eklentiler](../panel/plugins.md) | ## 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 yol | Kimlik | Ne yapar | |---|---|---| | `PUT /v1/scenes` | sunucu anahtarı | Scene'i idempotent sağlar | | `PUT /v1/assets` | sunucu anahtarı | Görseli ham gövdeyle yükler | | `GET /v1/assets` | sunucu 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}` | kimliksiz | Görsel baytlarını okur | | `GET /v1/model-files/{sha256}` | kimliksiz | Yayınlanmış modelin `glb` dosyasını okur | Yönetim uçları `Authorization: Bearer crx_live_<önek>_` ister; istemci anahtarı `403` alır. ### Scene sağlamak ``` PUT /v1/scenes Authorization: Bearer crx_live_<önek>_ Content-Type: application/json { "plugin_id": "package-ride", "module_id": "package-ride.presence", "key": "istanbul-1", "capacity": 200 } ``` ```json { "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ış](../realtime/overview.md) ve [Eklentiler](../plugins/overview.md). ### Görsel yüklemek ``` PUT /v1/assets Authorization: Bearer crx_live_<önek>_ Content-Type: image/png ``` ```json { "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 `` kullanın: uç **kimliksizdir**, ortamı `x-crx-env` başlığından ya da (başlık gönderemeyen ``/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](../getting-started/architecture-and-hosting.md). > **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/?env=` biçimindedir. Ayrıntı: [İçerik grupları › Katalog](content-groups.md#katalog-eşya-ve-karakter) 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 | Belirti | Neden | Çözüm | |---|---|---| | `422` (scene) | Eklenti bu ortamda etkin değil | Eklentiyi etkinleştirin ([Eklentiler](../panel/plugins.md)) | | `409` (scene) | Aynı anahtarda farklı kimlikli scene | Anahtarı 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 | | `` yüklenmiyor | `env` ya da `x-crx-env` eksik | Adrese `?env=` ekleyin | | `404` (model dosyası) | Model silinmiş ya da henüz hazır değil | Katalogdaki `model` alanını kontrol edin | | Panelde model `failed` | Dö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/?env=$ENV" -o afis.png ``` ## İlgili sayfalar - [İçerik grupları](content-groups.md) · [Envanter ve eşyalar](inventory-items.md) · [Mağaza ve IAP](store-iap.md) - [Gerçek zamanlı: genel bakış](../realtime/overview.md) · [Eklentiler](../plugins/overview.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/content-groups.html # İç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ız | Panel sayfası | |---|---| | Grup oluşturmak, alan eklemek, şablondan başlamak | [İçerik](../panel/content.md), [Grup düzenle](../panel/content-group-edit.md) | | Kayıt eklemek, değerleri düzenlemek, kaydı yayına almak ya da arşivlemek | [Kayıt düzenle](../panel/content-entry-edit.md) | | Yayın geçmişini ve farkı görmek, değişiklikleri oyuna göndermek | [İçerik](../panel/content.md) › Yayınla | | Model alanına 3B model bağlamak | [3D modeller](../panel/models.md) | ## 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 tutar | Ayarlar | |---|---|---| | `integer` | Tam sayı (isteğe bağlı ondalık gösterim ölçeği) | `min`, `max`, `scale` (0–3), `unit` (en çok 12 karakter), `default` | | `percent` | Baz puan yüzde (10000 = %100) | `min_bp`, `max_bp`, `default_bp` | | `text` | Metin | `max_chars` (en çok 512), `multiline` | | `bool` | Evet/hayır | `default` | | `select` | Seçenek(ler) | `options` (en çok 64), `multiple` | | `color` | Renk | `#rrggbb` | | `model` | 3B model | model kütüphanesi kimliği | | `image` | Görsel | varlığın `sha256` değeri | | `reference` | Başka kayda bağlantı | hedef: başka bir grup, yerleşik eşya ya da yerleşik karakter | | `table` | Tekrarlanan satırlı matris | `columns` (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 yol | Ne 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](scenes-assets-models.md). ## 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: `): yalnız `visibility = player` alanları ve değerlerini görür. - **Sunucu anahtarı** (`Authorization: Bearer crx_live_<önek>_`): 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 yol | Ne döner | |---|---| | `GET /v1/catalog/groups` | Yayı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ık | Anlamı | |---|---| | `x-crx-env` | Oyuncu oturumunda zorunlu; sunucu anahtarında ortam anahtardan gelir | | `If-None-Match` | Elinizdeki `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 | Belirti | Neden | Çözüm | |---|---|---| | `404` (grup) | Grup hiç yayınlanmadı ya da yok | Panelde grubu yayınlayın | | `401` | Kimlik yok ya da geçersiz `Bearer crx_…`; oyuncu oturumunda `x-crx-env` eksik | Kimliği ve başlığı düzeltin | | Panelde yayın reddedildi | Baş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üyor | Taslak yayınlanmadı ya da alan `server` görünürlüklü | Yayınlayın; görünürlüğü kontrol edin | | `304` | `ETag` eşleşti | Beklenen davranış; elinizdeki kopya güncel | ## Çalışan örnek `weapons` grubunun güncel yayınını `ETag` ile okuma: ```ts /** İç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 { 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 - [İçerik grupları ödül kaynağı olarak](rewards.md) · [Envanter ve eşyalar](inventory-items.md) · [Karakterler](characters.md) · [Scene, görsel ve 3B modeller](scenes-assets-models.md) - Oyun katalog entegrasyonu (ayrıntılı rehber) (`docs/oyun-katalog-entegrasyonu.md`) · [TypeScript SDK](../sdk/typescript.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/game-rules.html # 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ız | Panel sayfası | |---|---| | Kurallar ekranına genel bakış, konu ve kartlar | [Kurallar](../panel/rules.md) | | Bir oyun kuralı kartını düzenleyip yayınlamak, önizlemek | [Kural kartı](../panel/rules-game-card.md) | | Yayın geçmişini ve farkı görmek, geri almak | [Kural geçmişi](../panel/rules-history.md) | | Otomasyon oluşturmak, düzenlemek, açıp kapatmak | [Otomasyon düzenleyici](../panel/rules-automation-editor.md) | ## 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ış](../plugins/overview.md). ## 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>_ ``` | Yöntem ve yol | Ne yapar | |---|---| | `POST /v1/rules` | Kuralı kaydeder (`201`) | | `GET /v1/rules` | Kuralları 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}/disable` | Kuralı açar / kapatır (`204`) | | `DELETE /v1/rules/{key}` | Kuralı siler (`204`) | | `POST /v1/rules/dry-run` | Kuralı 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](../sdk/typescript.md)). 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>_ 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 } ``` ```json { "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>_ Content-Type: application/json { "condition": "character.progression.level >= 10", "actions": [ { "type": "set_flag", "key": "veteran", "value": true } ], "character_id": "0192f0f2-…" } ``` ```json { "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](webhooks.md) 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 | Belirti | Neden | Çözüm | |---|---|---| | `429` (komut) | Kural zinciri bütçeyi aştı | Birbirini tetikleyen kuralları sadeleştirin | | Kayıtta `400`/`422` | CEL derlenemedi, bilinmeyen tetikleyici ya da alan | Koş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ışında | Alanın gösterdiği sınıra uyun | | Değeri yayınladım, oyun eski davranıyor | Parametre `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](characters.md) sayfasındaki akışla doğrulayın. ## İlgili sayfalar - [Karakterler](characters.md) · [Ödüller](rewards.md) · [Webhook'lar](webhooks.md) · [Bildirimler](notifications.md) - [Eklentiler: genel bakış](../plugins/overview.md) · [TypeScript SDK](../sdk/typescript.md) --- Source: https://open-play.b4klabs.dev/docs/tr/guides/player-data.html # 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](../panel/game-settings-modules.md) sayfasından kapatılamaz. ## Panelde nerede | Ne yapacaksınız | Panel sayfası | |---|---| | Bir oyuncunun anahtarlarını, boyutlarını ve değerini görmek; gerektiğinde silmek | [Oyuncular](../panel/players.md) › oyuncu profili › "Oyun verisi" bölümü | Panelden değer **yazılamaz**; yalnız salt okunur biçimli JSON olarak görünür. Operatör silmesi gerekçe (8–500 karakter) ister ve denetim kaydına yazılır. ## Sınırlar | Sınır | Değer | |---|---| | Anahtar | `^[a-z0-9][a-z0-9._-]{0,63}$` (küçük harf, rakam, `.`, `_`, `-`; en çok 64 karakter) | | Değer | Üst düzey **JSON nesnesi**; serileşmiş (kompakt) boyutu en çok **65 536 bayt** | | Anahtar sayısı | Oyuncu başına en çok **16** | | Yazım hızı | Oyuncu başına dakikada en çok **60** yazım (PUT ve DELETE; okumalar sayılmaz) | Değerde **kayan noktalı sayılar serbesttir**: değer opak veridir, sunucu onunla hesap yapmaz. İki sınır vardır: nesne anahtarlarının sırası korunmaz ve 2^64'ten büyük tam sayılar ile çift duyarlığı aşan ondalıklar yuvarlanabilir; büyük sayıları dize olarak saklayın. ## Sürümlü yazım (CAS) Her anahtarın bir **sürümü** vardır: ilk yazımda `1`, her başarılı yazımda 1 artar. Sürüm bir i64'tür ve JSON'da **ondalık dize** olarak taşınır (`"3"`); HTTP başlığında `ETag: "3"` biçimindedir. **Önkoşulsuz yazım yoktur.** Her `PUT` tam olarak birini taşır: - `If-None-Match: *` — yalnız anahtar **yoksa** oluştur. - `If-Match: ""` — 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: `. **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](../auth/sessions-and-csrf.md)). Geliştirici anahtarı oyuncu uçlarında kabul edilmez (`401`). | Yöntem ve yol | Kimlik | Ne yapar | |---|---|---| | `GET /v1/player/data` | oyuncu oturumu | Kendi anahtarlarının özetini listeler | | `GET /v1/player/data/{key}` | oyuncu oturumu | Bir anahtarı okur (`ETag: ""`) | | `PUT /v1/player/data/{key}` | oyuncu oturumu | Önkoşullu oluşturur ya da günceller | | `DELETE /v1/player/data/{key}` | oyuncu oturumu | `If-Match` ile siler (`204`) | | `GET /v1/players/{player_id}/data` | **sunucu anahtarı** | Bir oyuncunun anahtar özetleri (yalnız okuma) | | `GET /v1/players/{player_id}/data/{key}` | **sunucu anahtarı** | Bir oyuncunun bir anahtarı (yalnız okuma) | Sunucu uçları `Authorization: Bearer crx_live_<önek>_` 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 ``` ```json { "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: If-None-Match: * Content-Type: application/json { "value": { "level": 4, "stars": 17, "playTimeSeconds": 913.5 } } ``` ```json { "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": }` 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 ``` ```json { "key": "my-game.save", "value": { "level": 5, "stars": 21 }, "version": "2", "updatedAt": "2026-11-03T10:16:02.004Z" } ``` Anahtar yoksa `404` ve `{"code":"not_found"}` döner. ## Hata kodları Bu uçların hataları, çekirdeğin geri kalanından farklı olarak **JSON**'dur: `{ "code": "…", "message": "…" }`. | Durum | `code` | Ne zaman | |---|---|---| | 404 | `not_found` | Anahtar yok (sunucu uçlarında oyuncu bu ortamda yoksa da) | | 409 | `version_conflict` | Sürüm tutmadı ya da var olan anahtara `If-None-Match: *`; `currentVersion` güncel sürümü (anahtar yoksa `null`) taşır | | 413 | `value_too_large` | Değer 64 KiB'ı aştı; `limit` alanı sınırı verir | | 422 | `invalid_key` | Anahtar kalıba uymuyor | | 422 | `invalid_value` | Değer nesne değil, gövde `{ "value": … }` değil ya da JSON değil | | 422 | `too_many_keys` | Oyuncu 16 anahtarda; `limit` alanı sınırı verir | | 428 | `precondition_required` | `If-Match` / `If-None-Match: *` yok, çift ya da biçimsiz | | 429 | `rate_limited` | Dakikada 60 yazım aşıldı; `Retry-After` saniye cinsinden bekleme verir | | 401 | — | Oturum yok, geçersiz ya da yasaklı oyuncu (kimlik katmanı ban ayrımını sızdırmaz) | | 403 | — | Origin, Fetch Metadata ya da CSRF reddi; sunucu uçlarında istemci anahtarı | Yasaklı bir oyuncunun yazımı kimlik katmanında `401` ile biter; depo da savunma olarak yazımı `403` ve `{"code":"account_banned"}` ile reddeder. ## TypeScript SDK `PlayerClient.data` bu uçların tipli karşılığıdır: `list()`, `get(key)`, `create(key, value)`, `update(key, value, version)`, `delete(key, version)`. Sürümler `bigint`'tir; değer normal JS nesnesidir (güvenli tam sayılar `number`, aralık dışı tam sayılar `bigint`, ondalıklar `number`). CSRF belirtecini ve çerezi SDK yönetir. `409` çatışması `PlayerDataConflict` (+ `currentVersion: bigint | null`) olarak fırlar; diğer retler `CrxError`'dır ve `playerDataErrorCode(error)` ile ayrışır. Bkz. [TypeScript SDK](../sdk/typescript.md). ## Çalışan örnek: kaydet, çatışmada birleştir ```ts /** 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 { 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 - [Misafir girişi](../auth/guest.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) - [TypeScript SDK](../sdk/typescript.md) --- Source: https://open-play.b4klabs.dev/docs/tr/realtime/overview.html # 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 | Soket | Yol | Ne için | Kim konuşur | Kimlik | |---|---|---|---|---| | Realtime (eklenti) | `/v1/realtime/ws` | Oyun 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/ws` | Bildirimin oyuncuya canlı itilmesi | Yalnız sunucu → oyuncu | Oyuncu oturumu (çerez ya da Bearer) | | Sohbet | `/v1/chat/ws` | Kanal 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ı](../reference/endpoints.md) 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ı almak | Realtime soketi, [protokol](protocol.md) | | 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](../plugins/calling-plugins.md)) | | Oyuncuya "yeni bildirim" anlık göstermek | `/v1/ws`; kalıcı kaynak her zaman `GET /v1/notifications` gelen kutusudur ([bildirimler](../guides/notifications.md)) | | Kanal sohbeti | `/v1/chat/ws` ya da REST ([sohbet](../guides/chat.md)) | | Skor tablosu, mağaza, envanter | REST; 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](../sdk/typescript.md) 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:///` ya da `wss:///`; el sıkışmasında `Origin` ortam başına denetlenir (CORS bunu değiştirmez). Ayrıntı: [mimari ve barındırma](../getting-started/architecture-and-hosting.md). 2. **Ortam başlığı.** Oyuncu kimliği her zaman bir ortama (environment) bağlıdır ve `x-crx-env: ` 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](../panel/game-settings-modules.md). ## 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ön | `type` | Alanlar | Anlam | |---|---|---|---| | İstemci → sunucu | `subscribe` | `channel_id` | Kanal aboneliği (aynı anda tek kanal) | | İstemci → sunucu | `unsubscribe` | — | Aboneliği bırakır | | İstemci → sunucu | `send` | `channel_id`, `client_message_id`, `body` | REST ile aynı tekrar-güvenli mesaj gönderme | | İstemci → sunucu | `ping` | `nonce` (sayı) | Canlılık yoklaması | | Sunucu → istemci | `subscribed` / `unsubscribed` | `channel_id` (yalnız `subscribed`) | Kabul | | Sunucu → istemci | `message` | `message`, `duplicate` | Kanal mesajı; `duplicate: true` ise `client_message_id` daha önce işlenmişti | | Sunucu → istemci | `pong` | `nonce` | `ping` yanıtı | | Sunucu → istemci | `error` | `code` | `invalid_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ı](../reference/rate-limits.md)). Ayrıntılı kullanım için [sohbet rehberi](../guides/chat.md). ## Sıradaki adım - Eklenti realtime protokolünü baştan sona görmek için [protokol](protocol.md). - Bağlantı koptuğunda ne olduğunu, limitleri ve boşta kapanmayı görmek için [yeniden bağlanma ve limitler](reconnect-and-limits.md). --- Source: https://open-play.b4klabs.dev/docs/tr/realtime/protocol.html # 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](../reference/versioning.md)). ## 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 metin**dir: `"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](../reference/numbers-and-encoding.md). `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ışı ```mermaid 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 ```http 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=; __Secure-crx.csrf= x-csrf-token: ``` ```http 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ık | Zorunlu | Not | |---|---|---| | `x-crx-env` | evet | Oyuncunun ortamı. Oturumun ortamıyla uyuşmalıdır. | | `Origin` | evet | Ortamın izinli kökenlerinden biri olmalı; bilet bu kökene bağlanır. | | `Cookie` ya da `Authorization: Bearer ` | evet | Çerez varsa her zaman çerez kipi seçilir; bozuk çerezde Bearer'a düşülmez. | | `x-csrf-token` + `__Secure-crx.csrf` çerezi | yalnı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](../auth/sessions-and-csrf.md)). | - 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:///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](reconnect-and-limits.md)). ### 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. ```json {"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: ```json {"type":"Ready","protocolVersion":1,"connectionId":"0192a3b4-0000-7000-8000-0000000000a1","playerId":"0192a3b4-0000-7000-8000-0000000000b2","sessionEpoch":"7","authorityGeneration":"42"} ``` | Alan | Anlam | |---|---| | `connectionId` | Bu soketin sunucu kimliği. | | `playerId` | Doğrulanmış oyuncu. | | `sessionEpoch` | Bu 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. | | `authorityGeneration` | Ortamı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 ```json {"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"}} ``` | Alan | Tür | Anlam | |---|---|---| | `requestId` | UUID | Deneme kimliği. | | `pluginId` | metin | Modülün sahibi eklenti ([eklentiler](../plugins/overview.md)). | | `moduleId` | metin | Eklentinin tanımladığı realtime modülü; eklentinin şema belgesindeki `modules` bölümünde listelenir. | | `target.kind` | metin | `PlayerSelf`, `Room`, `Scene` ya da `InterestSet`. | | `target.roomId` / `sceneId` / `interestSetId` | UUID | Hedefin kimliği (`kind`'a göre; `PlayerSelf` kimlik taşımaz). | | `cursor` | UUID, 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 ```json {"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. ```json {"type":"Input","protocolVersion":1,"requestId":"…","pluginId":"arena","moduleId":"arena.movement","messageType":"arena.movement.input","schemaVersion":1,"subscriptionId":"…","sessionEpoch":"7","inputSeq":"42","payload":{"delta":1}} ``` | Alan | Anlam | |---|---| | `messageType`, `schemaVersion` | Modülün şema belgesindeki girdi tanımıyla birebir eşleşmelidir. | | `subscriptionId` | `Joined`'dan gelen abonelik. | | `sessionEpoch` | `Ready`'deki değer. | | `inputSeq` | Artan 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`. | | `payload` | Eklentinin 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](reconnect-and-limits.md)). ### `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](../plugins/calling-plugins.md)). ```json {"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"}} ``` | Alan | Anlam | |---|---| | `idempotencyKey` | Zorunlu mantıksal tekrar anahtarı. Aynı anahtar ve aynı içerik aynı sonucu döndürür ([idempotency](../reference/idempotency-and-concurrency.md)). | | `expectedVersion` | İsteğe bağlı iyimser sürüm (ondalık metin). Uyuşmazlık hata olur. | | `moduleId` | Oda kapsamlı komutlarda modül bağlamı; yoksa `null`. | | `payload` | Eklentinin komut şemasına uyar; kodlanmış boyutu en çok 16 KiB'dir, aşım `InvalidPayload` alır. | ### `Ping` ```json {"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](reconnect-and-limits.md)). ## Sunucu çerçeveleri | `type` | Ne zaman | Alanlar | |---|---|---| | `Ready` | `Authenticate` başarılı | `protocolVersion`, `connectionId`, `playerId`, `sessionEpoch`, `authorityGeneration` | | `Joined` | `Join` yetkilendirildi | `requestId`, `subscriptionId`, `target` | | `Left` | `Leave` işlendi | `requestId`, `subscriptionId` | | `CommandResult` | `Command` kesinleşti | `requestId`, `messageType`, `schemaVersion`, `payload` | | `Ack` | `Input` işlendi | `requestId`, `inputSeq`, `status`, `revision` (isteğe bağlı) | | `Snapshot` | Geçici durum yayını | `subscriptionId`, `resourceId`, `messageType`, `schemaVersion`, `streamEpoch`, `revision`, `payload` | | `DurableRecovery` | Kalıcı akış için kurtarma bariyeri | `subscriptionId`, `cursor`, `barrierSequence`, `coveredEventIds`, `snapshot` (boş olabilir) | | `DurableEvent` | Kalıcı, sıralı eklenti olayı | `subscriptionId`, `streamId`, `sequence`, `eventId`, `messageType`, `schemaVersion`, `cursor`, `payload` | | `Pong` | `Ping` yanıtı | `nonce` | | `Error` | Hata | `requestId` (varsa), `code`, `retryable` | ### `Ack` durumları | `status` | Anlam | Ne yapmalı | |---|---|---| | `Applied` | Girdi durumu ilerletti. `revision` yeni revizyondur. | — | | `Replayed` | Aynı sıra numarası ve içerik zaten işlenmişti. | Yoksayın; güvenli tekrar. | | `Stale` | Sı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. | | `Mismatch` | Aynı sıra numarası farklı içerikle geldi. | İstemci hatasıdır; sıra numarasını yeniden üretmeyin, tekrar etmeyin. | | `ResyncRequired` | Sı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](reconnect-and-limits.md)). ## Hata çerçevesi ```json {"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. | `code` | Ne zaman | `retryable` | Soket | |---|---|---|---| | `UnexpectedFrame` | Fazına uymayan çerçeve (ör. kimlik doğrulamadan önce `Join`, ya da ikinci `Authenticate`) | hayır | kapanır | | `UnsupportedProtocol` | `protocolVersion` 1 değil | hayır | kapanır | | `InvalidPayload` | Bozuk 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 | | `AuthenticationFailed` | Bilet geçersiz/kullanılmış/süresi dolmuş, 5 sn zaman aşımı, oturum iptal edildi | hayır | kapanır | | `PermissionDenied` | Hedef, modül ya da işlem için yetki yok; yasaklı hesap | hayır | açık kalır | | `PayloadTooLarge` | Çerçeve bütçesini aştı (4 KiB kimlik doğrulama, 16 KiB sonrası) | hayır | kapanır | | `CapacityExceeded` | Hı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 | | `Unavailable` | Bağımlılık geçici kullanılamıyor ya da yerleşim hazır değil | genellikle 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ı | Kod | Neden | Anlam | |---|---|---| | `4100` | `idle_timeout` | Oyuncu etkinliği gelmedi; boşta kapanma ([ayrıntı](reconnect-and-limits.md)). Tipli `Error` çerçevesi **olmaz**. | | `1008` | `module_disabled` | `plugins` modülü kapatıldı. | | `1013` | `unavailable` | Modü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. ```ts 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](reconnect-and-limits.md). --- Source: https://open-play.b4klabs.dev/docs/tr/realtime/reconnect-and-limits.html # 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](protocol.md)). 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](../plugins/calling-plugins.md)); 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ılan | Ayar | |---|---|---| | Otomatik yeniden bağlanma | açık | `autoReconnect` | | Deneme gecikmesi | `min(reconnectBaseMs · 2^(n−1), 30 000)` ms + `[0, reconnectBaseMs)` jitter | `reconnectBaseMs`: 1000 (100 ile 10 000 arası) | | En çok deneme | 8 | `maxReconnectAttempts`: 0 ile 20 arası | | Kimlik doğrulama zaman aşımı | 5 sn | `authTimeoutMs` (en çok 5 sn) | | Uygulama `Ping` aralığı | 15 sn | `heartbeatMs`: 10 ms ile 60 sn | | Kalıcı komut zaman aşımı | 15 sn | `commandTimeoutMs`: 1 ms ile 120 sn | | Cevap bekleyen girdi penceresi | 4 | `maxPendingInputs`: 1 ile 8 | | Çerçeve boyutu | 16 KiB | `maxFrameBytes` (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](../operations/security.md)); 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. ```ts 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 | Limit | Değer | Aşılınca | |---|---|---| | Bilet ömrü | 30 sn, tek kullanım, `Origin`'e bağlı | `AuthenticationFailed` | | `Authenticate` bekleme süresi | 5 sn | `AuthenticationFailed`, soket kapanır | | Kimlik doğrulama fazı çerçevesi | 4 KiB | `PayloadTooLarge`, kapanır | | Çerçeve (kimlik doğrulama sonrası) | 16 KiB | `PayloadTooLarge`, kapanır | | Taşıma katmanı mesajı | 64 KiB | 16 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ğeri | `InvalidPayload` | | `Command` yükü | en çok 16 KiB | `InvalidPayload` | | 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 soket | 4 | `CapacityExceeded` (`retryable: false`), soket kapanır | | Adres başına açık soket | 8 | `CapacityExceeded` (`retryable: false`), soket kapanır (operatör gözlem kipine alırsa yalnız sayılır) | | Adres başına yeni bağlantı | 30/dakika | yükseltme reddedilir | | Soket başına abonelik | 32 (sunucu varsayılanı) | `Join` reddedilir | | Oturum yeniden doğrulama | 25 sn'de bir | oturum iptal edilmişse `AuthenticationFailed` ve kapanış | | Modül kapısı yeniden kontrol | 2 sn'de bir | `1008` / `1013` ile kapanış | | Kurtarmada tekrar oynatılan olay | 256 | `ResyncRequired` | | İmleç saklama | 24 saat | `ResyncRequired` | | Boşta kapanma | 5 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](../sdk/unity.md)). ## 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 | Belirti | Olası neden | Çözüm | |---|---|---| | Her bağlanışta hemen `AuthenticationFailed` | Bilet 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 kopma | Boşta kapanma (`4100`) | "Devam et" akışı kurun ya da canlı tutma girdisi gönderin | | Yükseltme `401` | Sorgu dizesi var, alt protokol istenmedi ya da `Origin` izinli değil | [Protokol: soketi aç](protocol.md) | Daha fazlası için [sorun giderme](../operations/troubleshooting.md). --- Source: https://open-play.b4klabs.dev/docs/tr/plugins/overview.html # 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üzey | Ne verir | Nası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](calling-plugins.md) | | **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ü](../realtime/protocol.md) | 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](bindings.md). 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ı](../guides/game-rules.md). - **Webhook'lar**: Open Play olaylarını kendi sunucunuza bildirir. Bkz. [webhook rehberi](../guides/webhooks.md). - **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ı](../reference/endpoints.md). Depoda gelen eklentiler: | Eklenti | Sürüm | Ne için | Yüzey | |---|---|---|---| | `package-ride` | 1.44.0 | Teslimat oyunu: sürüş, görevler, yarış bahisleri, sahne | 36 komut, 33 sorgu, 3 realtime modülü | | `arena` | 1.0.0 | Minimal ö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](../panel/plugins.md)). ## Ortam başına durum Bir eklenti, her ortam (environment) için ayrı ayrı bir durumda bulunur: | Durum | Komutlar | Sorgular | |---|---|---| | Etkin (`Enabled`) | kabul edilir | kabul edilir | | Salt okunur (`ReadOnly`) | reddedilir | kabul edilir | | Boşaltılıyor (`Draining`) | yeni komut reddedilir, süren işler biter | kabul edilir | | Kapalı (`Disabled`) | reddedilir | reddedilir | Reddedilen çağrı `403` ve `forbidden` alır ([hatalar](../reference/errors.md)). 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](../panel/game-settings-modules.md)). ## Kimlikle erişim Her işlemin şema belgesinde bir `access` değeri vardır: | `access` | Kim çağırır | |---|---| | `player` | Oyuncu oturumu (çerez ya da Bearer oturum jetonu) | | `server` | Yalnız sunucu anahtarı; oyuncu istemcisinde görünmez | | `public` | Kimlik 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](calling-plugins.md). ## Sıradaki adım - Komutu ve sorguyu çağırmak için [eklentileri çağırma](calling-plugins.md). - Tipli istemci üretmek ve şema sürümünü sabitlemek için [bağlamalar](bindings.md). - Kendi sunucunuza yeni bir eklenti yazmak için [eklenti yazmak](authoring.md). --- Source: https://open-play.b4klabs.dev/docs/tr/plugins/authoring.html # 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ış](overview.md), [eklentileri çağırma](calling-plugins.md) ve [bağlamalar](bindings.md) 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ı](../guides/game-rules.md), [webhook'lar](../guides/webhooks.md) ya da kendi sunucunuz + sunucu anahtarı ([uç nokta başvurusu](../reference/endpoints.md)). ## 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ça | Ne işe yarar | Nerede tanımlanır | |---|---|---| | **Manifest** | Eklentinin 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ülleri | `OpenPlayPlugin::manifest` | | **Tipli mesajlar** | Komut/sorgu girdisi ve çıktısı; her biri tek yönlü (`CommandInput`, `CommandOutput`, `QueryInput`, `QueryOutput`, gerçek zamanlı girdi/durum, olay), katı `serde` tipi | `PluginMessage` uygulayan struct'lar | | **Komut** | Durumu değiştiren, kalıcı ve tekrar oynatması güvenli işlem. Karar fonksiyonu **saftır**: I/O, saat, rastgelelik yoktur | `TypedCommand::decide`, `OpenPlayPlugin::register` | | **Sorgu** | Salt-okunur okuma; yalnız girdi/çıktı kodekleri kaydedilir | `OpenPlayPlugin::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, `schemaSha256` | `OpenPlayPlugin::artifact`, `PluginArtifact::from_manifest` | | **Gerçek zamanlı modül** (isteğe bağlı) | Sunucu-otoriter canlı girdi/durum indirgeyicisi (reducer) | `OpenPlayPlugin::register_realtime`, `TypedRealtimeModule` | | **Dispatcher** | Komutun veritabanına gerçekten yazıldığı, sorgunun okunduğu yer; sunucudaki ortak işlem sınırını (`PluginUnitOfWork`) kullanır | `PluginCommandDispatcher`, `PluginQueryDispatcher`, `PluginCommandResultLookup` | | **Panel yüzeyi** (isteğe bağlı) | Panelde bildirimsel yönetim ekranları, özel bileşenler, oyun kuralı kataloğu, leaderboard bildirimi | `admin_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](../reference/numbers-and-encoding.md)). `deny_unknown_fields` bilinmeyen alanı reddeder. ```rust // 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. ```rust // 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 { 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::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::()?; Ok(()) } fn artifact(&self) -> Result, 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. ```rust // 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 { 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: ```rust 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](bindings.md)). Depoda üç yer aynı kaynaktan, **aynı değişiklikte** güncellenir: 1. `artifacts/plugins/.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: ```bash npx crx plugins generate \ --input hello-counter.plugin.schema.json \ --schema-sha256 \ --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](bindings.md). 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](../panel/plugins.md)). 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ış](overview.md)). 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ı](../guides/game-rules.md)). - `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 | Hata | Belirti | Doğrusu | |---|---|---| | Sürüm artırıldı, yetki geçirilmedi | Eklenti ve gerçek zamanlı çağrılar `403 forbidden`, şema ucu `404` | Drain, ikili, sonra **Sürüme geç** | | Şema değişti, artifact/pin yenilenmedi | Artifact 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önderildi | Aynı işlem iki kez uygulanır | Aynı `Idempotency-Key` ve aynı gövde; emin değilseniz önce sonuç sorgusu ([çağırma](calling-plugins.md)) | | `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ç verir | Zaman ve tohum host bağlamından gelir; kalıcı etki dispatcher planında | | Ekonomik sonuç gerçek zamanlı reducer'da | Redis kaybında sonuç kaybolur | Kalıcı sonuç komut yoluna | | Artifact'ta olmayan ya da fazla mesaj | Açılış `freeze`/kayıt hatası | Manifest ve artifact aynı Rust kaynağından türesin | | Komut için istemci API anahtarı | `401` | Oyuncu oturumu ya da sunucu anahtarı | | Ayrı dispatcher bağlanmadan komut eklendi | Komut yönlendirilemez | Dispatcher 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 - Eklentiyi çağırmak için [eklentileri çağırma](calling-plugins.md). - İstemci bağlaması ve şema sürümü sabitleme için [bağlamalar](bindings.md). - Ortamda açıp sürüm değiştirmek için [panel: Eklentiler](../panel/plugins.md). --- Source: https://open-play.b4klabs.dev/docs/tr/plugins/calling-plugins.html # 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](bindings.md)). ## Uçlar | Yöntem ve yol | Amaç | |---|---| | `POST /v1/plugins/{plugin_id}/commands/{message_type}` | Komutu çalıştırır | | `GET /v1/plugins/{plugin_id}/commands/{message_type}/result` | Komutun 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}/schema` | Eklentinin ş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ğıran | Gönderilecek | |---|---| | Oyuncu (çerez) | `Cookie` (`__Secure-crx.session`) + `x-crx-env: `; değiştirici (POST) isteklerde ayrıca `x-csrf-token` + `__Secure-crx.csrf` çerezi | | Oyuncu (Bearer) | `Authorization: Bearer ` + `x-crx-env: `. 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](../auth/sessions-and-csrf.md)) | | Sunucu | `Authorization: Bearer crx_live_<önek>_` (sunucu anahtarı). `x-crx-env` gönderirseniz anahtarın ortamıyla aynı olmalı, aksi halde `401`. CSRF ve Origin denetimi uygulanmaz | | Herkese açık sorgu | Yalnı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 ```http 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=; __Secure-crx.csrf= x-crx-env: 0192a3b4-0000-7000-8000-000000000001 x-csrf-token: x-crx-schema-version: 1 Idempotency-Key: 5d1f2c88-4f0b-4c8f-9f1e-9a3f6b0c1d22 If-Match: "3" Content-Type: application/json {"expectedVersion":"3"} ``` ```http 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ık | Zorunlu | Anlam | |---|---|---| | `Idempotency-Key` | komutlarda evet | Mantıksal tekrar anahtarı; boş olamaz. [Kurallar](../reference/idempotency-and-concurrency.md) | | `x-crx-schema-version` | evet | Gönderdiğiniz yükün şema sürümü (pozitif tam sayı); işlemin `schemaVersion` değeriyle eşleşmelidir. Sorgularda da zorunludur | | `If-Match` | hayır | Beklenen 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/json` | evet | Gö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](../reference/numbers-and-encoding.md). `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. | Durum | Yanıt | |---|---| | İlk istek | İşlenir, `200` | | Aynı anahtar, aynı gövde | Kayıtlı sonuç döner | | Aynı anahtar, **farklı** gövde | `422` ve `idempotency_mismatch` | | Aynı anahtarlı istek hâlâ işleniyor | `409` 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: ```http 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= ``` Üç sorgu parametresi de zorunludur: `idempotency_key`, `output_message_type` ve `output_schema_version`. Yol, işlemin `resultPath` alanıyla aynıdır. | Yanıt | Anlam | Ne yapmalı | |---|---|---| | `200` + gövde | Komut kesinleşmişti; gövde çıktıdır | Sonucu kullanın | | `202` (boş gövde) | Henüz işleniyor | Kısa süre sonra tekrar sorgulayın | | `404` | Bu anahtarla kayıt yok: komut hiç uygulanmamış | Yalnız bu durumda **aynı anahtar ve gövdeyle** komutu gönderebilirsiniz | | `422` + `reason` | Komut işlendi ve iş kuralı tarafından reddedildi | Gönderme; `reason` makine kodudur | | `410` | Kayıt süresi doldu | Sonuç 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. ```http 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= ``` `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: ```http 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](bindings.md) 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](../reference/errors.md)): ```json {"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. | `code` | HTTP | `retryable` | Ne zaman | |---|---|---|---| | `unauthorized` | 401 | hayır | Oturum/anahtar yok, geçersiz, istemci anahtarı ya da ortam uyuşmazlığı | | `forbidden` | 403 | hayır | CSRF ya da Origin reddi; eklenti etkin değil ya da sürüm uyuşmuyor; yasaklı hesap (`message: account banned`) | | `module_disabled` | 404 | hayır | `plugins` modülü kapalı (`module` alanı modül adını taşır) | | `not_found` | 404 | hayır | Bilinmeyen eklenti ya da mesaj türü; kayıt yok | | `invalid_argument` | 400 | hayır | Eksik/bozuk başlık, bilinmeyen şema sürümü, şemaya uymayan yük, kopya sorgu parametresi | | `conflict` | 409 | hayır | `If-Match` sürümü gerçekle uyuşmuyor | | `idempotency_in_flight` | 409 | evet | Aynı anahtarlı istek hâlâ işleniyor | | `idempotency_mismatch` | 422 | hayır | Aynı anahtar, farklı gövde | | `unprocessable` | 422 | hayır | İş kuralı reddi; `reason` alanı makine kodunu taşır | | `idempotency_expired` | 410 | hayır | Kayıt süresi doldu | | `rate_limited` | 429 | evet | Oran sınırı; `Retry-After: 60` ([hız sınırları](../reference/rate-limits.md)) | | `tenant_unavailable` | 503 | evet | Ortamın yerleşimi geçici kullanılamıyor; `Retry-After: 30` | | `internal` | 500 | evet | Sunucu 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](../reference/errors.md)). İ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](bindings.md)). ```ts 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 { 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 { const demo = client.plugin(DemoGameBinding); const status = await demo.queries.getGiftStatus({ includeHistory: false }); return status.claimedToday; } ``` ## Sıradaki adım - Tipli istemci ve şema sabitleme: [bağlamalar](bindings.md). - Tekrar güvenliği ve sürüm denetimi kuralları: [idempotency ve eşzamanlılık](../reference/idempotency-and-concurrency.md). --- Source: https://open-play.b4klabs.dev/docs/tr/plugins/bindings.html # 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/.plugin.schema.json` aynı belgedir. Üst düzey alanlar: | Alan | Anlam | |---|---| | `pluginId` | Eklenti kimliği (ör. `package-ride`) | | `version` | Eklenti sürümü (ör. `1.44.0`) | | `schemaSha256` | `schemas` bölümünün özeti; **şema sabitleme anahtarı** (aşağıda) | | `protocolVersion` | Eklenti protokol ana sürümü (şimdilik `1`) | | `codecProfile` | Tel kodlaması: `plugin-v1-decimal-int64` ([sayılar](../reference/numbers-and-encoding.md)) | | `schemas` | Girdi ve çıktı JSON şemaları (bileşen adı → şema) | | `queries`, `commands` | İşlem tanımları | | `modules` | Realtime modülleri | Bir işlem tanımı şu alanları taşır: | Alan | Anlam | |---|---| | `name` | İşlem adı; istemci kodunda yöntem adıdır (ör. `claimLoginBonus`) | | `kind`, `method`, `path` | `command` ya da `query`; HTTP yöntemi; tam yol | | `requestSchema`, `responseSchema`, `schemaVersion` | Girdi ve çıktı şemaları ve gönderilecek `x-crx-schema-version` | | `requestMessageType`, `responseMessageType` | Yoldaki `{message_type}` ve çıktı mesaj türü | | `access` | `player`, `server` ya da `public` | | `retry` | `read` (güvenle tekrar), `same-key-command` (yalnız aynı anahtarla tekrar), `never` | | `journal` | `none`, `full` ya da `reference`: SDK'nın komutu yerelde nasıl kaydettiği (yeniden yüklemeden sonra devam için) | | `idempotencyRequired`, `receiptRetentionSeconds`, `resultPath` | Komutlar için: anahtar zorunluluğu, kayıt süresi, sonuç sorgu yolu | | `deprecated` | Varsa kullanımdan kalkma bilgisi: `since`, `sinceUnixS`, `replacement` ([sürümleme](../reference/versioning.md)) | 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](../realtime/protocol.md)). ## `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: ```ts 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 { 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 }): 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: ```bash npx crx plugins generate \ --input demo-game.plugin.schema.json \ --schema-sha256 ffc38829f30ea0dd1b829f351f905854a9875806fbc8a2bbd591815c7ee52c2f \ --output src/generated/demo-game.ts ``` 4. CI'da üretilmiş dosyanın güncel olduğunu denetleyin; `--check` dosyayı **yazmaz**, byte karşılaştırır: ```bash 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: ```ts 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](calling-plugins.md)). `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](../sdk/typescript.md) 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: ```ts 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 { 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`. ```csharp using System.Threading.Tasks; using OpenPlay; using OpenPlay.PackageRide; /// /// Oyuna girerken sunucudaki eklenti şemasının, pakete gömülü bağlamayla aynı olduğunu doğrular. /// Uyuşmazlıkta (UpgradeRequired, kod plugin_binding_mismatch) oyuncuyu güncellemeye yönlendirin. /// public static class PluginPinCheck { public static async Task 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 eklendi | `400` `invalid_argument` (şemaya uymayan yük) | | Çıktı şeması değişti | SDK 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şmuyor | `403` `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](../sdk/unity.md) sayfasındaki `PluginCall` örneğine bakın. ## Sıradaki adım - Sürüm kavramlarının tamamı (protokol, şema, kullanımdan kalkma): [sürümleme](../reference/versioning.md). - Sayı kodlaması: [sayılar ve kodlama](../reference/numbers-and-encoding.md). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/errors.html # 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 | Aile | Hangi uçlar | Gövde | |---|---|---| | Çekirdek REST | `/v1/characters…`, `/v1/wallets…`, `/v1/store…`, `/v1/auth/…` vb. ([uç noktalar](endpoints.md)) | 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/ws` | `Error` çerçevesi: `{type, requestId?, code, retryable}` ([protokol](../realtime/protocol.md)) | | Sohbet soketi | `/v1/chat/ws` | `{"type":"error","code":"…"}` ([realtime genel bakış](../realtime/overview.md)) | ## Çekirdek REST: durum tablosu | Durum | Gövde (düz metin, aksi belirtilmedikçe) | Ne zaman | Yeniden dene? | |---|---|---|---| | `400` | Hatanın açıklaması | Eksik ya da bozuk başlık, geçersiz alan, bilinmeyen komut türü, `If-Match` biçimi | Hayır; isteği düzeltin | | `401` | `unauthorized` | API 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 | | `403` | `permission denied` | Anahtar türü yetmiyor (ör. istemci anahtarıyla sunucu ucu), CSRF ya da Origin reddi | Hayır | | `403` | **JSON** `{"code":"account_banned","message":"account banned"}` | Giriş uçlarında, sahiplik kanıtlandıktan sonra yasaklı hesap | Hayır; döngü kurmayın | | `404` | `not found` | Kaynak yok ya da silinmiş | Hayır | | `404` | **JSON** `{"code":"module_disabled","message":"module disabled","module":"store"}` | Ürün modülü ortamda kapalı | Hayır; modülü açın ([modüller](../panel/game-settings-modules.md)) | | `409` | `version conflict: expected 3, actual 5` | `If-Match` sürümü gerçekle uyuşmuyor | Durumu yeniden okuyup yeni sürümle tekrar edin | | `409` | `already exists: ` | Aynı kimlik/anahtarla kaynak zaten var | Hayır | | `409` | `idempotent request in progress` | Aynı `Idempotency-Key` ile ilk istek hâlâ işleniyor | Evet, kısa süre sonra aynı anahtarla | | `410` | `idempotent request result expired` | Anahtarın kayıt süresi (24 saat) doldu | Yeni anahtarla, durumu önce okuyarak | | `413` | Gö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 | | `422` | `idempotency mismatch` | Aynı anahtar, farklı içerik | Hayır; anahtarı yeniden kullanmayın | | `422` | `unprocessable: ` | İş kuralı reddi (yetersiz bakiye, stok, limit…) | Hayır; durumu değiştirmeden tekrar etmeyin | | `422` | `rejected: ` | Eklenti iş kuralı reddi (çekirdek rotalar üzerinden) | Hayır | | `429` | `rate limited` ya da `rule budget exceeded: ` | Oran sınırı ya da kural bütçesi | Evet, bekleyerek ([hız sınırları](rate-limits.md)); **`Retry-After` yoktur** | | `500` | `internal error` | Beklenmeyen sunucu hatası; ayrıntı sızdırılmaz | Evet, sınırlı | | `503` | `tenant unavailable`; başlık `Retry-After: 30` | Ortamın yerleşimi taşınıyor ya da geçici kullanılamıyor | Evet, `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ü](../realtime/protocol.md)). - İ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). | `code` | Durum | Anlam | |---|---|---| | `not_visible` | 404 | İlan yok ya da bu oyuncuya görünmüyor | | `listing_changed` | 409 | İlanın sürümü (`expected_version`) değişti; kataloğu yenileyip yeniden gösterin | | `out_of_stock` | 422 | Stok bitti | | `limit_reached` | 422 | Oyuncu başına satın alma sınırı doldu | | `insufficient_funds` | 422 | Bakiye yetmiyor | | `inventory_full` | 422 | Envanter 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](../guides/store-iap.md). ## 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). ```json {"code":"unprocessable","message":"plugin command rejected","retryable":false,"requestId":"0192a3b4-0000-7000-8000-0000000000d1","reason":"street_reset_too_early"} ``` | `code` | Durum | `retryable` | Not | |---|---|---|---| | `unauthorized` | 401 | hayır | Oturum/anahtar yok ya da geçersiz; istemci anahtarı | | `forbidden` | 403 | hayır | CSRF/Origin reddi; eklenti etkin değil; sürüm yetkisi uyuşmuyor; yasaklı hesap (`message: account banned`) | | `module_disabled` | 404 | hayır | `module` alanı kapalı modülü söyler | | `not_found` | 404 | hayır | Bilinmeyen eklenti ya da mesaj türü | | `invalid_argument` | 400 | hayır | Bozuk/eksik başlık, bilinmeyen şema sürümü, şemaya uymayan yük | | `conflict` | 409 | hayır | `If-Match` sürümü uyuşmuyor | | `idempotency_in_flight` | 409 | **evet** | Aynı anahtarlı istek işleniyor | | `idempotency_mismatch` | 422 | hayır | Aynı anahtar, farklı gövde | | `unprocessable` | 422 | hayır | İş kuralı reddi; `reason` eklentiye özgü makine kodudur | | `idempotency_expired` | 410 | hayır | Kayıt süresi doldu | | `rate_limited` | 429 | **evet** | `Retry-After: 60` | | `tenant_unavailable` | 503 | **evet** | `Retry-After: 30` | | `internal` | 500 | **evet** | | Ayrıntı ve örnekler: [eklentileri çağırma](../plugins/calling-plugins.md). > **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 REST | Eklenti | |---|---|---| | Gövde | Düz metin (birkaç JSON istisna) | Daima JSON | | Makine kodu | Yalnız JSON olan hatalarda | Her hatada `code` | | `retryable` | Yok; durum koduna göre karar verin | Var | | Yeniden deneme süresi | Yalnız `503` (30 sn) | `429` (60 sn) ve `503` (30 sn) | | Yasaklı hesap | `403` `account_banned` (giriş uçlarında) | `403` `forbidden` | | `x-request-id` | Yok | Var | ## 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](idempotency-and-concurrency.md) 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](../operations/troubleshooting.md)). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/idempotency-and-concurrency.html # 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ı: | Kural | Değer | |---|---| | Uzunluk | 1 ile 200 karakter (boş olamaz) | | Karakter kümesi | Boşluksuz yazdırılabilir ASCII (SDK yalnız bunu üretir ve kabul eder) | | Önerilen değer | Rastgele 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üresi | 24 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ı](endpoints.md) 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: ```ts /** `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, maxAttempts = 4): Promise { 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](../plugins/bindings.md)). ## 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](../plugins/calling-plugins.md)). Ç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 yeri | Nası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ç ailesi | Yanıt | |---|---| | Çekirdek REST | `409` ve `version conflict: expected 3, actual 5` | | Eklenti | `409` ve `{"code":"conflict","message":"version conflict: expected 3, actual 5",…}` | | Mağaza oyuncu satın alma | `409` ve `{"code":"listing_changed",…}` | | Realtime komutu | `Error` ç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ı](errors.md). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/pagination.html # 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=&cursor=`. İ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. ```http GET /v1/characters?limit=2 HTTP/1.1 Authorization: Bearer crx_live_<önek>_ ``` ```json {"items":[{"id":"…","definition":"hero","version":3}],"next_cursor":""} ``` ```http GET /v1/characters?limit=2&cursor= HTTP/1.1 Authorization: Bearer crx_live_<önek>_ ``` ## Uca göre biçimler | Uç | Biçim | Varsayılan | Sınır | |---|---|---|---| | `GET /v1/characters` | İmleç (`definition` süzgeci) | 50 | 1 ile 200 arası kısılır | | `GET /v1/characters/{id}/events`, `…/timeline` | İmleç (artan `seq`) | 50 | 1 ile 200 arası kısılır | | `GET /v1/rules`, `GET /v1/webhooks` | İmleç | 50 | 1 ile 200 arası kısılır | | `GET /v1/ledger` | İmleç (`wallet_id` zorunlu) | 50 | 1 ile 200 arası kısılır | | `GET /v1/items`, `GET /v1/player/items` | İmleç (`owner` ile) | 50 | 1 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ç | 50 | 1 ile 100 arası kısılır | | `GET /v1/notifications` | İmleç (`status`, `limit`, `cursor`) | 50 | 1 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-me` | Oyuncu çevresi (`player_id`, `radius`) | — | Sayfa yok; çevre pencereleri sınırlıdır | | `GET /v1/player/leaderboards/{key}` | Tek sayfa (`limit`) | 20 | **1 ile 50; dışı `400`** | | `GET /v1/chat/channels/{channel_id}/messages` | Zaman tabanlı (`before_ms` + `limit`) | 50 | 1 ile 100 (service doğrular) | | `GET /v1/quests` | **`offset` + `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/parties` | Sayfalama 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 hata**dı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. ```text 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](endpoints.md), [hız sınırları](rate-limits.md). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/rate-limits.html # 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](../operations/production-checklist.md)). ## 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). | Kova | Limit | Pencere | Anahtar | Hangi uçlar | |---|---|---|---|---| | Misafir açma | 30 | 60 sn | İstemci IP'si | `POST /v1/auth/sign-in/guest` (yeniden giriş de sayılır) | | E-posta girişi | 5 | 60 sn | İstemci IP'si | `POST /v1/auth/sign-in/email` (her deneme) | | Başarısız giriş kilidi | 10 | 900 sn | Adres + IP | Yanlış parola denemeleri; başarılı girişte hak iade edilir; parola değiştirmede yanlış mevcut parola da sayılır | | Kayıt (IP) | 10 | 60 sn | İstemci IP'si | `POST /v1/auth/sign-up/email`, misafir terfisi | | Kayıt (oturum) | 30 | 60 sn | Oturum sahibi | `POST /v1/auth/guest/upgrade/email` | | Kayıt (adres) | 5 | 900 sn | E-posta adresi | Kayıt ve terfi | | Parola değiştirme (IP) | 10 | 60 sn | İstemci IP'si | `POST /v1/auth/change-password` | | Parola değiştirme (hesap) | 6 | 60 sn | Oyuncu | `POST /v1/auth/change-password` | | Kurtarma (IP) | 5 | 60 sn | İstemci IP'si | `forgot-password`, `reset-password`, `sign-in/magic-link` (üç uç tek kovayı paylaşır) | | Kurtarma (adres) | 5 | 900 sn | E-posta adresi | `forgot-password`, `sign-in/magic-link` | | OAuth başlangıcı | 30 | 60 sn | İstemci IP'si | `sign-in/oauth/{provider}`, `link/{provider}` | | OAuth dönüşü | 60 | 60 sn | İstemci IP'si | `callback/{provider}` | Ek olarak: | Sınır | Limit | Anahtar | Not | |---|---|---|---| | Görünen ad değiştirme | 10 / 3600 sn | Oyuncu | `POST /v1/auth/session/display-name`; aşım `429` | | Hesap silme denemesi | 5 / 3600 sn | Oyuncu | `POST /v1/auth/account/delete`; aşım `429` | | Başarısız giriş denetim satırı | 20 / 3600 sn | Hesap | Giriş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ır | Limit | Pencere | Anahtar | Aşılınca | |---|---|---|---|---| | Sohbet mesajı gönderme | 20 | 10 sn | Ortam + oyuncu | REST'te `429`; sohbet soketinde `message_rejected` | | Bildirim teslimi | 60 | 60 sn | Ortam + oyuncu | Teslim 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ır | Değer | Aşılınca | |---|---|---| | Adres başına yeni bağlantı | 30 / dakika | Yükseltme reddedilir | | Adres başına açık soket | 8 | `CapacityExceeded`, soket kapanır | | Oyuncu başına açık soket | 4 | `CapacityExceeded`, 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](../realtime/reconnect-and-limits.md). ## 429 aldığınızda ```http HTTP/1.1 429 Too Many Requests Content-Type: text/plain; charset=utf-8 rate limited ``` Eklenti ucunda: ```http 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](idempotency-and-concurrency.md)). 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: `) `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ı](../guides/game-rules.md)). ## 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](../operations/security.md)). - 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). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/numbers-and-encoding.html # 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 REST | Eklenti 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 nokta | **Hiçbir yerde yok** | **Hiçbir yerde yok** | | Para | Küçük birim tam sayısı (`amount`, `balance`) | Küçük birim, `...Minor` alanları, `int64` | | Yüzde | Baz puan (10000 = %100) | Baz puan | | Zaman | RFC 3339 metni (`created_at`) | Unix milisaniye tam sayı (`...Ms`) | | Kimlik | UUID metni | UUID metni | | Enum | `snake_case` metin | `snake_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çerli | Geçersiz | Neden | |---|---|---| | `"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 | Dil | Eşleme | |---|---| | TypeScript SDK | `int64` → `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 | Yer | Biçim | Örnek | |---|---|---| | Çekirdek REST yanıtları (`created_at`, `as_of`) | RFC 3339 | `2026-11-03T10:15:30+00:00` | | Eklenti ve realtime (`createdAtMs`, `sent_at_ms`) | Unix epoch milisaniye, tam sayı | `1793700930000` | | Sohbet sorgusu `before_ms` | Unix epoch milisaniye | `1793700930000` | | Webhook imzasındaki `t` | Unix epoch saniye | `1793700930` | | `Deprecation` başlığı | `@` (RFC 9745) | `@1793491200` | | Eklenti kullanımdan kalkma (`sinceUnixS`) | Unix saniye | `1793577600` | 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](pagination.md)). - **API anahtarları:** `crx_live_<önek>_`; önek yalnız tanımlama içindir, gizli kısım yalnız bir kez gösterilir ([güvenlik](../operations/security.md)). ## 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ı](webhook-events.md)). - 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](errors.md)). --- Source: https://open-play.b4klabs.dev/docs/tr/reference/versioning.html # 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üm | Nerede görünür | Anlamı | |---|---|---| | API yolu `/v1` | Tüm uç yolları | Tek yol sürümü; `/v2` yoktur | | Realtime `protocolVersion` | Her ç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 | | `schemaVersion` | Her 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](../plugins/bindings.md)) | | 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ı](endpoints.md) 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](../plugins/bindings.md). ## 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: | İşaret | Nerede | |---|---| | `Deprecation: @` yanıt başlığı ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)) | 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` nesnesi | Eklenti 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üzey | Başlık değeri | Yerine | |---|---|---| | `/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/unity.md)). - 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](../plugins/bindings.md)). ## 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. --- Source: https://open-play.b4klabs.dev/docs/tr/reference/endpoints.html # 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](errors.md)). Çekirdek uçlar kapatılamaz. - **Kimlik:** Sunucu anahtarı ve istemci anahtarı `Authorization: Bearer crx_live_<önek>_` başlığıyla gönderilir. Oyuncu oturumu çerezle ya da Bearer oturum jetonuyla gelir ve `x-crx-env: ` 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](../operations/security.md)). - **Idempotency-Key:** `zorunlu` ise başlık olmadan istek `400` alır; `isteğe bağlı` ise gönderirseniz tekrar güvenli olur ([idempotency](idempotency-and-concurrency.md)). **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](../plugins/calling-plugins.md) sayfasındadır. Toplam uç sayısı: **225**. ## Karakterler Modül: `characters` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/characters` | Sunucu veya istemci anahtarı | — | — | Karakterleri listeler (imleç sayfalama, `definition` süzgeci). | | `POST` | `/v1/characters` | Sunucu 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}/commands` | Sunucu anahtarı (istemci anahtarı yalnız `set_flag`) | isteğe bağlı | evet | Karakter komutunu çalıştırır (`set_flag`, `set_attribute`, `add_resource`, `add_modifier`, `remove_modifier`, `add_xp`, `rollback`). | | `GET` | `/v1/characters/{id}/events` | Sunucu veya istemci anahtarı | — | — | Karakterin olay akışını listeler (imleç sayfalama, artan `seq`). | | `GET` | `/v1/characters/{id}/state` | Sunucu veya istemci anahtarı | — | — | Karakterin `at` anındaki durumunu olayları yeniden oynatarak verir. | | `GET` | `/v1/characters/{id}/stats` | Sunucu veya istemci anahtarı | — | — | Etkin istatistikleri adım adım döküm (breakdown) ile verir. | | `GET` | `/v1/characters/{id}/timeline` | Sunucu veya istemci anahtarı | — | — | Olay akışını okunabilir özetlerle verir. | | `POST` | `/v1/definitions` | Sunucu 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-curves` | Sunucu 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/rules` | Sunucu veya istemci anahtarı | — | — | Kuralları listeler (imleç sayfalama). | | `POST` | `/v1/rules` | Sunucu anahtarı | — | — | Kural kaydeder; derlenemeyen kural hiç yazılmaz. | | `POST` | `/v1/rules/dry-run` | Sunucu 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}/disable` | Sunucu anahtarı | — | — | Kuralı değerlendirme zincirinden çıkarır. | | `POST` | `/v1/rules/{key}/enable` | Sunucu anahtarı | — | — | Kuralı değerlendirme zincirine alır. | | `POST` | `/v1/traits` | Sunucu anahtarı | — | — | Özelliği (trait) kaydeder (değişmez sürüm). | | `GET` | `/v1/catalog/characters` | Sunucu 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/auth/admin/player-wallet-movements` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Bir oyuncunun cüzdan hareketlerini listeler. | | `POST` | `/v1/currencies` | Sunucu anahtarı | — | — | Para birimi tanımlar. | | `GET` | `/v1/economy/faucet-sink` | Sunucu anahtarı | — | — | Günlük kaynak (faucet) ve gider (sink) toplamlarını verir. | | `GET` | `/v1/economy/flags` | Sunucu anahtarı | — | — | Ekonomi anomali işaretlerini listeler. | | `GET` | `/v1/economy/merchant-gap` | Sunucu anahtarı | — | — | Tüccar alış-satış farkı raporunu verir. | | `GET` | `/v1/economy/top-holders` | Sunucu anahtarı | — | — | Bir para biriminde en çok bakiyeye sahip oyuncuları listeler. | | `GET` | `/v1/ledger` | Sunucu veya istemci anahtarı | — | — | Bir cüzdanın defter (ledger) kayıtlarını listeler (imleç sayfalama). | | `POST` | `/v1/transfers` | Sunucu anahtarı | zorunlu | — | İki cüzdan arasında para aktarır. | | `GET` | `/v1/wallets` | Sunucu veya istemci anahtarı | — | — | Bir sahibin cüzdanlarını (`owner`) listeler. | ## Eşyalar Modül: `items` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/inventories/{owner}` | Sunucu veya istemci anahtarı | — | — | Sahibin envanterini okur. | | `POST` | `/v1/inventories/{owner}/equip` | Sunucu anahtarı | zorunlu | evet | Eşyayı donatır. | | `POST` | `/v1/inventories/{owner}/move` | Sunucu anahtarı | zorunlu | evet | Eşyayı envanterde başka bir yuvaya taşır. | | `POST` | `/v1/inventories/{owner}/split` | Sunucu anahtarı | zorunlu | evet | Bir yığını ikiye böler. | | `POST` | `/v1/inventories/{owner}/stack` | Sunucu anahtarı | zorunlu | evet | İki yığını birleştirir. | | `POST` | `/v1/inventories/{owner}/unequip` | Sunucu anahtarı | zorunlu | evet | Donatılmış eşyayı çıkarır. | | `POST` | `/v1/item-definitions` | Sunucu anahtarı | — | — | Eşya tanımını kaydeder. | | `GET` | `/v1/item-definitions/{key}` | Sunucu veya istemci anahtarı | — | — | Eşya tanımını okur. | | `GET` | `/v1/items` | Sunucu veya istemci anahtarı | — | — | Bir sahibin eşya örneklerini listeler (imleç sayfalama). | | `POST` | `/v1/items` | Sunucu 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}/consume` | Sunucu anahtarı | zorunlu | evet | Eşyayı tüketir. | | `POST` | `/v1/items/{id}/destroy` | Sunucu anahtarı | zorunlu | evet | Eşyayı yok eder. | | `POST` | `/v1/items/{id}/transfer` | Sunucu anahtarı | zorunlu | evet | Eşyayı başka bir sahibe aktarır. | | `GET` | `/v1/player/inventory` | Oyuncu oturumu | — | — | Oturumdaki oyuncunun envanterini okur. | | `GET` | `/v1/player/items` | Oyuncu oturumu | — | — | Oturumdaki oyuncunun eşyalarını listeler (imleç sayfalama). | | `GET` | `/v1/catalog/items` | Sunucu 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/merchants` | Sunucu anahtarı | — | — | Tüccar tanımlar. | | `GET` | `/v1/merchants/{key}` | Sunucu veya istemci anahtarı | — | — | Tüccarı okur. | | `POST` | `/v1/merchants/{key}/buy` | Sunucu anahtarı | zorunlu | — | Oyuncunun tüccardan satın almasını yürütür. | | `POST` | `/v1/merchants/{key}/sell` | Sunucu anahtarı | zorunlu | — | Oyuncunun tüccara satışını yürütür. | | `POST` | `/v1/trades` | Sunucu anahtarı | zorunlu | — | İki oyuncu arasında takas başlatır. | | `GET` | `/v1/trades/{id}` | Sunucu anahtarı | — | — | Takası okur. | | `POST` | `/v1/trades/{id}/accept` | Sunucu anahtarı | zorunlu | — | Takası kabul eder. | | `POST` | `/v1/trades/{id}/cancel` | Sunucu anahtarı | zorunlu | — | Takası iptal eder. | | `POST` | `/v1/trades/{id}/offer` | Sunucu anahtarı | zorunlu | — | Takasa teklif ekler ya da günceller. | ## Mağaza Modül: `store` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/iap/verify` | Sunucu anahtarı | zorunlu | — | Mağaza makbuzunu doğrular ve ürünü teslim eder (IAP). | | `GET` | `/v1/store/catalog` | Sunucu anahtarı | — | — | Ham mağaza kataloğunu verir (yönetim görünümü). | | `PUT` | `/v1/store/listings/{id}` | Sunucu anahtarı | zorunlu | evet | Mağaza ilanını günceller. | | `DELETE` | `/v1/store/listings/{id}` | Sunucu anahtarı | zorunlu | evet | Mağaza ilanını siler. | | `POST` | `/v1/store/listings/{listing_id}/reserve` | Sunucu anahtarı | zorunlu | evet | İlan için rezervasyon açar. | | `GET` | `/v1/store/pages` | Oyuncu oturumu | — | — | Oturumdaki oyuncunun görebildiği mağaza sayfalarını verir. | | `POST` | `/v1/store/pages` | Sunucu anahtarı | zorunlu | evet | Mağaza sayfası oluşturur. | | `POST` | `/v1/store/pages/reorder` | Sunucu anahtarı | zorunlu | evet | Mağaza sayfalarını yeniden sıralar. | | `PUT` | `/v1/store/pages/{id}` | Sunucu anahtarı | zorunlu | evet | Mağaza sayfasını günceller. | | `DELETE` | `/v1/store/pages/{id}` | Sunucu anahtarı | zorunlu | evet | Mağaza sayfasını siler. | | `POST` | `/v1/store/pages/{page_id}/listings` | Sunucu anahtarı | zorunlu | evet | Sayfaya ilan ekler. | | `POST` | `/v1/store/pages/{page_id}/listings/reorder` | Sunucu anahtarı | zorunlu | evet | Sayfadaki 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}/cancel` | Sunucu anahtarı | zorunlu | evet | Rezervasyonu iptal eder. | | `POST` | `/v1/store/reservations/{id}/checkout/currency` | Sunucu anahtarı | isteğe bağlı | evet | Rezervasyonu oyun içi para ile tahsil eder. | | `POST` | `/v1/store/reservations/{id}/checkout/iap` | Sunucu anahtarı | isteğe bağlı | evet | Rezervasyonu mağaza makbuzu ile tahsil eder. | | `POST` | `/v1/player/store/listings/{listing_id}/buy` | Oyuncu oturumu | zorunlu | evet | Oyuncu, 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/login-rewards/{player_id}` | Sunucu anahtarı | — | — | Giriş ödülü durumunu verir. | | `POST` | `/v1/login-rewards/{player_id}/claim` | Sunucu anahtarı | — | — | Giriş ödülünü teslim eder. | | `POST` | `/v1/npc-rewards/{key}/claim` | Sunucu anahtarı | zorunlu | — | NPC ödülünü teslim eder. | | `GET` | `/v1/npc-rewards/{key}/eligibility` | Sunucu anahtarı | — | — | NPC ödülüne uygunluğu sorgular. | | `POST` | `/v1/reward-tables` | Sunucu anahtarı | — | — | Ödül tablosunu kaydeder. | | `GET` | `/v1/reward-tables/{key}` | Sunucu anahtarı | — | — | Ödül tablosunu okur. | | `POST` | `/v1/reward-tables/{key}/grant` | Sunucu anahtarı | zorunlu | — | Ödül tablosundan oyuncuya ödül verir. | | `GET` | `/v1/quests` | Sunucu anahtarı | — | — | Görevleri listeler (`offset` + `limit`). | | `POST` | `/v1/quests` | Sunucu anahtarı | — | — | Görev tanımlar. | | `GET` | `/v1/quests/{key}` | Sunucu anahtarı | — | — | Görevi okur. | | `POST` | `/v1/quests/{key}/claim` | Sunucu 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}/use` | Sunucu veya istemci anahtarı | zorunlu | — | VIP kartını kullanır. | ## Sosyal Modül: `social` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/clan-applications/{application_id}` | Sunucu anahtarı | — | — | Klan başvurusunu okur. | | `POST` | `/v1/clan-applications/{application_id}/resolve` | Sunucu 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}/respond` | Sunucu anahtarı | zorunlu | — | Klan davetini yanıtlar. | | `GET` | `/v1/clan-settings` | Sunucu anahtarı | — | — | Klan çalışma ayarlarını okur. | | `PATCH` | `/v1/clan-settings` | Sunucu anahtarı | zorunlu | — | Klan çalışma ayarlarını günceller. | | `GET` | `/v1/clans` | Sunucu anahtarı | — | — | Klanları listeler. | | `POST` | `/v1/clans` | Sunucu 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}/applications` | Sunucu anahtarı | zorunlu | — | Klana başvuru oluşturur. | | `POST` | `/v1/clans/{clan_id}/disband` | Sunucu anahtarı | zorunlu | — | Klanı dağıtır. | | `POST` | `/v1/clans/{clan_id}/invites` | Sunucu anahtarı | zorunlu | — | Klana davet gönderir. | | `POST` | `/v1/clans/{clan_id}/leadership` | Sunucu anahtarı | zorunlu | — | Klan liderliğini devreder. | | `POST` | `/v1/clans/{clan_id}/leave` | Sunucu anahtarı | zorunlu | — | Üyeyi klandan çıkarır (ayrılma). | | `POST` | `/v1/clans/{clan_id}/members/kick` | Sunucu anahtarı | zorunlu | — | Üyeyi klandan atar. | | `POST` | `/v1/clans/{clan_id}/members/role` | Sunucu anahtarı | zorunlu | — | Üyenin rolünü değiştirir. | | `POST` | `/v1/clans/{clan_id}/treasury/deposit` | Sunucu anahtarı | zorunlu | — | Klan kasasına para yatırır. | | `POST` | `/v1/clans/{clan_id}/treasury/withdraw` | Sunucu anahtarı | zorunlu | — | Klan kasasından para çeker. | | `POST` | `/v1/clans/{clan_id}/xp` | Sunucu anahtarı | zorunlu | — | Klana XP ekler. | | `GET` | `/v1/parties` | Sunucu anahtarı | — | — | Bir oyuncunun gruplarını (party) listeler (`player_id`). | | `POST` | `/v1/parties` | Sunucu 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}/disband` | Sunucu anahtarı | — | — | Grubu dağıtır. | | `POST` | `/v1/parties/{party_id}/invites` | Sunucu anahtarı | — | — | Gruba davet gönderir. | | `POST` | `/v1/parties/{party_id}/leave` | Sunucu anahtarı | — | — | Üyeyi gruptan çıkarır (ayrılma). | | `POST` | `/v1/parties/{party_id}/rewards` | Sunucu anahtarı | zorunlu | — | Grup ödülünü üyelere paylaştırır. | | `POST` | `/v1/party-invites/{invite_id}/accept` | Sunucu anahtarı | — | — | Grup davetini kabul eder. | ## Sohbet Modül: `chat` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/chat/channels` | Oyuncu oturumu | — | — | Sohbet kanallarını listeler. | | `POST` | `/v1/chat/channels` | Oyuncu oturumu | — | — | Sohbet kanalı oluşturur. | | `GET` | `/v1/chat/channels/{channel_id}/messages` | Oyuncu oturumu | — | — | Kanal mesajlarını listeler (`before_ms` + `limit`). | | `POST` | `/v1/chat/channels/{channel_id}/messages` | Oyuncu oturumu | — | — | Kanala mesaj gönderir (`client_message_id` ile tekrar güvenli). | | `GET` | `/v1/chat/ws` | Oyuncu oturumu (çerez veya Bearer) | — | — | Sohbet WebSocket'i (kanal aboneliği ve mesaj gönderme). | ## Rekabet Modül: `competition` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/leaderboards` | Sunucu anahtarı | — | — | Skor tablosu tanımlarını listeler. | | `POST` | `/v1/leaderboards` | Sunucu 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-me` | Sunucu anahtarı | — | — | Bir oyuncunun çevresindeki sıraları verir. | | `GET` | `/v1/leaderboards/{key}/entries` | Sunucu 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}/scores` | Sunucu anahtarı | zorunlu | — | Oyuncu skoru gönderir. | | `POST` | `/v1/tournaments` | Sunucu anahtarı | — | — | Turnuva oluşturur. | | `GET` | `/v1/tournaments/{tournament_id}` | Sunucu veya istemci anahtarı | — | — | Turnuvayı okur. | | `POST` | `/v1/tournaments/{tournament_id}/matches/{match_id}/result` | Sunucu anahtarı | zorunlu | — | Maç sonucunu bildirir. | | `POST` | `/v1/tournaments/{tournament_id}/participants` | Sunucu anahtarı | zorunlu | — | Turnuvaya katılımcı kaydeder. | | `POST` | `/v1/tournaments/{tournament_id}/start` | Sunucu anahtarı | — | — | Turnuvayı başlatır. | ## Bildirimler Modül: `notifications` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/devices` | Oyuncu oturumu | — | — | Bildirim cihazı kaydeder. | | `DELETE` | `/v1/devices/{token}` | Oyuncu oturumu | — | — | Bildirim cihazı kaydını siler. | | `GET` | `/v1/notifications` | Oyuncu oturumu | — | — | Oyuncunun bildirim kutusunu listeler. | | `GET` | `/v1/push/config` | Kimlik gerektirmez (`x-crx-env`) | — | — | Web push için istemci yapılandırmasını verir (sırsız). | | `POST` | `/v1/notifications` | Sunucu anahtarı | — | — | Oyuncuya bildirim gönderir. | | `POST` | `/v1/notifications/{id}/read` | Oyuncu oturumu | — | — | Bildirimi okundu işaretler. | | `GET` | `/v1/ws` | Oyuncu oturumu (çerez veya Bearer) | — | — | Bildirim WebSocket'i (yalnız sunucudan oyuncuya canlı itme). | ## Webhook'lar Modül: `webhooks` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/webhooks` | Sunucu anahtarı | — | — | Webhook uç noktalarını listeler (sırsız). | | `POST` | `/v1/webhooks` | Sunucu 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/assets` | Sunucu anahtarı | — | — | Görselleri listeler (imleç sayfalama). | | `PUT` | `/v1/assets` | Sunucu anahtarı | — | — | Görsel yükler (içerik adresli, SHA-256). | | `DELETE` | `/v1/assets/{sha256}` | Sunucu anahtarı | — | — | Görseli siler (kullanımdaysa reddedilir). | | `PUT` | `/v1/scenes` | Sunucu anahtarı | — | — | Sahneyi oluşturur ya da var olanı döndürür. | ## Eklentiler ve realtime Modül: `plugins` | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/plugins/{plugin_id}/commands/{message_type}` | Oyuncu oturumu veya sunucu anahtarı (işleme göre) | zorunlu | evet | Eklenti komutunu çalıştırır (`Idempotency-Key` zorunlu). | | `GET` | `/v1/plugins/{plugin_id}/commands/{message_type}/result` | Oyuncu 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}/schema` | Oyuncu oturumu veya sunucu anahtarı (işleme göre) | — | — | Eklentinin şema belgesini (artifact) verir. | | `POST` | `/v1/realtime/tickets` | Oyuncu oturumu (çerez kipinde CSRF) | — | — | Realtime WebSocket için tek kullanımlık bilet üretir. | | `GET` | `/v1/realtime/ws` | Tek kullanımlık bilet | — | — | Realtime WebSocket'i (`openplay.realtime.v1`). | ## Çekirdek: Keşif | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/openapi.json` | Kimlik gerektirmez | — | — | Küratörlü OpenAPI belgesi (çekirdek REST yüzeyi; eklentiler ve WebSocket dahil değil). | ## Çekirdek: Oyuncu kimliği ve oturumu | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/auth/account/delete` | Oyuncu 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-password` | Oyuncu oturumu | — | — | Parolayı değiştirir; tüm oturumlar düşer. | | `GET` | `/v1/auth/csrf` | Kimlik gerektirmez (`x-crx-env`) | — | — | Yeni CSRF jetonu verir. | | `GET` | `/v1/auth/identities` | Oyuncu oturumu | — | — | Oyuncunun bağlı kimlik sağlayıcılarını listeler. | | `POST` | `/v1/auth/forgot-password` | Kimlik gerektirmez (`x-crx-env`) | — | — | Parola sıfırlama bağlantısı gönderir. | | `POST` | `/v1/auth/guest/upgrade/email` | Oyuncu oturumu | — | — | Misafir hesabı aynı oyuncu kimliğiyle e-posta hesabına yükseltir. | | `GET` | `/v1/auth/jwks` | Kimlik 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/logout` | Oturum ya da yenileme çerezi | — | — | Oturumu kapatır (bilinmeyen jeton sessizce başarılıdır). | | `GET` | `/v1/auth/magic-link/verify` | Kimlik 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/providers` | Kimlik gerektirmez (`x-crx-env`) | — | — | Ortamda etkin OAuth sağlayıcılarını listeler. | | `POST` | `/v1/auth/refresh` | Oturum ya da yenileme çerezi | — | — | Oturumu yeniler (jeton döndürme). | | `POST` | `/v1/auth/reset-password` | Kimlik gerektirmez (`x-crx-env`) | — | — | Sıfırlama jetonunu tüketir ve yeni parolayı yazar. | | `GET` | `/v1/auth/session` | Oyuncu oturumu | — | — | Oturum kapsamını ve oyuncunun profil özetini verir. | | `POST` | `/v1/auth/session/display-name` | Oyuncu oturumu | — | — | Oyuncunun görünen adını değiştirir. | | `POST` | `/v1/auth/sign-in/email` | Kimlik gerektirmez (`x-crx-env`) | — | — | E-posta ve parola ile giriş yapar. | | `POST` | `/v1/auth/sign-in/guest` | Kimlik gerektirmez (`x-crx-env`) | — | — | Misafir olarak giriş yapar (aynı sır aynı oyuncuya döner). | | `POST` | `/v1/auth/sign-in/magic-link` | Kimlik 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/email` | Kimlik 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/auth/admin/ban-player` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncuyu yasaklar. | | `POST` | `/v1/auth/admin/create-player` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Yönetici eliyle oyuncu hesabı açar. | | `GET` | `/v1/auth/admin/list-player-sessions` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun oturumlarını listeler. | | `GET` | `/v1/auth/admin/list-players` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncuları listeler (imleç sayfalama). | | `GET` | `/v1/auth/admin/player-security-events` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Bir oyuncunun güvenlik olaylarını listeler. | | `POST` | `/v1/auth/admin/rename-player` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun görünen adını değiştirir. | | `POST` | `/v1/auth/admin/reset-password` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun parolasını sıfırlar. | | `POST` | `/v1/auth/admin/revoke-player-sessions` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun tüm oturumlarını kapatır. | | `GET` | `/v1/auth/admin/security-events` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Ortamın güvenlik olay günlüğünü listeler. | | `POST` | `/v1/auth/admin/set-player-role` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun rolünü değiştirir. | | `POST` | `/v1/auth/admin/unban-player` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Oyuncunun yasağını kaldırır. | | `POST` | `/v1/auth/admin/upgrade-guest` | Sunucu anahtarı veya oyuncu oturumu (yönetici rolü) | — | — | Misafir oyuncuyu e-posta hesabına yükseltir. | ## Çekirdek: İçerik grubu kataloğu | Yöntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/catalog/groups` | Sunucu 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `GET` | `/v1/player/data` | Oyuncu 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 | — | evet | Bulut kaydı yazar; `If-None-Match: *` (oluştur) ya da `If-Match` (güncelle) zorunludur. | | `DELETE` | `/v1/player/data/{key}` | Oyuncu oturumu | — | evet | Bulut kaydı anahtarını siler; `If-Match` zorunludur. | | `GET` | `/v1/players/{player_id}/data` | Sunucu 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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açı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öntem | Yol | Kimlik | Idempotency-Key | If-Match | Açıklama | |---|---|---|---|---|---| | `POST` | `/v1/auth/organization/accept-invitation` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: organizasyon davetini kabul eder. | | `POST` | `/v1/auth/organization/add-team-member` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takıma üye ekler. | | `POST` | `/v1/auth/organization/cancel-invitation` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: daveti iptal eder. | | `POST` | `/v1/auth/organization/create` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: organizasyon kurar. | | `POST` | `/v1/auth/organization/create-team` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takım kurar. | | `POST` | `/v1/auth/organization/delete` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: organizasyonu siler. | | `POST` | `/v1/auth/organization/delete-team` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takımı siler. | | `POST` | `/v1/auth/organization/invite-member` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: üye davet eder. | | `GET` | `/v1/auth/organization/list` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: oturumdaki oyuncunun üyeliklerini listeler. | | `GET` | `/v1/auth/organization/list-members` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: üyeleri listeler. | | `GET` | `/v1/auth/organization/list-team-members` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takım üyelerini listeler. | | `GET` | `/v1/auth/organization/list-teams` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takımları listeler. | | `POST` | `/v1/auth/organization/reject-invitation` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: daveti reddeder. | | `POST` | `/v1/auth/organization/remove-member` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: üyeyi çıkarır. | | `POST` | `/v1/auth/organization/remove-team-member` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takımdan üye çıkarır. | | `POST` | `/v1/auth/organization/set-active` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: oturuma etkin organizasyonu yazar. | | `POST` | `/v1/auth/organization/update` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: organizasyonu yeniden adlandırır. | | `POST` | `/v1/auth/organization/update-member-role` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: üye rolünü değiştirir. | | `POST` | `/v1/auth/organization/update-team` | Oyuncu oturumu | — | — | Kullanımdan kalkıyor: takımı yeniden adlandırır. | --- Source: https://open-play.b4klabs.dev/docs/tr/reference/webhook-events.html # 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](../guides/webhooks.md) bakın. ## Teslim başlıkları | Header | Açıklama | |---|---| | `X-Crx-Signature` | `t=,v1=`; `v1` = `HMAC-SHA256(gizli, "{t}.{ham gövde}")`. | | `X-Crx-Event-Id` | Olay kimliği (UUID); tekrar eden teslimatları ayıklamak için kullanın. | | `X-Crx-Event-Type` | Olay türü (aşağıdaki tablodaki değerlerden biri). | ## Webhook olayları Toplam olay türü: **23**. | Olay türü | Kaynak | Ne zaman gönderilir | |---|---|---| | `flag_set` | Karakter | Bir bayrak (flag) ayarlandı. | | `attribute_set` | Karakter | Bir öznitelik değeri ayarlandı. | | `resource_changed` | Karakter | Bir kaynağın değeri değişti (yeni değer olayda yer alır). | | `modifier_added` | Karakter | Karaktere bir değiştirici (modifier) eklendi. | | `modifiers_removed` | Karakter | Bir kaynaktan gelen değiştiriciler kaldırıldı. | | `xp_added` | Karakter | Karaktere XP eklendi. | | `level_up` | Karakter | Karakter seviye atladı. | | `ability_unlocked` | Karakter | Karakterin bir yeteneği açıldı. | | `event_emitted` | Karakter | Bir kural özel bir olay yayınladı. | | `character_rolled_back` | Karakter | Karakter önceki bir sürüme geri alındı (yeni olay olarak yazılır). | | `store_page_created` | Mağaza | Mağaza sayfası oluşturuldu. | | `store_page_updated` | Mağaza | Mağaza sayfası güncellendi. | | `store_page_deleted` | Mağaza | Mağaza sayfası silindi. | | `store_pages_reordered` | Mağaza | Mağaza sayfaları yeniden sıralandı. | | `store_listing_created` | Mağaza | Mağaza ilanı oluşturuldu. | | `store_listing_updated` | Mağaza | Mağaza ilanı güncellendi. | | `store_listing_deleted` | Mağaza | Mağaza ilanı silindi. | | `store_listings_reordered` | Mağaza | Bir sayfadaki ilanlar yeniden sıralandı. | | `store_purchase_initiated` | Mağaza | Satın alma rezervasyonu açıldı. | | `store_payment_started` | Mağaza | Satın alma için ödeme başladı. | | `store_purchase_completed` | Mağaza | Satın alma tamamlandı ve ürün teslim edildi. | | `store_reservation_cancelled` | Mağaza | Satın alma rezervasyonu iptal edildi. | | `reward_granted` | Ödül | Bir ödül tablosundan oyuncuya ödül verildi. | --- Source: https://open-play.b4klabs.dev/docs/tr/reference/glossary.html # 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 | Terim | Tanı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 | Terim | Tanım | |---|---| | **Sunucu anahtarı** (server key) | `crx_live_<önek>_` 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](endpoints.md)). | | **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. | | **JWKS** | Eriş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. | | **CSRF** | Tarayı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 denetimi** | Değ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](../getting-started/architecture-and-hosting.md). | | **Oyuncu bileti** (realtime ticket) | Realtime soketine bağlanmak için alınan tek kullanımlık, 30 saniyelik kimlik belirteci ([protokol](../realtime/protocol.md)). | ## Güvenilirlik: tekrar ve çakışma | Terim | Tanı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-and-concurrency.md)). | | **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. | | **`expectedVersion`** | Beklenen kaynak sürümü: HTTP'de `If-Match` başlığı, realtime `Command` çerçevesinde alan, SDK'da seçenek. | | **Fail-open** | Bir sınırlayıcı bağımlılığına (Redis) ulaşılamazsa isteğin sınırlanmadan geçmesi ([hız sınırları](rate-limits.md)). | ## Eklentiler | Terim | Tanım | |---|---| | **Eklenti** (plugin) | Oyuna özgü komut, sorgu ve realtime modülleri sunan, sunucuya derlenmiş güvenilir paket ([genel bakış](../plugins/overview.md)). Üçü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](../plugins/bindings.md)). | | **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-int64`** | Eklenti ve realtime `int64` değerlerini ondalık metin olarak taşıyan kodlama profili ([sayılar ve kodlama](numbers-and-encoding.md)). | ## Realtime | Terim | Tanı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](../realtime/reconnect-and-limits.md)). | | **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 | Terim | Tanı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ı](../guides/game-rules.md)). | | **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ı](../guides/content-groups.md)). | | **Webhook** | Open Play'in olayları kendi sunucunuza imzalı HTTP isteğiyle bildirmesi ([webhook rehberi](../guides/webhooks.md)). | | **At-least-once** | Webhook teslimatının en az bir kez, gerekirse birden çok kez yapılması; `X-Crx-Event-Id` ile tekilleştirin. | --- Source: https://open-play.b4klabs.dev/docs/tr/operations/security.html # 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](production-checklist.md) ile birlikte okuyun. ## 1. API anahtarları İki tür anahtar vardır ve ikisi de tek bir **ortama** bağlıdır: | Tür | Nerede tutulur | Ne 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ülebilir | Hassas komutlar (ör. `add_xp`, `set_attribute`) çalışmaz; ama bazı uçlarda anahtar türü kapısı yoktur ([uç noktalar](../reference/endpoints.md)) | > **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>_`. 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ı](../panel/game-settings-api-keys.md)): | Eylem | Etki | |---|---| | Oluştur | Yeni anahtar; gizli değer bir kez gösterilir | | Devre dışı bırak / etkinleştir | Geç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 | | Sil | Yumuş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](../panel/audit.md) 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ı](../auth/server-verification.md)). ### 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](../auth/sessions-and-csrf.md) 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://` 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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil). İ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](../panel/game-settings-environments.md)). 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ık | Anlam | |---|---| | `X-Crx-Signature` | `t=,v1=` | | `X-Crx-Event-Id` | Olay kimliği (UUID); tekilleştirme anahtarı | | `X-Crx-Event-Type` | Olay türü ([webhook olayları](../reference/webhook-events.md)) | İ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 deneme | 10; 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 adresi | Yalnız `https`; özel, loopback ve link-local IP adresleri ile kullanıcı bilgisi (`kullanici:parola@`) içeren adresler kayıtta reddedilir (SSRF koruması) | | Gizli anahtar | Kayı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`): ```ts 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=,v1=`. */ 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](../panel/webhooks.md). Kayıt ve uç nokta yönetimi için [webhook rehberi](../guides/webhooks.md). ## 4. Sırların döndürülmesi: özet | Sır | Nası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](../auth/email-password.md)) | | 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](../reference/errors.md)). - **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](../panel/safety-switches.md)). - **Eklentiler** sunucuya derlenmiş güvenilir koddur; üçüncü taraf eklenti çalıştırma ve yalıtım henüz yoktur ([eklentiler](../plugins/overview.md)). - **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. --- Source: https://open-play.b4klabs.dev/docs/tr/operations/production-checklist.html # Ü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](../getting-started/architecture-and-hosting.md#tarayıcıdan-çağrı-doğrudan-cors-ya-da-ters-vekil)). - [ ] **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](../realtime/reconnect-and-limits.md)). - [ ] **İ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](../panel/game-settings-environments.md)); 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](../panel/game-settings-modules.md)). 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](../panel/game-settings-sign-in.md)). ## 3. Anahtarlar ve sırlar - [ ] **Sunucu anahtarları yalnız sunucuda.** İstemci paketinde, mobil uygulamada, WebGL derlemesinde, depoda yoktur ([güvenlik](security.md)). - [ ] 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](../reference/endpoints.md)). - [ ] 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](../reference/idempotency-and-concurrency.md)). - [ ] 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ı](../reference/rate-limits.md)). - [ ] Hata işleme **durum koduna** göre; düz metin mesajı ayrıştırılmıyor ([hatalar](../reference/errors.md)). ## 5. Oyuncu kimliği - [ ] Oturum kaybı akışı kurulu: `401` gelince oturum yenileme, olmazsa yeniden giriş; yasaklı hesap için ayrı ekran ([oturumlar ve CSRF](../auth/sessions-and-csrf.md)). - [ ] 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ı](../auth/server-verification.md)). - [ ] 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](../realtime/protocol.md)). - [ ] **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](../realtime/reconnect-and-limits.md)). - [ ] **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ış](../realtime/overview.md)). ## 7. Eklentiler - [ ] Kullandığınız eklentinin **şema özeti** (`schemaSha256`) sürüm manifestinize yazılı ve bağlamalar bu özetle üretildi ([bağlamalar](../plugins/bindings.md)). - [ ] Sunucu güncellemesinden sonra özet karşılaştırması (başlangıçta ya da CI'da) yapılıyor. - [ ] Eklentinin ortamdaki durumu **etkin** ([Eklentiler](../panel/plugins.md)); 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](../reference/versioning.md)). ## 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](security.md)). - [ ] 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](../panel/webhooks.md) 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](../panel/operators.md)). - [ ] [İşlem geçmişi](../panel/audit.md) düzenli gözden geçiriliyor; [güvenlik şalterleri](../panel/safety-switches.md) 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](../reference/versioning.md)). ## 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](troubleshooting.md) sayfasına bakın. --- Source: https://open-play.b4klabs.dev/docs/tr/operations/troubleshooting.html # 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ı](../reference/errors.md). > **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 | Belirti | Olası neden | Çözüm | |---|---|---| | Sunucu çağrısı `401 unauthorized` | `Authorization: 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 ait | Başlığı ve anahtarı kontrol edin; panelde anahtarın durumuna ve ortamına bakın ([API anahtarları](../panel/game-settings-api-keys.md)). Sunucu "yanlış anahtar" ile "iptal edilmiş"i ayırt etmez | | Sunucu çağrısı `403 permission denied` | İstemci anahtarıyla sunucu ucu çağırıyorsunuz | Sunucu anahtarı kullanın ([uç noktalar](../reference/endpoints.md): "Sunucu anahtarı" olan uçlar) | | Oyuncu çağrısı `401` | `x-crx-env` başlığı yok ya da UUID değil; oturum yok, süresi dolmuş ya da iptal; ortam uyuşmuyor | Baş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ğil | Aş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 bitiyor | 30 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 girildi | Yeniden giriş akışı kurun; davranış [güvenlik](security.md) sayfasında | | Tarayıcıda oturum çerezi hiç kurulmuyor | HTTP üzerinden çalışıyorsunuz (`Secure` + `__Secure-` öneki); API başka bir alan adında (çerez üçüncü taraf olur, `SameSite=Lax`); tarayıcı çerezleri engelliyor | HTTPS ve **aynı köken** kullanın ([mimari ve barındırma](../getting-started/architecture-and-hosting.md)) | | `429 rate limited` giriş/kayıtta | IP 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ı](../reference/rate-limits.md) ve [üretim kontrol listesi](production-checklist.md) | | E-posta girişi başarısız, parola doğru | Baş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 | Belirti | Olası neden | Çözüm | |---|---|---| | Her POST `403` | `x-csrf-token` başlığı ya da `__Secure-crx.csrf` çerezi gönderilmiyor | `GET /v1/auth/csrf` ile jeton alın; çerezi ve başlığı birlikte gönderin. SDK bunu otomatik yapar | | İlk POST geçiyor, ikincisi `403` | CSRF 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önderdiniz | Her 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ışıyor | `Origin` ortamın izinli kökenlerinde değil; `Sec-Fetch-Site: cross-site` ile gezinme isteği | Oyunun alan adını ortam alan adı olarak tanımlayın ([Oyun ayarları: ortamlar](../panel/game-settings-environments.md)); 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://` dışında bir origin (`http`, port, `null`) | Alan adını ortam alan adı olarak ekleyin ([Oyun ayarları: ortamlar](../panel/game-settings-environments.md)); olmuyorsa aynı köken ters vekil kurun. Vekilinize ek CORS başlığı eklemeyin | ## Çağrı hataları | Belirti | Olası neden | Çözüm | |---|---|---| | `404` ve JSON `module_disabled` (`module` alanı) | Ürün modülü ortamda kapalı | Panelde modülü açın ([modüller](../panel/game-settings-modules.md)) | | `404 not found` | Kaynak yok, silinmiş ya da yanlış ortam | Kimliği ve ortamı kontrol edin | | `400` "Idempotency-Key zorunlu" benzeri mesaj | Anahtar zorunlu uçta başlık yok ya da boş/200+ karakter | Başlığı ekleyin ([idempotency](../reference/idempotency-and-concurrency.md)) | | `400` "geçerli UUID olmalı" | Klan, grup ya da turnuva ucunda anahtar UUID değil | UUID v4 üretin | | `400` `If-Match` hatası | `*`, `W/"3"`, negatif ya da sayısal olmayan değer | Tam sürüm numarası gönderin (`3` ya da `"3"`) | | `409 version conflict` | Kaynak siz okuduktan sonra değişti | Yeniden okuyun, yeni sürümle tekrar edin | | `409 idempotent request in progress` | Aynı anahtarlı istek hâlâ işleniyor | Kısa süre sonra **aynı anahtarla** tekrar edin | | `422 idempotency mismatch` | Aynı anahtarı farklı gövdeyle kullandınız | Her 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 | | `410` | Anahtarın 24 saatlik kayıt süresi doldu | Durumu okuyup işlem uygulanmadıysa yeni anahtarla yeniden başlatın | | `413` | Gövde 2 MiB üstü | Gövdeyi küçültün; görsel için görsel yükleme ucunu kullanın | | `429` | Oran sınırı ya da kural bütçesi | Bekleyip aynı anahtarla tekrar edin; kural bütçesi için kuralı sadeleştirin | | `503` ve `Retry-After: 30` | Ortamın yerleşimi geçici kullanılamıyor | `Retry-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](../reference/numbers-and-encoding.md)) | | `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 | Belirti | Olası neden | Çözüm | |---|---|---| | `403` ve `forbidden` | Eklenti ortamda etkin değil (salt okunur, boşaltılıyor, kapalı) ya da ortamın eklenti sürümü sunucudakiyle uyuşmuyor; CSRF/Origin reddi | Panelde eklentinin durumuna ve sürümüne bakın ([Eklentiler](../panel/plugins.md)) | | `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_found` | Eklenti 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_flight` | Aynı anahtarlı komut işleniyor | `retryable: true`; bekleyip aynı anahtarla tekrar edin | | `422` `unprocessable` + `reason` | Eklenti iş kuralı reddi | `reason` eklentiye özgü koddur; eklentinin belgesine bakın; tekrar etmek işe yaramaz | | Komut sonucu belirsiz | Ağ koptu | `…/result?idempotency_key=…` ile sorgulayın ya da aynı anahtarla tekrar edin ([eklentileri çağırma](../plugins/calling-plugins.md)) | | SDK `upgrade_required` (`plugin_binding_mismatch`) | Bağlamanın sabitlemesi (pin) elinizdeki beklentiyle ya da sunucudaki belgeyle uyuşmuyor | Güncel belgeyi alıp bağlamayı yeniden üretin ([bağlamalar](../plugins/bindings.md)) | | Ü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`'tir | `package.json`'da takma ad tanımlayın: `"@open-play/sdk": "npm:@ferdiunal/open-play-ts@"` | | `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 | Belirti | Olası neden | Çözüm | |---|---|---| | Bilet isteği `403` | Kimlik, 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 `401` | Sorgu dizesi var, `openplay.realtime.v1` alt protokolü istenmedi ya da `Origin` izinli değil | [Protokol: soketi aç](../realtime/protocol.md) | | Hemen `AuthenticationFailed` ve kapanma | Bilet kullanılmış (tek kullanım), 30 sn dolmuş ya da farklı `Origin`'den alınmış; 5 sn içinde `Authenticate` gelmedi | Her bağlantıda yeni bilet; soket açılınca hemen `Authenticate` gönderin | | `UnexpectedFrame` | Yanlış fazda çerçeve (kimlik doğrulamadan önce `Join`, ikinci `Authenticate`) | Sırayı [protokole](../realtime/protocol.md) göre kurun | | `InvalidPayload` | Bozuk JSON, bilinmeyen alan, şemaya uymayan yük, `int64` sayı olarak gönderildi | Sayaç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 soket | Gönderimi azaltın; eski sekmeleri kapatın | | Soket 5 dakikada kapanıyor (`4100`) | Boşta kapanma | "Devam et" akışı; canlı tutma girdisi ([limitler](../realtime/reconnect-and-limits.md)) | | Girdiler `Ack: Stale` | Aynı 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 `401` | Bu iki soket yükseltmede `x-crx-env` başlığı ister; tarayıcı başlık ekleyemez | Tarayıcıda REST kullanın ([realtime genel bakış](../realtime/overview.md)) | ## Webhook'lar | Belirti | Olası neden | Çözüm | |---|---|---| | Uç noktama hiç istek gelmiyor | Uç 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ğil | Panelde uç noktayı ve modülü kontrol edin ([Webhook ve olay kuyruğu](../panel/webhooks.md)) | | Kayıt `400` | Adres `https` değil; özel/loopback IP ya da kullanıcı bilgisi içeriyor | Genel erişilebilir `https` adres verin ([güvenlik](security.md)) | | İmza uyuşmuyor | Gövdeyi ayrıştırıp yeniden serileştirdiniz (ham bayt gerekir); gizli anahtar yanlış (`whsec_…` metnin kendisi, base64 değil); `t.` önekini unuttunuz | Ham gövdeyi doğrulayın; örnek: [güvenlik](security.md) | | Zaman damgası reddi | Sunucunuzun saati sapmış | Saati NTP ile eşitleyin; pencereyi 5 dakikada tutun | | Aynı olay iki kez geldi | Teslimat en az bir kez (at-least-once) | `X-Crx-Event-Id` ile tekilleştirin | | Teslimatlar `failed` | 10 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 gelmedi | Gövde 64 KiB üstü gönderilmez | Olay boyutunu küçültün | ## Unity | Belirti | Olası neden | Çözüm | |---|---|---| | Paket kurulmuyor | `~/.upmconfig.toml` ya da kapsamlı kayıt defteri yapılandırması eksik; belirteçte `read:packages` yok | [Unity SDK](../sdk/unity.md) kurulum adımları | | WebGL'de istekler engelleniyor | Oyun ve API aynı kökende değil; Unity WebGL SDK'sı bugün aynı kökeni şart koşar | Aynı köken ters vekil kurun ([Unity SDK](../sdk/unity.md)) | | `UpgradeRequired` (`plugin_binding_mismatch`) | Pakete gömülü `package-ride` bağlaması sunucudakiyle uyuşmuyor | SDK'yı güncelleyin | | Çalışma zamanında oturum geri yüklenmiyor | Gizli saklama varsayılanı güvenli değil (`PlayerPrefs`) | [Unity SDK](../sdk/unity.md): 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](../reference/errors.md) bulun. 3. [Üretim kontrol listesindeki](production-checklist.md) ağ maddelerini (başlık iletimi, istemci IP'si, HTTPS) gözden geçirin. 4. Destek talebine yukarıdaki bilgileri ekleyin. --- Source: https://open-play.b4klabs.dev/docs/tr/operations/faq.html # 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](security.md), [uç noktalar](../reference/endpoints.md)). **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://` 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](../getting-started/architecture-and-hosting.md)). **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](../plugins/calling-plugins.md), [protokol](../realtime/protocol.md)). Unity için [Unity SDK](../sdk/unity.md). **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](../reference/endpoints.md)). Ayrıntı [TypeScript SDK](../sdk/typescript.md) ve [Unity SDK](../sdk/unity.md) 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ı](../auth/server-verification.md)). 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](security.md)). **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](../reference/errors.md)). ## 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](../reference/idempotency-and-concurrency.md)). **`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](../reference/numbers-and-encoding.md)). **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](../reference/numbers-and-encoding.md)). **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](../reference/errors.md)). ## 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ı](../reference/rate-limits.md)). **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](production-checklist.md)). ## 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ış](../plugins/overview.md)). **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](../plugins/bindings.md)). **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](../panel/plugins.md)). ## 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](../realtime/reconnect-and-limits.md)). **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ış](../realtime/overview.md)). ## 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](security.md)). **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](security.md)). **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](../panel/plugins.md) bakın. **Dokümanda bulamadığım bir şey var.** Önce [sorun giderme](troubleshooting.md) ve [sözlüğe](../reference/glossary.md) bakın; bulamazsanız sorunuzu durum kodu, yol ve zaman bilgisiyle iletin. --- Source: https://open-play.b4klabs.dev/docs/tr/panel/dashboard.html # 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: | Kart | Gösterdiği | Aç düğmesi | |---|---|---| | Oyuncular | Seçili ortamdaki toplam oyuncu hesabı sayısı | [Oyuncular](players.md) | | Oyuncu karakterleri | Karakter sayısı; liste uzunsa sayının sonunda "+" görünür | [Karakterler](characters.md) | | İçerik | Karakter şablonu ve eşya tanımı sayısı | İçerik | | Kurallar | "açık kural / toplam kural" (örneğin 3/5) | Kurallar | | Eklenti | Oyuna bağlı eklentinin durumu: Açık, Salt okunur, Drain (kapanıyor, kalan saniye ile), Yetki yok ya da Eklenti yok | Eklentiler | "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 | Durum | Ne yapmalı | |---|---| | Kartta "Bu sayıyı görme yetkiniz yok." yazıyor | Hesabınızın rolü o alanı okuyamıyor; bir yöneticiden rolünüzü genişletmesini isteyin. | | Kartta "Sayı alınamadı." yazıyor | Geç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üyor | Oyun istemciniz henüz giriş yapmamıştır; ilk oyuncu girişinden sonra sayı güncellenir. | | Eklenti kartında "Yetki yok" yazıyor | Eklentinin 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 - [Temel kavramlar](../getting-started/concepts.md) - [Open Play'e genel bakış](../getting-started/overview.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/players.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Görünen ad | Evet | Oyuncunun görünen adı; en az 2 karakter | | E-posta | Hayır | Oyuncunun e-posta adresi | | E-posta doğrulandı | Hayır | Adresin doğrulanmış sayılmasını sağlar | | Geçici parola | Hayır | En az 12 karakter; boşsa parolalı hesap oluşmaz | | Metadata JSON | Hayır | Ek bilgiler, JSON nesnesi | | İşlem nedeni | Evet | İş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 | Durum | Ne yapmalı | |---|---| | Liste boş görünüyor | Doğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin. | | Oyuncuyu bulamıyorum | Misafir hesapların e-postası olmaz; adı, kimliğini ya da giriş yöntemini kullanarak arayın. | | "Kullanıcı oluştur" düğmesi yok | Hesabınızın rolünde hesap oluşturma yetkisi yoktur; bir yöneticiden isteyin. | | Form "metadata" hatası veriyor | Alana geçerli bir JSON nesnesi yazın; dizi ya da düz metin kabul edilmez. | ## İlgili dokümanlar - [Kimlik doğrulamaya genel bakış](../auth/overview.md) - [Hesap bağlama](../auth/account-linking.md) - [Güvenlik](../operations/security.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/characters.html # 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](game-settings-modules.md) 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](character-profile.md). ### 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Tanım (ya da Tanım anahtarı) | Evet | Karakterin üretileceği tanım; güncel sürümü kullanılır | | Dış kimlik | Hayır | Oyun sunucunuzla eşleştirme kimliği; aynı kimlikle ikinci karakter oluşturulamaz | | İşlem nedeni | Evet | İş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 | Durum | Ne yapmalı | |---|---| | Liste boş görünüyor | Doğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin. | | "Bu ortamda henüz tanım yok" yazıyor | Karakter 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 yok | Hesabınızın rolünde karakter oluşturma yetkisi yoktur; bir yöneticiden isteyin. | ## İlgili dokümanlar - [Karakterler](../guides/characters.md) - [Temel kavramlar](../getting-started/concepts.md) - [Kimlik doğrulamaya genel bakış](../auth/overview.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/clans.html # 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 | | Üyeler | Oyuncu, rol, katılım ve ayrılış zamanı. Ayrılmış üyeler de listelenir ve "Ayrıldı" rozetiyle işaretlenir | | Davetler | Davet edilen oyuncu, durum ve son geçerlilik | | Başvurular | Başvuran oyuncu, durum ve oluşturulma zamanı | | Kasa transferleri | Yatırma ya da çekme, miktar, işlemi yapan oyuncu, bakiyeler ve para birimi. Tutarlar metin olarak gösterilir | | Komut geçmişi | Tamamlanmış komutun türü, istek özeti (hash) ve zamanı; yanıt gövdesi gösterilmez | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Liste boş görünüyor | Doğru ortamı seçtiğinizi ve filtrelerin kapalı olduğunu kontrol edin. | | Üye sayısı ile satır sayısı uyuşmuyor | Ayrılmış üyeler de listelenir; "Ayrıldı" rozetli satırlar aktif üye sayısına girmez. | | Panelden klanı düzenleyemiyorum | Sayfa 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ıyor | Rolünüzün klan okuma yetkisi yoktur; bir yöneticiden isteyin. | ## İlgili dokümanlar - [Klanlar ve partiler](../guides/social.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/parties.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Dağıtım kimliği | Evet (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 | Durum | Ne yapmalı | |---|---| | Liste boş görünüyor | Doğ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ıyor | Kimlik başka bir partiye ya da ortama ait olabilir; doğru partiyi seçtiğinizden emin olun. | | Panelden partiyi değiştiremiyorum | Sayfa salt okunurdur; parti işlemleri oyundan ya da sunucu çağrılarıyla yapılır. | ## İlgili dokümanlar - [Klanlar ve partiler](../guides/social.md) - [Ödüller](../guides/rewards.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/scene-chat.html # Ş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](player-profile.md) sayfasındaki "Banla" işlemini kullanın. ## Alanlar ve seçenekler ### Susturma penceresi | Alan | Zorunlu | Açıklama | |---|---|---| | Süre | Evet | 5 dakika, 1 saat, 24 saat, 7 gün ya da Özel bitiş | | Bitiş anı | Yalnız özel sürede | Gelecekte olmalı, en çok 30 gün sonra | | İşlem nedeni | Evet | 8-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 | Durum | Ne yapmalı | |---|---| | "Bu oyunda sahne sohbeti yok" yazıyor | Seçili oyunun eklentisi sahne sohbeti sunmuyor; sahne bildiren bir eklenti bağlanınca sayfa çalışır. | | Sil ve Sustur düğmeleri görünmüyor | Rolünüzün sohbet yazma yetkisi yoktur; yalnızca okuyabilirsiniz. | | Özel bitiş hata veriyor | Bitiş gelecekte olmalı ve 30 günden uzak olmamalı. | | Eski mesajı bulamıyorum | Mesajlar 30 gün sonra otomatik silinir. | | Susturulan oyuncu yeniden yazıyor | Misafir hesap yeni bir misafir açmış olabilir; hesabı yasaklamayı düşünün. | ## İlgili dokümanlar - [Sohbet](../guides/chat.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/organizations.html # 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](tenancy.md) 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](clans.md) 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 | Alan | Pencere | Açıklama | |---|---|---| | Ad, Plan | Organizasyon | Ad ve plan metni (plan en çok 64 karakter) | | Built-in rol, Dinamik roller | Üyeyi düzenle | Yerleşik rol ve ek dinamik roller | | E-posta, Rol, Son geçerlilik | Davet | Rol admin ya da member; geçerlilik en çok 30 gün | | Yeni son geçerlilik | Daveti yeniden gönder | Yeni son tarih | | Takım adı | Takım | Takımın adı | | Oyuncu | Takıma üye ekle | Organizasyon üyelerinden biri | | Rol adı, Permissions JSON | Dinamik rol | Rol adı ve izin nesnesi | | İşlem nedeni | Hepsi | 8-500 karakter | | Onay metni | Silme ve çıkarma | Kaydın adını aynen yazın; ad yoksa "SİL" yazılır | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Sayfayı menüde göremiyorum | Kullanı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ı veriyor | Son 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 ekleyemiyorum | Yalnızca organizasyonun üyeleri takıma eklenebilir; önce üye olarak ekleyin. | | Yerleşik rolü silemiyorum | Yerleşik roller silinemez; yalnızca dinamik roller silinir. | ## İlgili dokümanlar - [Klanlar ve partiler](../guides/social.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/sessions.html # 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](players.md) 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](player-profile.md) 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 | Durum | Ne yapmalı | |---|---| | Oturumu bu sayfadan kapatamıyorum | Sayfa yalnızca özettir; kapatma işlemi oyuncu profilindedir. | | Oturumları kapatma seçeneği görünmüyor | Rolünüzün oturum iptal yetkisi yoktur; bir yöneticiden isteyin. | | Oyuncu yeniden giriş yapabildi | Oturumları kapatmak girişi engellemez; girişi engellemek için hesabı yasaklayın. | ## İlgili dokümanlar - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) - [Kimlik doğrulamaya genel bakış](../auth/overview.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/content.html # İç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](content-group-edit.md) 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](content-entry-edit.md) 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 | Alan | Nerede | Açıklama | |---|---|---| | Ara | Kayıt listesi (kart görünümü) | Kayıt adı ya da anahtarıyla arar | | Taslak değişikliği olanlar | Kayı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ı](character-templates.md) sayfasına gider. - **3D model kütüphanesi:** [3D modeller](models.md) sayfasına bağlantı. Sahneler modülü kapalıysa bağlantı yerine nedeni yazılır. ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | "Yeni grup" düğmesi yok | Hesabınızın rolünde içerik yazma yetkisi yoktur; bir yöneticiden isteyin. | | Yayınla düğmesi çalışmıyor, "Engelleyici" görüyorum | Engelleyici 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üyor | Değişiklik taslakta kalmıştır; grubu **Yayınla**. | | 3D model kütüphanesi bağlantısı görünmüyor | Sahneler modülü bu ortamda kapalıdır; Oyun ayarları içindeki modüller sekmesinden açın. | ## İlgili dokümanlar - [Grubu düzenle](content-group-edit.md) - [Kayıt](content-entry-edit.md) - [Karakter şablonları](character-templates.md) - [3D modeller](models.md) - [İçerik grupları rehberi](../guides/content-groups.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/character-templates.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ad | Evet | Oyuncunun göreceği ad | | Anahtar | Evet | Küçük harf, rakam, alt çizgi; kayıttan sonra sabit | | Tür | Evet | Tüketilebilir, Silah / ekipman, Malzeme, Kozmetik, VIP kartı | | Yığınlanabilir | Evet | Evet ya da Hayır | | En çok yığın | Yığınlanabilirse | 1 ile 2147483647 | | Takas edilebilir | Evet | Evet ya da Hayır | | NPC alış fiyatı, NPC satış fiyatı | Hayır | Boşsa o yönde ticaret kapalı; 0 bedava | | 3D model | Hayır | Yayınlanmış model | | Değerler | Hayır | Oyun verisi satırları | | İşlem nedeni | Evet | En az 8 karakter | ### Karakter tanımı formu | Alan | Zorunlu | Açıklama | |---|---|---| | Tanım anahtarı | Evet | Kayıttan sonra sabit | | Özellik anahtarları | Evet | En az bir; sıra önemli | | Üzerine yazılacak değerler | Hayır | Bölüm, ad, alan ve değer satırları | | Görünen ad | Hayır | Oyunda görünen ad | | 3D model | Hayır | Yayınlanmış model | | Oyun verisi | Hayır | Ek değerler | | İşlem nedeni | Evet | En 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 | Durum | Ne 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 yok | Hesabı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ı silemiyorum | Bir 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" yok | Silah ayrı tür değildir; "Silah / ekipman" türüdür. | ## İlgili dokümanlar - [İçerik](content.md) - [3D modeller](models.md) - [Karakterler rehberi](../guides/characters.md) - [Envanter ve eşyalar rehberi](../guides/inventory-items.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/models.html # 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](model-profile.md)). ## 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](model-profile.md) **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 | Alan | Zorunlu | Açıklama | |---|---|---| | Yükleme türü | Evet | Tek dosya ya da zip paket | | Dosya | Evet | En çok 50 MiB | | Ad | Hayır | Boşsa dosya adı kullanılır, en çok 200 karakter | | Ana dosya | Hayır | Yalnızca zip kipinde; birden çok model dosyası varsa zip içindeki yol | | Birim | Biçime göre | Mikron, milimetre, santimetre, inç, fit, metre | | Yukarı eksen | Biçime göre | Y 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 | Durum | Ne yapmalı | |---|---| | Model "Başarısız" durumda | Modelin 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 bilmiyor | **Ana 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üyor | Yükleme sırasında birimi yanlış seçmişsinizdir; modeli yeniden yükleyin. | | Model yükle düğmesi yok | Hesabı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 - [Model profili](model-profile.md) - [İçerik](content.md) - [Scene, görsel ve 3B modeller rehberi](../guides/scenes-assets-models.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/store.html # 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](store-product.md) 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](currencies.md) ve satılacak bir eşya (İçerik ya da [Karakter şablonları](character-templates.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ad | Evet | En çok 120 karakter | | Anahtar | Evet | Küçük harf, rakam, tire, alt çizgi; en çok 64 karakter | | Açıklama | Hayır | En çok 500 karakter | | Vitrin afişi | Hayır | PNG, JPEG ya da WebP görsel | | Durum | Evet | Taslak ya da Yayında | | Ne zaman satışta? | Evet | Hemen ya da tarih aralığı | | Başlangıç, Bitiş | Aralıkta | Boş uç sınırsızdır | | İşlem nedeni | Evet | 8-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 | Durum | Ne 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](currencies.md) 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ıyor | Oyun vitrini anahtarıyla açar; oyundaki çağrıyı da yeni anahtarla güncelleyin. | ## İlgili dokümanlar - [Ürün](store-product.md) - [Para birimleri](currencies.md) - [Mağaza ve uygulama içi satın alma rehberi](../guides/store-iap.md) - [Ekonomi rehberi](../guides/economy.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/currencies.html # 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](currency-profile.md)). ## 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](currency-profile.md) 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](currency-profile.md) sayfasında anlatılır. Kartta görünen bilgiler: | Bilgi | Açıklama | |---|---| | Ad ve ikon | Oyuncuların gördüğü ad ve görsel | | Anahtar | Oyunun para birimini bulduğu kimlik | | Kuruş hanesi | Tutarın kaç ondalık haneyle gösterileceği | | Arz tavanı | Toplam basılabilecek miktar; yoksa "Sınırsız" | | Gönderilebilir | Oyuncuların birbirine gönderip gönderemediği | ## Sık karşılaşılan durumlar | Durum | Ne 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 yok | Hesabınızın rolünde para birimi yazma yetkisi yoktur. | ## İlgili dokümanlar - [Para birimi](currency-profile.md) - [Ekonomi raporları](economy.md) - [Mağaza](store.md) - [Ekonomi rehberi](../guides/economy.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/reward-tables.html # Ö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](reward-table-profile.md)). ## 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](reward-table-profile.md) 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](../guides/rewards.md) bakın. ## Alanlar ve seçenekler Bu sayfada doldurulacak bir form alanı yoktur; oluşturma formu [Ödül tablosu](reward-table-profile.md) sayfasında anlatılır. Kartta görünen bilgiler: | Bilgi | Açıklama | |---|---| | Ad ve anahtar | Tablonun 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ım | Tablonun kaç yerde kullanıldığı; hiç kullanılmıyorsa "Kullanılmıyor" | ## Sık karşılaşılan durumlar | Durum | Ne 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 yok | Hesabınızın rolünde ödül tablosu yazma yetkisi yoktur. | ## İlgili dokümanlar - [Ödül tablosu](reward-table-profile.md) - [Para birimleri](currencies.md) - [Ödüller rehberi](../guides/rewards.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/economy.html # 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 nedeni**ni 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](currency-profile.md) 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 | Alan | Nerede | Açıklama | |---|---|---| | Sahip türü | Sahip sorgusu | Oyuncu, karakter, klan ya da sistem | | Sahip (arama) | Sahip sorgusu | Ada göre arayıp seçin | | Sahip kimliği | Sahip sorgusu | Yalnızca sistem sahibi için | | Cüzdan kimliği | Sahip sorgusu | Cüzdanları görme yetkisi yoksa; UUID | | Tutar | Bakiye penceresi | Para biriminin ondalık haneleriyle | | İşlem nedeni | Bakiye penceresi | En az 8 karakter | | Para birimi anahtarı | Günlük raporlar | Örneğin gold | | Başlangıç, Bitiş | Günlük raporlar | Kapanmış günler | ## Sık karşılaşılan durumlar | Durum | Ne 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österiyor | Hesabı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 reddedildi | Cü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 - [Para birimleri](currencies.md) - [Para birimi](currency-profile.md) - [Ekonomi rehberi](../guides/economy.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/rules.html # 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](./rules-history.md)). ## 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ı](./rules-game-card.md)). 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](./rules-automation-editor.md)). 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Kural ara | Hayır | Kart başlığı, açıklaması ve otomasyon anahtarında arar | | Konu düğmeleri | Hayır | Listeyi seçtiğiniz konulara daraltır; hiçbiri seçili değilse hepsi görünür | | İşlem nedeni (aç/kapat penceresi) | Evet | 8-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 | Durum | Ne yapmalı | |---|---| | "Bu oyunun eklentisi panelden ayarlanabilir kural sunmuyor" yazıyor | Oyunun eklentisi kural kartı bildirmiyordur. Otomasyonları yine bu sayfadan ekleyebilirsiniz. | | "Kuralları görmek için yetkiniz yok" yazıyor | Rolünüzde kural okuma yetkisi yoktur; bir yöneticiden isteyin. | | **Yeni kural** düğmesi yok | Düğmeyi yalnızca kural yazma yetkisi olan yöneticiler görür. | | Kartta "Gözlemde" rozeti var | Kuralın koruması gevşetilmiştir; kural oyuna uygulanmıyor, yalnızca kaydediliyor. Ayrıntı için [Güvenlik şalterleri](./safety-switches.md) sayfasına bakın. | | Kartta "Yeniden başlatınca" rozeti var | Değ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 - [Oyun kuralları rehberi](../guides/game-rules.md) - [Bildirimler rehberi](../guides/notifications.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/leaderboards.html # 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ı](./leaderboard-detail.md)). ## Alanlar ve seçenekler | Alan | Zorunlu | Açıklama | |---|---|---| | Tablonun adı | Evet | 1-400 karakter | | Anahtar | Evet | Küçük harf ya da rakamla başlar; küçük harf, rakam, nokta, tire, alt çizgi; en fazla 200 karakter | | Hangi puan önde? | Evet | Yüksek puan ya da düşük puan | | Ne zaman sıfırlansın? | Evet | Hiç, her gün, her hafta (Pazartesi), her ay ya da özel aralık | | Gün, Saat | Yalnızca özel aralıkta | Gün ya da saatten en az biri; toplam en az 1 saat, tam sayı | | Puan nasıl gösterilsin? | Evet | Sayı, para ya da süre | | Birim eki | Hayır | En fazla 8 karakter; süre biriminde yoktur | | Oyunda görünsün | Hayır | Varsayılan kapalı | ## Sık karşılaşılan durumlar | Durum | Ne 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ıyor | Eklenti tabloyu ilk teslimatta kurar; henüz teslimat olmamıştır. Beklemeniz yeterlidir. | | "Sıralama (Competition) modülü bu ortamda kapalı" | [Oyun ayarları › Modüller](./game-settings-modules.md) sayfasından modülü açın. | | "Yeni tablo" düğmesi yok | Tablo yönetme yetkiniz yoktur; bir yöneticiden isteyin. | | Yalnızca ilk tabloları görüyorum | Liste 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ıyor | Tabloyu bir eklenti kurmuştur ve yalnız o eklenti puan yazar. | ## İlgili dokümanlar - [Skor tabloları ve turnuvalar](../guides/competition.md) - [Bildirimler](../guides/notifications.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/notifications.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Şablon anahtarı, görünen ad, açıklama, dil | Anahtar, ad, dil zorunlu | Anahtar değişmez; ad en fazla 120, açıklama 500 karakter | | Başlık, Metin | Evet | Başlık için 65, metin için 240 karakterlik yumuşak sınır | | Değişken adı, tür, örnek değer, zorunlu | Değişken eklenince | En fazla 32 değişken; sayı değişkenine tam sayı | | Sürüm notu | Hayır | En fazla 200 karakter | | Kural anahtarı, kural adı | Evet | Anahtar en fazla 64 karakter, değişmez; ad en fazla 120 | | Ne zaman, koşullar | Ne zaman zorunlu | En fazla 20 koşul satırı; koşul yoksa olay her olduğunda çalışır | | Şablon, dil, kanal | Evet | Kanal: oyun içi, anlık bildirim ya da ikisi | | Sağlayıcı bilgileri ve anahtar dosyası | Evet | Firebase: hizmet hesabı JSON; Apple: Team ID, Key ID, Bundle ID, ortam ve .p8; OneSignal: App ID ve REST API anahtarı | | İşlem nedeni, doğrulama kodu | Evet | Neden 8-500 karakter; kod 6 hane | Bir ortamda en fazla 50 bildirim kuralı olabilir. ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | "Bu oyunda bildirimler kapalı" | [Oyun ayarları › Modüller](./game-settings-modules.md) sayfasından Bildirimler modülünü açın. | | Kartta "Bağlı değil" yazıyor | Sağ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" diyor | Oyuncu 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ışıyor | Panelde ilk kuralı eklediğinizde bu ortamda onların yerini panel kuralları alır. | | Kural ya da şablon düzenlenemiyor | Bildirim yönetimi yetkiniz yoktur; görünüm salt okunurdur. Bir yöneticiden isteyin. | ## İlgili dokümanlar - [Bildirimler rehberi](../guides/notifications.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/game-settings-general.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Oyun adı | Salt okunur | Yeniden adlandırma penceresinden değişir | | Kısa ad | Salt okunur | Oyun kurulurken verilir, değişmez | | Stüdyo | Salt okunur | Oyunun bağlı olduğu stüdyo | | Eklenti | Salt okunur | Eklenti yoksa "Eklenti yok" yazar; yetkiniz varsa Eklentiler sayfasına bağlantı çıkar | | Yeni ad (yeniden adlandırma penceresi) | Evet | Mevcut addan farklı olmalı | | İşlem nedeni (pencerelerde) | Evet | 8-500 karakter | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | "Ortam seçilmedi" yazıyor | Üst çubuktan bir oyun ve ortam seçin. | | Yeniden adlandır ve Arşivle düğmeleri yok | Bu 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ıyor | Arşivlemek istediğiniz oyunun ortamı şu an seçili. Başka bir oyunun ortamını seçip yeniden deneyin. | | Arşivleme reddedildi | Oyunun canlı yetkisi ya da etkin bir API anahtarı vardır. Pencerede yazan nedene göre etkin [API anahtarlarını](./game-settings-api-keys.md) iptal edin ya da [Eklentiler](./plugins.md) sayfasından eklentinin durumunu kapatın, sonra yeniden deneyin. | ## İlgili dokümanlar - [Temel kavramlar](../getting-started/concepts.md) - [Mimari ve barındırma](../getting-started/architecture-and-hosting.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/plugins.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Yeni durum | Evet | Açık, Salt okunur, Boşaltılıyor ya da Kapalı | | İşlem nedeni | Evet | 8-500 karakter | | Eklenti (bağlama penceresi) | Evet | Sunucuda kurulu eklentilerden biri | | Eklenti kimliği (boşaltma penceresi) | Evet | Kimlik 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 | Durum | Ne 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ı var | Ortam 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 yok | Eklenti yönetme yetkiniz yoktur; bir yöneticiden isteyin. | | **Ekranları aç** düğmesi yok | Eklenti panel ekranı tanımlamıyor ya da bu ortamda kapalı. | | "Kullanımdan kalkan sorgular" kutusu görünüyor | Eklentinin 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 - [Eklentilere genel bakış](../plugins/overview.md) - [Eklenti çağırma](../plugins/calling-plugins.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/safety-switches.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | İşlem nedeni (gevşetme penceresi) | Evet | 8-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 | Durum | Ne 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üyorum | O kartın şalteri gevşektir. Bu sayfadan ya da [Kurallar](./rules.md) sayfasındaki kart sayfasından **Zorla** diyerek yeniden açın. | | Neden sorulmadan değişti | Zorla 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 - [Oyun kuralları ve otomasyonlar](../guides/game-rules.md) - [Güvenlik](../operations/security.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/audit.html # İş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 | Alan | Zorunlu | Açıklama | |---|---|---| | Süzgeçler ve arama | Hayır | Eylem, hedef, yönetici, oyuncu ve zaman aralığı; sunucuda uygulanır | | Sütunlar | Hayır | Gizlenebilir ve sabitlenebilir; kayıt zamanı gizlenemez | | Sayfa boyu | Hayır | 15, 25, 50, 100 ya da 500 | | Kapsam sekmesi | Hayır | Yalnızca süper yöneticide: ortam kayıtları ya da operatör işlemleri | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Operatör işlemleri sekmesi yok | Bu kayıtları yalnızca süper yönetici görür. | | Aradığım kayıt görünmüyor | Seç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 istiyorum | Mümkün değil; kayıtlar değiştirilemez. | ## İlgili dokümanlar - [Güvenlik](../operations/security.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/game-settings-sign-in.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Tek aktif oturum | Hayır | Ortam başına anahtar; açık oturumlara dokunmaz | | İstemci kimliği (client id) | Evet | En fazla 256 karakter, boşluksuz görünür karakterler | | İstemci sırrı (client secret) | Evet | En az 8, en fazla 1024 karakter; yalnızca yazılır | | Yönlendirme tabanı | Canlıda evet | https ile başlar; sorgu, parça ya da kullanıcı bilgisi içermez | | İşlem nedeni | Evet | 8-500 karakter | | Doğrulama kodu | Evet | 6 haneli güncel kod | | Onay metni (kaldırma) | Evet | Sağlayıcı kimliği aynen | | Çalışma durumu etkin (sağlayıcı ayarları) | Hayır | Sağlayıcıyı açar ya da kapatır | | Güvenli ek bilgiler (JSON) | Hayır | Sır içeren alan adları reddedilir | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Google düğmesi oyunda görünmüyor ya da başlatma `403` dönüyor | Google bu ortamda yapılandırılmamış ya da kapalıdır. Bilgileri girin ve sağlayıcıyı etkinleştirin. | | Google "redirect_uri_mismatch" diyor | Google Cloud konsoluna kaydettiğiniz adres, yönlendirme tabanı + `/google` ile birebir aynı olmalıdır. | | "Bilgileri düzenle" düğmesi yok | Yalnızca süper yönetici istemci bilgilerini yazabilir. | | "Kaydet" doğrulama kodu hatası veriyor | Kurtarma 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üyor | Bilgiler sunucu ayarından gelir. Panelden girdiğiniz değer bu ortamda onu ezer. | | Oyuncu iki cihazda aynı anda oturumda kalamıyor | **Tek aktif oturum** açıktır. Kapatırsanız oyuncular birden çok cihazda aynı anda oturumda kalabilir. | ## İlgili dokümanlar - [Google ile giriş](../auth/oauth.md) - [Kimlik doğrulamaya genel bakış](../auth/overview.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) - [Hesap bağlama](../auth/account-linking.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/game-settings-modules.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | İşlem nedeni | Evet | 8-500 karakter | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | **Aç** ve **Kapat** düğmeleri yok | Modü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ıyor | Modü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ıyor | Başka modüller bu modüle bağlıdır; önce onları kapatın. | | "Etkin eklentiler kullanıyor" yazıyor | Bir eklenti bu modüle ihtiyaç duyar. [Eklentiler](./plugins.md) 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 - [Temel kavramlar](../getting-started/concepts.md) - [Mimari ve barındırma](../getting-started/architecture-and-hosting.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/game-settings-api-keys.html # 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: ```bash curl https:///v1/ \ -H "Authorization: Bearer crx_live_<önek>_" ``` 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Anahtar adı | Evet | Listede tanımak için ad; en az 2 karakter | | Anahtar türü | Evet | Sunucu (gizli) ya da istemci (oyuna gömülebilir) | | Geçerlilik | Evet | Süresiz, 30 gün, 90 gün, 1 yıl ya da özel tarih; düzenlemede ayrıca "Değiştirme" | | Not | Evet | İşlem geçmişine yazılan gerekçe, 8-500 karakter | | Anahtarın sahibi | Hayır | Varsayılan "Bu ortam"; "Gelişmiş ayarlar" içindedir | | Sahip oyuncu / organizasyon | Sahip türüne bağlı | Sahibi seçtiğinizde zorunludur | | Kapsam etiketleri | Hayır | Yalnızca not amaçlı etiketler | | İzinler | Hayır | Ad-değer notları; erişimi kısıtlamaz | | Ek bilgiler | Hayır | Ad-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 | Durum | Ne yapmalı | |---|---| | Gizli değeri kaybettim | Anahtarı **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 yok | Rolü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 - [Hızlı başlangıç](../getting-started/quickstart.md) - [Sunucuda doğrulama](../auth/server-verification.md) - [Güvenlik](../operations/security.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/webhooks.html # 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](../guides/webhooks.md) 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](game-settings-modules.md) 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 | Tablo | Sütunlar | |---|---| | Webhook endpoints | URL, Event filtreleri, Durum (enabled ya da disabled), Oluşturulma; Endpoint kimliği sütun menüsünden açılır | | Delivery attempts / DLQ | Endpoint (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 | | Outbox | Event (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 | Durum | Ne yapmalı | |---|---| | "Webhook yüzeyi yetki gerektiriyor" yazıyor | Rolü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 geldi | Teslimat 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öremiyorum | Sı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 - [Webhook'lar](../guides/webhooks.md) - [Webhook olayları](../reference/webhook-events.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/operations.html # 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](player-profile.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Oyuncu | Evet (İ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 | Durum | Ne yapmalı | |---|---| | "Operations yüzeyi yetki gerektiriyor" yazıyor | Rolünüzün iş kaydı okuma yetkisi yok; bir yöneticiden isteyin. | | Oyuncu ilişkileri yüklenmiyor | Bu 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 istiyorum | Bu sayfa salt okunur; oturumları [Oyuncu profili](player-profile.md) sayfasından kapatın. | ## İlgili dokümanlar - [Hesap bağlama](../auth/account-linking.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/tenancy.html # 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](../getting-started/concepts.md) 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ı](game-settings-general.md) sayfasını açar. ## Alanlar ve seçenekler | Alan | Pencere | Zorunlu | Açıklama | |---|---|---|---| | Stüdyo adı | Yeni stüdyo | Evet | 1-160 karakter | | Stüdyo | Yeni oyun | Evet | Oyunun ait olacağı stüdyo (stüdyo kartından açtıysanız seçilmiş gelir) | | Oyun adı | Yeni oyun | Evet | 1-160 karakter | | Kısa ad | Yeni oyun | Hayır | Küçük harf, rakam, tire; en çok 48 karakter | | Yeni ad | Yeniden adlandır | Evet | 1-160 karakter | | Gerekçe | Hepsi | Evet | 8-500 karakter | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Sayfayı göremiyorum | Stü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ıyor | Oyunun 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 reddedildi | Oyunun canlı yetkisi ya da etkin API anahtarı olabilir; önce onları kapatın. | ## İlgili dokümanlar - [Temel kavramlar](../getting-started/concepts.md) - [Mimari ve barındırma](../getting-started/architecture-and-hosting.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/operators.html # 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](players.md) 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 | Rol | Yapabilecekleri | |---|---| | super_admin | Bü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_manager | Atanmış 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 | | support | Atanmış ortamlarda destek yapar: oyuncuları, oturumları ve sohbeti okur; oyuncu bilgisini günceller, yasaklar, oturum kapatır, sohbette susturur | | read_only | Atanmış ortamlarda yalnızca okur | ### Oluşturma ve düzenleme formu | Alan | Zorunlu | Açıklama | |---|---|---| | E-posta | Hayı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 ad | Evet | Panelde görünen ad | | Rol | Evet | Dört rolden biri | | Geçici parola | Evet (oluştururken) | En az 12 karakter | | Ortamlar | Evet | Erişilecek ortamlar | | İşlem nedeni | Evet | 8-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 | Durum | Ne yapmalı | |---|---| | Sayfayı göremiyorum | Yönetici hesaplarını okuma yetkisi yalnızca en geniş yetkili roldedir; bir süper yöneticiden isteyin. | | Ortam listesinde bir ortam yok | Listede 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 yok | Yöneticinin iki adımlı doğrulaması kapalı ya da kendi hesabınıza bakıyorsunuz. | | Devre dışı bıraktığım yönetici hâlâ panelde | Devre dışı bırakma yeni girişleri engeller; açık oturumu **Oturumları revoke et** ile kapatın. | ## İlgili dokümanlar - [Temel kavramlar](../getting-started/concepts.md) - [Kimlik doğrulamaya genel bakış](../auth/overview.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/player-profile.html # 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](players.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Görünen ad | Evet | Oyuncunun görünen adı | | E-posta | Hayır | Boşsa e-posta kaldırılır; ayrı yetki gerekir | | E-posta doğrulandı | Hayır | Adresin doğrulanmış sayılması; ayrı yetki gerekir | | Görsel URL | Hayır | Oyuncu görselinin adresi | | Metadata JSON | Hayır | JSON nesnesi | | İşlem nedeni | Evet | 8-500 karakter | ### Yasak penceresi | Alan | Zorunlu | Açıklama | |---|---|---| | Süre | Evet | 1, 3, 7, 30 gün, süresiz ya da özel tarih ve saat (en çok 365 gün) | | Yasak gerekçesi | Hayır | En çok 200 karakter; yalnız yönetici ekranlarında görünür | | İşlem nedeni | Evet | 8-500 karakter | ### Parola pencereleri | Alan | Pencere | Açıklama | |---|---|---| | Yeni parola | Parola yönet | En az 12 karakter | | E-posta | Hesaba çevir | Hesabın giriş e-postası | | İşlem nedeni | Hepsi | Parola ve dönüştürme pencerelerinde en çok 200 karakter | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | E-posta alanı pasif | Rolü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ı kaybettim | Parola 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üyor | Ban 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 - [Kimlik doğrulamaya genel bakış](../auth/overview.md) - [Hesap bağlama](../auth/account-linking.md) - [Oturumlar ve CSRF](../auth/sessions-and-csrf.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/character-profile.html # 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](characters.md) 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 | Alan | Hangi komutlarda | Açıklama | |---|---|---| | Komut | Hepsi | Bayrak 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ğeri | Bayrak ayarla | Bayrağı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üm | Bayrak dışındaki komutlar (Değiştirici kaldır hariç) | Tam sayı | | Etkilenen istatistik, Değiştirici türü | Değiştirici ekle | Hangi istatistik, nasıl etkilenecek | | Kaynak türü, Kaynak kimliği | Değiştirici ekle ve kaldır | Değiştiricinin kaynağı | | İşlem nedeni | Hepsi | En az 8 karakter | ### Geçmişteki hâli | Alan | Zorunlu | Açıklama | |---|---|---| | Tarih ve saat | Evet | RFC 3339 zamanı; gelecek ya da geçersiz zaman reddedilir | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | "Komut çalıştır" düğmesi pasif | Karakter bilgisi yüklenememiştir; profili yenileyin. | | Komut sürüm çakışmasıyla reddedildi | Karakter 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üyor | Rolü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 - [Karakterler](../guides/characters.md) - [Ekonomi](../guides/economy.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/model-profile.html # 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 | Alan | Nerede | Açıklama | |---|---|---| | Önizlenen sürüm | Önizleme | Hangi sürümün gösterileceği | | Taban sürüm | Klip ekle | Klipleri alacak sürüm | | Klip dosyası ya da zip | Klip ekle | fbx ya da zip | | Yeni ad | Yeniden adlandır | En çok 200 karakter | | Farkı gördüm, yine de yayınla | Yayın onayı | Zorunlu onay kutusu | | İşlem nedeni | Yeniden adlandır, arşiv, silme, onaylı yayın | 8-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 | Durum | Ne 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önemiyorum | Sü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 - [3D modeller](models.md) - [İçerik](content.md) - [Scene, görsel ve 3B modeller rehberi](../guides/scenes-assets-models.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/content-group-edit.html # İç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](content.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Grup adı | Evet | Panelde görünen ad | | Anahtar | Evet | Oyunun grubu bulduğu kimlik; kayıttan sonra sabit | | Simge | Hayır | Kartta görünen simge | | Kısa açıklama | Hayır | En çok 500 karakter | ### Alan türleri | Tür | Ne tutar | Ek ayarlar | |---|---|---| | Sayı | Tam sayı | Birim, ondalık hane, en az, en çok, varsayılan | | Yüzde | Yüzde değeri | En az, en çok, varsayılan | | Metin | Metin | En çok karakter, çok satırlı | | Evet / Hayır | Mantıksal değer | Varsayılan | | Seçenek listesi | Listeden seçim | Seçenekler (en çok 64), birden fazla seçilebilir | | Renk | `#rrggbb` renk | Yok | | 3D model | Model kütüphanesinden bir model | Yok; Sahneler modülü kapalıysa seçilemez | | Görsel | Yüklenmiş bir görsel | Yok | | Başka bir kayıt | Başka bir kayda bağlantı | Hedef, hedef grup | | Tablo | Tekrarlanan satırlar, örneğin seviye-hasar tablosu | En az satır, en çok satır, sütunlar (en çok 8) | ### Her alan için ortak ayarlar | Alan | Zorunlu | Açıklama | |---|---|---| | Alan adı | Evet | Formda görünen ad | | Anahtar | Evet | Oyunun okuduğu kimlik; kayıttan sonra sabit | | Tür | Evet | Kaydedilmiş alanda değişmez | | Görünürlük | Evet | Oyuncu görebilir ya da yalnız oyun sunucusu | | Yardım metni | Hayır | Formda alanın altında görünür | | Zorunlu | Hayır | Boş bırakılırsa kayıt kaydedilemez | | Listede göster | Hayır | Liste ve özette de gösterir | Bir grupta en çok 48 alan olur. ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | Anahtarı yanlış yazdım ve kaydettim | Anahtar değişmez. Doğru anahtarla yeni bir alan ekleyin; eskisi artık gerekmiyorsa kayıtlardan kaldırın. | | "3D model" türü seçilemiyor | Sahneler modülü bu ortamda kapalıdır; modülü açın. | | Bir alanın türünü değiştiremiyorum | Kaydedilmiş alanın türü sabittir. **Yeni alan olarak ekle** ile yeni tür için ayrı bir alan açın. | | Bazı alanlarda kilit simgesi var | Bir 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çmiyor | 1. adımda ad ya da anahtar eksik ya da geçersizdir; kırmızı mesajları düzeltin. | ## İlgili dokümanlar - [İçerik](content.md) - [Kayıt](content-entry-edit.md) - [İçerik grupları rehberi](../guides/content-groups.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/content-entry-edit.html # İç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](content.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Kayıt adı | Evet | Panelde görünen ad | | Anahtar | Yeni kayıtta | Oyundaki 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üzde | Sınırlar içinde sayı; ondalık haneler grubun ayarına göredir | | Metin | Serbest metin; üst karakter sınırı grubun ayarındadır | | Evet / Hayır | Onay kutusu | | Seçenek listesi | Tek ya da çoklu seçim | | Renk | Renk seçici | | 3D model | Model kütüphanesinden seçim; kaydın üstünde önizleme görünür | | Görsel | Görsel yükleme | | Başka bir kayıt | Hedef gruptan kayıt seçimi | | Tablo | Satı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 | Durum | Ne 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ıyorum | Anahtar 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ıyor | Araç kaydının mağazada ürünü varsa fiyatın tek kaynağı üründür; [Ürün](store-product.md) sayfasından değiştirin. | | Sayfadan çıkarken onay soruyor | Kaydedilmemiş değişiklik var; **Düzenlemeye dön** ile kaydedin ya da **Değişiklikleri at** deyin. | | Başkası aynı anda kaydı değiştirdi | Kayıt sürüm kontrolüyle yazılır; sayfayı yenileyip değişikliği yeniden yapın. | ## İlgili dokümanlar - [İçerik](content.md) - [İçerik grubu düzenle](content-group-edit.md) - [İçerik grupları rehberi](../guides/content-groups.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/store-product.html # 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](store.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ne satılıyor? | Evet | Satılacak eşya | | Vitrindeki adı | Hayır | En çok 120 karakter; boşsa eşya adı | | Açıklama | Hayır | En çok 2000 karakter | | Ürün görseli | Hayır | PNG, JPEG ya da WebP | | Nasıl ödenir? | Evet | Oyun parası ya da gerçek para | | Para birimi ve Fiyat | Oyun parasında | Fiyat 0'dan büyük | | Uygulama içi ürün | Gerçek parada | App Store ya da Google Play ürünü | | Rozet | Hayır | Yok, Yeni, İndirim, Sınırlı, Popüler | | Stok | Evet | Sınırsız ya da sınırlı; sınırlıda adet | | Bir oyuncu en çok kaç tane alabilir? | Hayır | Boşsa sınırsız | | Durum | Evet | Taslak ya da Yayında | | Ne zaman satışta? | Evet | Hemen ya da tarih aralığı | | Vitrin | Evet | Oluşturduktan sonra değişmez | | İşlem nedeni | Evet | 8-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 | Durum | Ne 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ştiremiyorum | Mağazada ürünü olan aracın fiyatının tek kaynağı üründür; fiyatı burada değiştirin. | ## İlgili dokümanlar - [Mağaza](store.md) - [Para birimleri](currencies.md) - [Mağaza ve uygulama içi satın alma rehberi](../guides/store-iap.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/currency-profile.html # 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](currencies.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ad | Evet | En çok 120 karakter | | Anahtar | Oluştururken | Küçük harf, rakam, tire, alt çizgi; sonradan değişmez | | İkon görseli | Hayır | PNG, JPEG ya da WebP | | Kuruş hanesi | Evet | 0 (tam sayı), 1, 2 (kuruş), 3 ya da 4 | | Arz tavanı | Evet | Sınırsız ya da sınırlı | | Toplam arz en çok | Sınırlıda | Kuruş haneli tutar | | Oyuncular arası gönderilebilir | Hayır | Seçiliyse oyuncular birbirine gönderebilir | | İşlem nedeni | Evet | 8-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 | Durum | Ne 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ştiremiyorum | Anahtar yalnızca oluştururken yazılır; yanlışsa yeni bir para birimi oluşturun. | | "Bunu görme yetkiniz yok" yazan kartlar | Hesabı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 - [Para birimleri](currencies.md) - [Ekonomi raporları](economy.md) - [Ekonomi rehberi](../guides/economy.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/reward-table-profile.html # Ö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ı](reward-tables.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ad | Evet | En çok 120 karakter | | Anahtar | Oluştururken | Küçük harf, rakam, tire, alt çizgi; sonradan değişmez | | Kaç çekiliş | Evet | 1-10 | ### Şans satırları (en çok 50) | Alan | Açıklama | |---|---| | Ödül türü | Para, Eşya ya da Ödül yok | | Ne | Para birimi ya da eşya | | En az, En çok | Miktar 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 | Durum | Ne 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 silemiyorum | Tablo kullanımdadır; önce kullanan yerlerden ayırın. Silme penceresi nerelerde kullanıldığını gösterir. | | Miktar hatası alıyorum | Para 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 - [Ödül tabloları](reward-tables.md) - [Para birimleri](currencies.md) - [Ödüller rehberi](../guides/rewards.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/leaderboard-detail.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Oyuncu bul | Hayır | Yalnızca görüntüleme; oyuncunun çevresini gösterir | | Oyunda görünsün | Hayır | Oyuncuların tabloyu görmesini açar ya da kapatır | | Puan nasıl gösterilsin? | Evet | Sayı, para ya da süre | | Birim eki | Hayır | En fazla 8 karakter; süre biriminde yoktur | ## Sık karşılaşılan durumlar | Durum | Ne 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](./leaderboards.md) 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 istiyorum | Bunlar kurulduktan sonra değişmez. [Leaderboard](./leaderboards.md) sayfasından yeni bir tablo kurun. | | Listede yalnızca ilk satırlar var | Sayfa ilk satırları gösterir; devamı bu sürümde listelenmiyor. | ## İlgili dokümanlar - [Skor tabloları ve turnuvalar](../guides/competition.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/rules-game-card.html # 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](./rules.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Ayar değeri | Hayır | Kartın her ayarı için bir satır; aralık dışı değer kabul edilmez | | Kural şalteri ("Bu kural açık") | Hayır | Yalnızca şalteri olan kartlarda görünür; yazma yetkisi ister | | Senaryo (önizleme) | Hayır | Kartın sunduğu örnek durumlardan biri | | Senaryo sayıları (önizleme) | Hayır | Senaryoya göre değişir; sınırlar alanın altında yazar | | Neden değiştiriyorsunuz? (yayın penceresi) | Evet | 8-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 | Durum | Ne yapmalı | |---|---| | Ayar alanları kapalı ve "yetkiniz yok" yazıyor | Değ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ı" diyor | Siz 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" diyor | Listedeki 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ıyor | Kart kimliği bu oyunun kataloğunda yoktur; [Kurallar](./rules.md) sayfasından yeniden açın. | | Önizleme "yok" diyor | Bu kartın önizleme senaryosu bulunmuyor; yayınlamadan önce değerleri dikkatle kontrol edin. | ## İlgili dokümanlar - [Oyun kuralları ve otomasyonlar](../guides/game-rules.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/rules-history.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | İşlem nedeni (geri alma penceresi) | Evet | 8-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 | Durum | Ne yapmalı | |---|---| | Geri alma düğmeleri yok | Geri almak için oyun kuralı yayın yetkisi gerekir; geçmişi okuyabilirsiniz. Bir yöneticiden isteyin. | | "Henüz yayın yok" yazıyor | Ayarlar 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 reddedildi | Siz 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öremiyorum | Liste sayfa sayfa yüklenir; sonda **Daha eski sürümler** düğmesine basın. | ## İlgili dokümanlar - [Oyun kuralları ve otomasyonlar](../guides/game-rules.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/rules-automation-editor.html # 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](./rules.md) 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Kural adı | Evet | 1-120 karakter | | Anahtar | Evet | Addan üretilir, en fazla 64 karakter; yayından sonra değişmez | | Açıklama | Hayır | En fazla 500 karakter | | Ne zaman | Evet | Olay seçimi (seviye atlama, kaynak değişimi, bayrak değişimi gibi) | | Koşulların bağlacı | Birden fazla koşulda | Hepsi / herhangi biri | | Neye bakılıyor, hangi bilgi, ad, karşılaştırma, değer, durum | Koşul eklenince | En fazla 20 koşul; sayı alanlarında tam sayı | | Ne yapılsın, ad, miktar, durum | Evet (en az bir eylem) | En fazla 10 eylem; miktar sıfır olamaz | | İşlem, değer, kaynak türü, kaynak adı | Güçlendirme eylemlerinde | Yüzde işlemlerinde değer yüzdenin 100 katıdır | | Öncelik | Evet | 1-1000 arası tam sayı, varsayılan 100; "Gelişmiş" bölümündedir | | Karakter, örnek olay bilgileri | Denemede | Yalnızca denemeyi etkiler | | Neden değiştiriyorsunuz? | Evet | 8-500 karakter | | Hemen açılsın | Hayır | Yalnı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 | Durum | Ne yapmalı | |---|---| | "Yayınlamadan önce tamamlayın" listesi çıkıyor | Listedeki 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ıyor | Kural 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" diyor | Denemek için karakter aramasından bir karakter seçmelisiniz. | ## İlgili dokümanlar - [Oyun kuralları ve otomasyonlar](../guides/game-rules.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/game-settings-environments.html # 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 | Alan | Zorunlu | Açıklama | |---|---|---| | Alan adları | Hayır | Satır başına bir alan adı; en fazla 10 | | Gerekçe | Evet | 8-500 karakter | | Ortam türü (ekleme penceresi) | Evet | Geliştirme, Hazırlık ya da Canlı | | İşlem nedeni (pencerelerde) | Evet | 8-500 karakter | ## Sık karşılaşılan durumlar | Durum | Ne 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" diyor | Bu sekme yalnızca süper yöneticiye açıktır. | | Eski adresteki oyun giriş yapamıyor | Adres listeden çıkarılmış. Adresi listeye geri ekleyip kaydedin; ardından yeni adrese geçişi tamamlayın. | | Arşivleme reddedildi | Ortamın canlı yetkisi ya da etkin bir API anahtarı vardır; pencere nedenini gösterir. | ## İlgili dokümanlar - [Mimari ve barındırma](../getting-started/architecture-and-hosting.md) - [Temel kavramlar](../getting-started/concepts.md) --- Source: https://open-play.b4klabs.dev/docs/tr/panel/plugin-page.html # 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](./plugins.md) sayfasından yönetici değiştirir. ## Alanlar ve seçenekler | Alan | Zorunlu | Açıklama | |---|---|---| | Arama | Hayır | Eklentinin tanımladığı alanlarda arar | | Sayfa başına | Hayır | 12, 25 ya da 50 | | İşlem nedeni (eylem penceresi) | Evet | Sınırlar alanın altında yazar | | Eylem alanları | Eylemine göre | Eklenti tanımlar; her alanın açıklaması pencerede yazar | ## Sık karşılaşılan durumlar | Durum | Ne yapmalı | |---|---| | "Bu eklenti bu ortamda salt okunur" | Eklenti yalnızca okumaya açık. Değişiklik gerekiyorsa bir yöneticiden [Eklentiler](./plugins.md) 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ı var | Ekran 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ı" diyor | Kayıt siz bakarken değişmiştir. Güncel hâlini görüp yeniden onaylayın. | ## İlgili dokümanlar - [Eklentilere genel bakış](../plugins/overview.md) - [Eklenti çağırma](../plugins/calling-plugins.md) - [Temel kavramlar](../getting-started/concepts.md)