Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Karakterler

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

Ne işe yarar

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

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

Panelde nerede

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

Veri modeli (düz dille)

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

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

Oyunun yaptığı çağrılar

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

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

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

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

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

Not

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

Uyarı

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

Karakter oluşturmak

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

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

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

Uyarı

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

Komut çalıştırmak

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

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

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

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

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

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

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

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

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

Geri alma

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

Olaylar ve webhook’lar

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

Sınırlar

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

Sık görülen hatalar

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

Çalışan örnek

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

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

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

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

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

İlgili sayfalar