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ız | Panel sayfası |
|---|---|
| Eşya tanımlarını ve oyun verilerini (yerleşik eşya grubu) düzenlemek | İçerik |
| Eşyaya 3B model bağlamak | 3D modeller, Model profili |
| Bir oyuncunun envanterini ve cüzdanını görmek | Oyuncu profili |
| Eşyayı mağazada satmak | Mağaza |
Veri modeli (düz dille)
- Tür (
kind):consumable,equipment,material,cosmetic,vip_card. - Yığılabilir (
stackable) vemax_stack: Yığılabilir eşyada aynı tanım sürümünden ve boşmetadata’lı örnekler tek satırda birleşir.stackable: trueisemax_stackzorunludur (1 ile2147483647arası). tradeable:falseise eşya başka sahibe devredilemez (422). Varsayılantrue.npc_buy_price,npc_sell_price: En küçük birimde tam sayı.nullise 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ürplayer,character,clanya dasystemolabilir. Ö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
404döner ve envanteri açmaz. version: Hem eşya örneğinin hem envanterin kendi sürümü vardır; her değişiklikte artar veIf-Matchile 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 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:<uuid> | 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) |
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,equipveunequipuçları anahtar türüne bakmaz; geçerli bir istemci anahtarı da çağırabilir veownerya 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.mintvedestroyyalnı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
}
quantity1 ile2147483647arasındadır.sourcemint,craft,purchase,questya dagrantolabilir; verilmezsemint.metadatadoluysa 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_stackaşılırsa422.
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:<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 |
|---|---|
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ı da422’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-Matchgönderirseniz400alırsınız.- Aynı eşya başka bir envanterde yerleşikse
409(çift yerleşim kilidi). split:amount1 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ınmetadata’sındaequip_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şuldacharacterbağlamı şimdilik boştur,nowveseedsabit0’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-Keyzorunludur. Aynı anahtar ve aynı gövde ikinci kez gelirse işlem tekrar çalışmaz; yanıtreplayed: trueile döner. Aynı anahtar, farklı gövde422’dir. Anahtar yalnız başarıda yanar. If-Matcheşzamanlı yazmayı engeller (uyuşmazlık409).- 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
limit1–200; sayfalama imleçlidir. - Varsayılan envanter kapasitesi 32 dolu yuvadır.
quantity,amountve 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):
/** 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
);
}