TypeScript SDK
TypeScript SDK’sı Open Play’e tarayıcıdan, Node.js’ten ve oyun motorlarının JavaScript katmanından bağlanmanın tipli yoludur: oyuncu girişi, eklenti komutları, gerçek zamanlı bağlantı ve sunucu anahtarıyla yönetim çağrıları. Bu sayfa kurulumu, giriş noktalarını, temel istemcileri ve hangi modüllerin tipli istemcisi olduğunu anlatır.
Not
Bu belge
@ferdiunal/open-play-ts0.1.15 sürümüyle doğrulanmıştır. Paket GitHub Packages’ta yayımlıdır; depo bugün özeldir, bu yüzden kurulum için yetkili bir GitHub belirteci gerekir.
API başvurusu: tüm tür ve işlevlerin üretilmiş başvurusu https://open-play.b4klabs.dev/docs/sdk/typescript/ adresindedir (@open-play/sdk ve @open-play/panel-kit).
Kurulum
Gereksinimler: Node.js 22 ya da üstü; paket yalnız ESM’dir (require ile yüklenmez). Tarayıcıda Fetch, AbortController ve BigInt destekli modern bir tarayıcı gerekir.
-
Kayıt defterini tanıtın. Projenizin kökündeki
.npmrcdosyasına kapsamı (scope) ve belirteç değişkenini yazın. Belirteçread:packagesyetkili bir GitHub klasik belirtecidir (ince ayarlı belirteç denenmedi); dosyaya değer yazmayın, ortam değişkeni kullanın:@ferdiunal:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} -
Paketi kurun:
export GITHUB_TOKEN="<read:packages yetkili belirteç>" npm install @ferdiunal/[email protected]pnpmaynı.npmrcile çalışır. Belirteci depoya, imaja ya da CI günlüğüne koymayın. -
İçe aktarın:
import { CrxClient, createPlayerClient } from "@ferdiunal/open-play-ts";
Uyarı
Paket adı
@ferdiunal/open-play-ts’tir. SDK deposundaki bazı dosyalarda ve örneklerde geçen@open-play/sdkadı yalnızca kaynak deposundaki iç addır; yayımlanan pakette kullanmayın.
Giriş noktaları
| İçe aktarma | İçerik | Ortam |
|---|---|---|
@ferdiunal/open-play-ts | CrxClient, createPlayerClient, createGameClient, hata ve sayı yardımcıları, katalog/mağaza/sıralama tipleri | Tarayıcı ve Node |
.../player | Oyuncu istemcisi ve eklenti bağlama oluşturma tipleri | Tarayıcı ve Node |
.../game | GameClient | Tarayıcı ve Node |
.../realtime | Gerçek zamanlı istemci tipleri | Tarayıcı ve Node |
.../browser | IndexedDB günlüğü ve sekme koordinasyonu (createBrowserAdapter) | Tarayıcı |
.../codegen | Bağlama ve tip üreticisi | Yalnız Node |
CLI: crx (npx crx --help). Kod üretimi bölümüne bakın.
İstemciler
CrxClient: sunucu anahtarıyla REST
CrxClient, API anahtarıyla çalışan tipli REST istemcisidir. Sunucu anahtarını yalnız güvenilen arka uçta kullanın; tarayıcıya ya da oyun paketine koymayın.
const client = new CrxClient({ baseUrl: "https://oyun.example.com/open-play", apiKey: serverKey });
ClientOptions: baseUrl, apiKey, isteğe bağlı fetch, timeoutMs (1–120 000 ms, varsayılan 15 sn), maxRetries (0–5, varsayılan 2), maxResponseBytes (varsayılan 2 MiB, en çok 16 MiB). Her çağrı { path, query, body, headers } girdisi ve isteğe bağlı { signal, timeoutMs, maxRetries } alır.
import { CrxClient, CrxError, ErrorKind } from "@ferdiunal/open-play-ts";
/**
* Sunucu anahtarıyla bir karaktere XP verir. Aynı `requestKey` ile yapılan tekrar aynı sonucu döndürür;
* `If-Match`, okuduğunuz sürümle yazdığınız sürümün çakışmasını yakalar.
*/
export async function grantXp(
baseUrl: string,
serverKey: string,
characterId: string,
amount: bigint,
requestKey: string,
): Promise<bigint> {
const client = new CrxClient({ baseUrl, apiKey: serverKey });
const current = await client.getCharacter({ path: { id: characterId } });
try {
const result = await client.executeCommand({
path: { id: characterId },
body: { type: "add_xp", amount },
headers: { "Idempotency-Key": requestKey, "If-Match": current.version.toString() },
});
return result.version;
} catch (error) {
if (error instanceof CrxError && error.kind === ErrorKind.Http && (error.status === 409 || error.status === 422)) {
// 409: sürüm çakışması; yeniden okuyup karar verin. 422: aynı anahtar başka bir gövdeyle kullanılmış.
// Yeni bir anahtarla kör yeniden deneme yapmayın.
throw error;
}
throw error;
}
}
/** Sayfalamayı SDK yürütür: imleç (cursor) opaktır, `break` yeni sayfa istemez. */
export async function countCharacters(baseUrl: string, serverKey: string, definitionKey: string): Promise<number> {
const client = new CrxClient({ baseUrl, apiKey: serverKey });
let total = 0;
for await (const _character of client.iterateListCharacters({ query: { definition: definitionKey, limit: 100 } })) {
total += 1;
}
return total;
}
Çağrı kuralları
baseUrlHTTPS olmak zorundadır; yalnızca loopback (localhost,127.0.0.1,[::1]) düz HTTP kabul eder. Adres kullanıcı bilgisi, sorgu ya da parça içeremez. Yönlendirmeler (redirect) izlenmez.- Anahtar yalnız
Authorizationbaşlığındadır; hata nesnelerinde ve hata ayıklama çıktısında görünmez. - Yeniden deneme yalnızca okuma
GET’lerinde ve gerçekten verilmiş aynıIdempotency-Keytaşıyan komutlarda yapılır. Geçici408,429,500,502,503,504ve güvenli işlemde ağ hatası, sınırlı üstel geri çekilmeyle denenir;Retry-Aftertoplam süre sınırı içindedir.409ve422yeniden denenmez. Oluşturma ve silme çağrıları otomatik tekrarlanmaz. - Komutlarda
If-Match(kaynağın okuduğunuz sürümü) veIdempotency-Keybaşlıklarını siz verirsiniz (Idempotency ve eşzamanlılık). - Sayfalama imleçleri opaktır;
iterate…yöntemleri (iterateListCharacters,iterateListCharacterEvents,iterateGetCharacterTimeline,iterateListRules,iterateListWebhooks) sayfaları sizin yerinize yürütür.
PlayerClient: oyuncu oturumu
createPlayerClient({ baseUrl, environmentId }) oyuncu tarafı istemcisidir (yalnız çerez kipi; sunucu anahtarı bu istemciye girmez). İçerdikleri:
| Üye | Ne yapar? |
|---|---|
session | Giriş, oturum durumu, yenileme, çıkış: Kimlik doğrulama |
plugin(binding) · publicPlugin(binding) | Eklenti komut/sorgu/modülleri: Eklenti bağlamaları |
realtime | Gerçek zamanlı soket (aşağıda) |
catalog | Panelden yönetilen eşya/veri/model/içerik grubu okuması (koşullu GET, watch… yoklayıcıları) |
leaderboard(key, { limit }) | Çekirdek skor tablosu okuması (limit 1–50) |
store.catalog() · store.buy() | Mağaza vitrini ve satın alma |
data.list() · data.get() · data.create() · data.update() · data.delete() | Oyuncu verisi (bulut kaydı): sürümlü yazım, çatışmada PlayerDataConflict; bkz. Oyuncu verisi |
notifications | Cihaz kaydı, gelen kutusu, web push |
dispose() | Yerel kaynakları bırakır (sunucu oturumunu kapatmaz) |
İlk kullanım için Hızlı başlangıç. Oturum durumları için Oturumlar ve CSRF.
createPlayerClient ağ isteğini kurucuda başlatmaz; baseUrl oyun sayfanızla aynı origin’dir (ters vekil) ya da oyunun alan adı ortama bağlıysa Open Play adresidir (CORS; aynı site gerekir) (Mimari ve barındırma).
GameClient: oyuncu + eklenti + realtime tek yaşam döngüsü
createGameClient, bir oyuncu istemcisini, eklenti bağlamalarını ve tek gerçek zamanlı soketi aynı yaşam döngüsünde tutar. dispose() çocuk bağlamaları ve kendi yarattığı oyuncu istemcisini kapatır; sunucu çıkışı için ayrıca game.session.logout() çağırın. Dışarıdan verdiğiniz PlayerClient GameClient tarafından sahiplenilmez.
import { createGameClient, type GameClient } from "@ferdiunal/open-play-ts";
import { createBrowserAdapter } from "@ferdiunal/open-play-ts/browser";
/**
* Bir oyunun oyuncu istemcisi, eklenti bağlamaları ve tek realtime soketi aynı yaşam döngüsünde tutulur.
* IndexedDB günlüğü (journal) yarım kalan eklenti komutlarının sayfa yenilendikten sonra kurtarılmasını sağlar.
*/
export function startGame(environmentId: string): { game: GameClient; stop: () => Promise<void> } {
const browser = createBrowserAdapter({ journal: "indexeddb", allowMemoryFallback: false });
const game = createGameClient({
playerOptions: { baseUrl: window.location.origin, environmentId, browser },
});
return {
game,
stop: async () => {
await game.dispose();
await browser.dispose();
},
};
}
journal: "indexeddb" yarım kalan eklenti komutlarının sayfa yenilendikten sonra kurtarılmasını sağlar. SSR/Node’da journal: "memory" seçin ve istemciyi istekler arasında paylaşmayın.
Gerçek zamanlı soket
client.realtime.connect() tek kullanımlık bilet alır, soketi açar ve kimlik doğrular; kopmada bileti yenileyip imleç/epoch ile kaldığı yerden sürdürür. Sunucu etkinlik göstermeyen soketi 4100 koduyla kapatır; onIdleClose verirseniz SDK otomatik yeniden bağlanmaz ve “Devam et” kararı sizindir. Protokol ayrıntıları Gerçek zamanlı bölümündedir.
import { createPlayerClient, isIdleClose, type PlayerClient } from "@ferdiunal/open-play-ts";
/**
* Realtime soketini açar. Sunucu, etkinlik göstermeyen soketi 4100 koduyla kapatır; `onIdleClose` verirseniz
* SDK otomatik yeniden bağlanmaz ve "Devam et" kararını size bırakır.
*/
export function createRealtimeClient(environmentId: string, showContinue: (resume: () => Promise<unknown>) => void): PlayerClient {
const client: PlayerClient = createPlayerClient({
baseUrl: window.location.origin,
environmentId,
realtime: {
onIdleClose: () => showContinue(() => client.realtime.connect()),
},
});
return client;
}
/** Durumu yoklayan uygulama aynı bilgiyi `lastClose` üzerinden okuyabilir. */
export function wasIdleClosed(client: PlayerClient): boolean {
return isIdleClose(client.realtime.lastClose);
}
/** Oturum açıldıktan sonra soketi kurar; bilet yenileme ve yeniden bağlanma SDK'dadır. */
export async function connect(client: PlayerClient): Promise<void> {
const connection = await client.realtime.connect();
console.log("realtime durumu:", connection.state);
}
Kapsam: hangi modülün tipli istemcisi var?
SDK, REST yüzeyinin tamamını sarmaz. Aşağıdaki tablo bugünkü durumu gösterir; “ham REST” olan modüller için uçları uç nokta başvurusunda bulur, HTTP çağrısını kendiniz yaparsınız (anahtar ve başlık kuralları aynıdır).
| Modül / alan | Tipli istemci | Nerede |
|---|---|---|
| Karakter tanımları, özellikler (trait), seviye eğrileri, karakterler, komutlar, geçmiş, istatistik | Var | CrxClient (sunucu anahtarı) |
| Kurallar | Var | CrxClient (createRule, listRules, dryRunRule, …) |
| Webhook uç kaydı (oluştur/listele/oku/sil) | Var | CrxClient; imza doğrulaması sizin kodunuzdur (Webhook’lar) |
| Klanlar | Var | CrxClient (20 işlem) |
| Katalog (eşya/veri/model) ve içerik grupları | Var | PlayerClient.catalog (oyuncu), CrxClient.catalog (sunucu) |
| Ödül tablosu kaydı ve verme | Var | CrxClient.createRewardTable, getRewardTable, grantRewardTable |
| Mağaza vitrini ve satın alma | Var | PlayerClient.store |
| Çekirdek skor tablosu okuma | Var | PlayerClient.leaderboard |
| Bildirimler: cihaz kaydı, gelen kutusu, web push | Var | PlayerClient.notifications |
| Eklentiler ve gerçek zamanlı | Var (şemadan üretilen bağlamayla) | PlayerClient.plugin, GameClient |
| Eşyalar ve envanter | Yok | Ham REST |
| Para birimleri, cüzdanlar, transferler, defter | Yok | Ham REST |
| Takas (trading), tüccarlar | Yok | Ham REST |
| Mağaza yönetimi, IAP doğrulama | Yok | Ham REST |
| Görev, VIP, günlük giriş ve NPC ödülleri | Yok | Ham REST |
| Partiler | Yok | Ham REST |
| Sunucu tarafı skor tabloları, turnuvalar | Yok | Ham REST |
| Sohbet | Yok | Ham REST/WebSocket |
| Sahneler, varlıklar | Yok | Ham REST |
| Magic link, hesap bağını çözme, yerel platform girişleri | Yok | Ham REST (Kimlik doğrulama) |
CrxClient’ta tipli işlem sayısı 45’tir (25 çekirdek + 20 klan) ve katalog ile ödül tablosu işlemleri bunlara eklenir. Tipli istemcisi olmayan bir modülü kullanmak sorun değildir; yalnızca tipleri elle yazarsınız.
Hatalar
Tüm hatalar CrxError olarak gelir: kind, status, attempts ve güvenli metadata (code, operation, deliveryState, reference, reason). HTTP gövdesi, istek URL’si ve anahtar hata nesnesine girmez.
ErrorKind | Anlamı |
|---|---|
Http | Sunucu hata durumu döndürdü (status dolu) |
Network | Bağlantı koptu; mutasyon uygulanmış olabilir |
Timeout | Toplam süre sınırı doldu |
Cancelled | signal ile iptal |
Contract | Girdi ya da yanıt sözleşmeye uymuyor |
Configuration | Güvensiz ya da bozuk ayar |
Auth | Oyuncu oturumu yeniden doğrulama istiyor |
UnknownOutcome | İstek gönderildi ama sonuç bilinmiyor; kör tekrar yapmayın, sonucu sorgulayın |
Storage | Günlük, tarayıcı koordinasyonu ya da OAuth bağ deposu kullanılamıyor |
Realtime · Backpressure · UpgradeRequired · ScopeChanged · Disposed | Gerçek zamanlı yaşam döngüsü, kuyruk dolu, eklenti sürüm uyuşmazlığı, oturum değişti, istemci kapatıldı |
Banlı hesap error.code === ACCOUNT_BANNED_CODE ile ayırt edilir (Kimlik doğrulamaya genel bakış).
Sayılar ve JSON
i64/u64alanlarbigint’tir; 32 bit alanlar sınır denetimlinumber’dır.9007199254740993nkayıpsız taşınır.- SDK yükünü
JSON.parse/JSON.stringifyile yeniden dolaştırmayın;parseJson,stringifyJson,encodeModel,decodeModelkullanın. Ondalık sayılarLosslessNumberolur. - Varsayılan sınırlar: JSON 2 MiB, derinlik 96, düğüm sayısı 200 000.
- Eklenti protokolünde
i64değerler ondalık dizgedir (plugin-v1-decimal-int64). Bkz. Sayılar ve kodlama.
Kod üretimi (crx)
Eklenti şemasından tipli bağlama ve karakter tanımından tip üretmek için CLI kullanılır (yalnız Node; ağ erişimi yoktur, girdi dosyadır, çıktı yalnız yeni dosyaya yazılır, --check bayt karşılaştırır):
# Eklenti bağlaması: şema özeti (sha256) sabitlenir; değişirse üretim reddedilir
npx crx plugins generate --input plugin.schema.json --schema-sha256 "$SCHEMA_SHA256" --output binding.ts
npx crx plugins generate --input plugin.schema.json --schema-sha256 "$SCHEMA_SHA256" --output binding.ts --check
# Karakter tanımı tipleri
npx crx types generate --input definition.json --version 1 --schema-sha256 "$SCHEMA_SHA256" --output hero.ts
Eklenti şemasını nereden alacağınızı ve schemaSha256 sabitlemenin anlamını Eklenti bağlamaları sayfası anlatır.
Bilinen sınırlar
- Yalnız çerez kipi vardır; oyuncu istemcisinde Bearer ya da sunucu anahtarı kipi yoktur.
- Erişim JWT’si uygulamaya açılmaz (Sunucuda doğrulama).
- Magic link, unlink ve yerel platform girişleri için yardımcı yoktur.
- Görünen ad değiştirme (
POST /v1/auth/session/display-name) için yöntem yoktur. - Sunucu CORS’u yalnız ortam alan adı listesindeki origin’lere verir; oturum çerezi
SameSite=Laxolduğundan oyun ile Open Play aynı sitede olmalıdır, değilse ters vekil kullanın.