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 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 |
| Bir tablonun en yüksek kayıtlarını ve sezon geçmişini görmek | Leaderboard ayrıntısı |
| Turnuva için ödül para birimi | Para birimleri |
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,unitveunit_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 dacustom(custom_period_msuzunluğ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çenekscore_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 daduration_ms;unit_suffix1–8 karakterlik ektir ($,ms…). Birim yalnız gösterim içindir. player_visible:trueise oyuncu oturumu tabloyu okuyabilir; varsayılanfalse.metric_expr: Kayıt anında derlenen CEL ifadesi (zorunlu, boş olamaz). Eklentilerin kendiliğinden kurduğu tablolarevent.scorekullanı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>_<gizli> ister; istemci anahtarı ve oyuncu oturumu reddedilir. Oyuncu ucu çerez ya da oturum belirteci ve x-crx-env: <ortam uuid> 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.
Tablo tanımı
POST /v1/leaderboards
Authorization: Bearer crx_live_<önek>_<gizli>
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>_<gizli>
Idempotency-Key: score-oyuncu-1234-2026-11-03
Content-Type: application/json
{ "player_id": "0192f0f2-5b1a-7c3e-9d11-4a5b6c7d8e9f", "score": 4200 }
{ "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
{
"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_visibletablolar okunur; aksi404. Modül kapalıysa da404. limit1–50 (varsayılan 20).rankvescoreJSON’da ondalık metindir (kayıpsızi64); SDK bunlarıbigintyapar. 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ı yoksamenull’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>_<gizli>
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:playerya daclan.capacity2–1024; kayıt kapanışı açılıştan sonra olmalı;name1–200 karakter.seeding_source:registration_order(kayıt sırası;leaderboard_idverilmez) ya daleaderboard(yalnız oyuncu turnuvasında;leaderboard_idzorunlu).prize_table:currency_key(boşluksuz, en çok 200 karakter) ve pozitifamountiçeren nesne.- Akış: oluştur →
participantsile kaydet ({ "participant_id" }) →startile bracket’i üret → her maç içinresultbildir (reporter_id,reported_side:homeawayadmin,outcome:home_winaway_windraw,score).admintarafı 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-Keyister (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.
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 sayfasındaki ödül verme akışını kullanın.
Sınırlar
- Oyuncu okumasında
limit1–50; sunucuentriesuçlarında sayfalama imleçlidir. - Turnuva
capacity2–1024. season: customiçin pozitifcustom_period_msgerekir.
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
/** 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<number> {
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<void> {
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`);
}