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

Envanter ve eşyalar

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

Ne işe yarar

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

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

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

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

Panelde nerede

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

Veri modeli (düz dille)

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

Oyunun yaptığı çağrılar

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

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

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

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

Not

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

Uyarı

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

Eşya üretmek (mint)

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

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

Tüketmek, yok etmek, devretmek

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

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

Envanter yerleşimi

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

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

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

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

Kurallar:

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

Eşya kataloğu

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

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

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

Olaylar

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

Sınırlar

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

Sık görülen hatalar

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

Çalışan örnek

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

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

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

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

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

İlgili sayfalar