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ız | Panel sayfası |
|---|---|
| Karakter şablonu, özellik (trait) ve seviye eğrisi tanımlamak | Karakter şablonları |
| Karakterleri listelemek, birini açıp durumunu ve geçmişini görmek | Karakterler, Karakter profili |
| Oyuncunun hesabını ve karakterlerini birlikte görmek | Oyuncular, Oyuncu profili |
| Olay tetikli otomasyon yazmak | Oyun 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) veformula(kuadratik formül,max_levelile). - Karakter: Bir şablondan doğan örnek.
external_idalanına oyunun kendi oyuncu kimliğini yazabilirsiniz; aynı ortamda iki karaktere aynıexternal_idverilemez. - 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 serbestcustomalanı. - 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 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. 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ızmodule_disabledveaccount_bannedJSON 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_idile ikinci bir oluşturma409döner; bu, kopya karakter oluşmasını da engeller. Ağ hatasında önceexternal_idile 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:
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ıtreplayed: trueile döner. Aynı anahtarla farklı gövde422’dir. Anahtar yalnız komut başarılı olunca “yanar”; reddedilen komut anahtarı tüketmez. Aynı anda uçuşta olan aynı anahtar409alır.If-Match(isteğe bağlı): Okuduğunuzversiondeğeri,3ya da"3"biçiminde. Karakter bu sürümde değilse komut409ile reddedilir ve hiçbir şey değişmez.*ve zayıfW/biçimleri kabul edilmez (400).rollbackkomutundaIf-Matchiş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
customalanı en çok 64 KB; içinde ondalık sayı olamaz.- Liste uçlarında
limit1–200. - Seviye 1’den küçük olamaz; en çok 65535.
GET …/state?at=geleceğe ya da eksi bir zamana400dö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.
/** 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;
}
}