Bağlamalar ve şema sabitleme
Bu sayfa, bir eklentinin şema belgesinden (artifact) tipli istemci kodu üretmeyi ve istemcinizin hangi şema sürümüyle derlendiğini sabitlemeyi (pin) anlatır. Sabitleme sayesinde sunucu eklentiyi güncellediğinde istemciniz sessizce yanlış alanlarla konuşmak yerine uyuşmazlığı fark eder.
Şema belgesi ne içerir
GET /v1/plugins/{plugin_id}/schema ya da depodaki artifacts/plugins/<id>.plugin.schema.json aynı belgedir. Üst düzey alanlar:
| Alan | Anlam |
|---|---|
pluginId | Eklenti kimliği (ör. package-ride) |
version | Eklenti sürümü (ör. 1.44.0) |
schemaSha256 | schemas bölümünün özeti; şema sabitleme anahtarı (aşağıda) |
protocolVersion | Eklenti protokol ana sürümü (şimdilik 1) |
codecProfile | Tel kodlaması: plugin-v1-decimal-int64 (sayılar) |
schemas | Girdi ve çıktı JSON şemaları (bileşen adı → şema) |
queries, commands | İşlem tanımları |
modules | Realtime modülleri |
Bir işlem tanımı şu alanları taşır:
| Alan | Anlam |
|---|---|
name | İşlem adı; istemci kodunda yöntem adıdır (ör. claimLoginBonus) |
kind, method, path | command ya da query; HTTP yöntemi; tam yol |
requestSchema, responseSchema, schemaVersion | Girdi ve çıktı şemaları ve gönderilecek x-crx-schema-version |
requestMessageType, responseMessageType | Yoldaki {message_type} ve çıktı mesaj türü |
access | player, server ya da public |
retry | read (güvenle tekrar), same-key-command (yalnız aynı anahtarla tekrar), never |
journal | none, full ya da reference: SDK’nın komutu yerelde nasıl kaydettiği (yeniden yüklemeden sonra devam için) |
idempotencyRequired, receiptRetentionSeconds, resultPath | Komutlar için: anahtar zorunluluğu, kayıt süresi, sonuç sorgu yolu |
deprecated | Varsa kullanımdan kalkma bilgisi: since, sinceUnixS, replacement (sürümleme) |
Realtime modülü tanımı moduleId, input, state, events, signals, authorityMode, deliveryClass, interestPolicy, stateTtlMs ve sequencePolicy taşır; girdi ve durum mesajlarında maxBytes, ratePerSecond ve burst bulunur (protokol).
schemaSha256 nedir
schemaSha256, belgedeki schemas nesnesinin kurallı JSON (anahtarlar sıralı, boşluksuz, UTF-8) çıktısının SHA-256 özetidir (küçük harfli onaltılık, 64 karakter). Şema alanları değişirse özet değişir; sürüm numarası aynı kalsa bile. Bu yüzden “sürüm eşleşiyor” demek yetmez: özet eşleşmelidir.
Özeti kendiniz de yeniden hesaplayıp doğrulayabilirsiniz:
import { createHash } from "node:crypto";
type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
/** Anahtarları özyinelemeli sıralar; sunucudaki kurallı JSON biçimiyle aynı baytları üretir (boşluksuz). */
function canonical(value: Json): Json {
if (Array.isArray(value)) return value.map(canonical);
if (value !== null && typeof value === "object") {
const sorted: { [key: string]: Json } = {};
for (const key of Object.keys(value).sort()) sorted[key] = canonical(value[key] as Json);
return sorted;
}
return value;
}
/** Şema belgesinin `schemas` bölümünden `schemaSha256` değerini yeniden hesaplar. */
export function schemaSha256(artifact: { schemas: Record<string, Json> }): string {
return createHash("sha256").update(JSON.stringify(canonical(artifact.schemas)), "utf8").digest("hex");
}
/** Belgenin kendi bildirdiği özet, içeriğiyle uyuşuyor mu? */
export function artifactIsConsistent(artifact: { schemaSha256: string; schemas: Record<string, Json> }): boolean {
return schemaSha256(artifact) === artifact.schemaSha256;
}
Not
Bu hesap yalnız
schemasbölümünü kapsar; işlem listesini (commands,queries) ya daversionalanını kapsamaz. Bir işlemin yolu ya da erişim türü değişirse özet değişmeyebilir; bu yüzden bağlamayı yeniden üretirken belgenin tamamının farkına da bakın.
TypeScript bağlaması üretme
- Şema belgesini alın ve bir dosyaya kaydedin. Sunucudan: oyuncu oturumuyla ya da sunucu anahtarıyla
GET /v1/plugins/{plugin_id}/schema. - Özeti belgeden okuyun (
schemaSha256) ve release manifestinize/CI’ya yazın: bu sizin sabitleme değerinizdir. - SDK’nın
crxkomutuyla bağlamayı üretin:
npx crx plugins generate \
--input demo-game.plugin.schema.json \
--schema-sha256 ffc38829f30ea0dd1b829f351f905854a9875806fbc8a2bbd591815c7ee52c2f \
--output src/generated/demo-game.ts
- CI’da üretilmiş dosyanın güncel olduğunu denetleyin;
--checkdosyayı yazmaz, byte karşılaştırır:
npx crx plugins generate --input demo-game.plugin.schema.json \
--schema-sha256 ffc38829f30ea0dd1b829f351f905854a9875806fbc8a2bbd591815c7ee52c2f \
--output src/generated/demo-game.ts --check
Davranış:
- Girdi belgesi yerel dosyadır; komut ağa çıkmaz ve belgeyi yürütmez.
--schema-sha256zorunludur ve belgedekischemaSha256ile aynı olmalıdır; farklıysa üretim reddedilir. Özet otomatik yenilenmez: değişikliği bilinçli onaylarsınız.- Çıktı yalnız yeni dosya yazar; var olan dosyayı ezmez (üretimi yenilemek için dosyayı silin). Çıktı klasörü önceden var olmalı ve yolu sembolik bağ içermemelidir (komut yolu gerçek yoluyla karşılaştırır); sembolik bağ ve özel dosya reddedilir; hedef
.tsolmalıdır. Girdi dosyası en çok 2 MiB olabilir. - Başarıda
CRX PLUGINS GENERATE PASS(--checkileCRX PLUGINS CHECK PASS) yazar ve0ile çıkar. - Hata durumunda komut ayrıntı vermez (yol ve içerik sızmasın diye); tek satır
CRX TYPES FAIL: input/version/hash/output reddedildiyazar ve1ile çıkar. Önce girdi dosyasını, özeti ve çıktı yolunu kontrol edin. - Aynı girdi her zaman aynı bayt çıktısını verir.
Üretilen dosya, şemalardan TypeScript tiplerini (bigint olarak int64), işlem tablolarını, modül tanımlarını ve gömülü artifact + özeti taşır. Kullanım:
import { DemoGameBinding } from "./generated/demo-game.js";
const demo = client.plugin(DemoGameBinding);
await demo.queries.getGiftStatus({ includeHistory: true });
const handle = await demo.commands.claimDailyGift.prepare({ giftKey: "daily" });
access: "server" olan işlemler oyuncu istemcisinde görünmez ve çağrılamaz; bunları sunucunuzdan sunucu anahtarıyla HTTP ile çağırın (eklentileri çağırma). access: "public" sorgular game.publicPlugin(...) ile kimliksiz çağrılır.
Not
Komut tanımı
retry: "same-key-command"ise SDK ağ hatasında yalnız aynı anahtarla yeniden dener;journal: "full"ise sayfa yeniden yüklenince yarım kalan komut aynı anahtarla devam ettirilir. SDK kurulumu ve journal seçenekleri TypeScript SDK sayfasındadır.
Sabitleme (pin) ve uyuşmazlık
İstemci iki yerde sürüm uyumunu denetler:
- Derleme/başlatma zamanı, yerel:
createGameClientile bağlama kurarken oyununuzun release manifestindeki beklentiyi (pin) verirsiniz. Bağlama içindekipluginId,schemaSha256,protocolVersion,codecProfile(ve verdiysenizversion) beklentiyle birebir eşleşmezseCrxError(kind: upgrade_required,code: plugin_binding_mismatch) fırlatır; hiçbir istek gitmez. Bu denetim sunucuya sormaz; yalnız elinizdeki bağlamanın beklediğiniz bağlama olduğunu doğrular. - Sunucudaki gerçek belge: TypeScript SDK’sı çalışma zamanında
/schemaucunu kendiliğinden çağırmaz. Sunucu eklentiyi güncellediyse istemcinizin eski bağlamasıyla uyuşmazlığı görmek için belgeyi başlangıçta kendiniz sorgulayıp özeti karşılaştırın:
import { DemoGameBinding } from "./generated/demo-game.js";
/**
* Sunucudaki şema belgesini alır ve elinizdeki bağlamayla karşılaştırır. Uyuşmazsa istemciyi güncelleyin:
* yeni belgeyi alın, yeni `schemaSha256`'yı manifestinize yazın, bağlamayı yeniden üretin.
* Çerez oturumu açıkken tarayıcıda çalışır (aynı kökenden, `credentials: "include"`).
*/
export async function serverSchemaMatches(baseUrl: string, environmentId: string): Promise<boolean> {
const response = await fetch(`${baseUrl}/v1/plugins/${DemoGameBinding.pluginId}/schema`, {
credentials: "include",
headers: { "x-crx-env": environmentId },
});
if (!response.ok) throw new Error(`şema belgesi alınamadı: HTTP ${response.status}`);
const live = (await response.json()) as {
pluginId: string;
schemaSha256: string;
protocolVersion: number;
codecProfile: string;
};
return (
live.pluginId === DemoGameBinding.pluginId &&
live.schemaSha256 === DemoGameBinding.schemaSha256 &&
live.protocolVersion === DemoGameBinding.protocolVersion &&
live.codecProfile === DemoGameBinding.codecProfile
);
}
Unity SDK’sında bu denetim hazırdır: PluginClient.VerifyServerSchemaAsync() sunucunun belgesini alır ve pluginId, schemaSha256, protocolVersion, codecProfile (ve pin’de verilmişse version) alanlarını bağlamanın pin’iyle karşılaştırır. Uyuşmazlıkta OpenPlayException (ErrorKind.UpgradeRequired, kod plugin_binding_mismatch) fırlatır; pin alanı yok ya da bozuksa ErrorKind.Contract.
using System.Threading.Tasks;
using OpenPlay;
using OpenPlay.PackageRide;
/// <summary>
/// Oyuna girerken sunucudaki eklenti şemasının, pakete gömülü bağlamayla aynı olduğunu doğrular.
/// Uyuşmazlıkta (<c>UpgradeRequired</c>, kod <c>plugin_binding_mismatch</c>) oyuncuyu güncellemeye yönlendirin.
/// </summary>
public static class PluginPinCheck
{
public static async Task<bool> ServerSchemaMatches(OpenPlayClient client)
{
var rides = PackageRideClient.Create(client); // pin: pluginId, schemaSha256, protocolVersion, codecProfile, version
try
{
await rides.Plugin.VerifyServerSchemaAsync(); // GET /v1/plugins/package-ride/schema; yalnız pin alanlarını okur
return true;
}
catch (OpenPlayException ex) when (ex.Kind == ErrorKind.UpgradeRequired)
{
UnityEngine.Debug.LogWarning("Eklenti şeması değişti; istemciyi güncelleyin: " + ex.Code);
return false;
}
}
}
Uyuşmazlık olursa istemci ne görür
Sunucu eklentiyi güncelleyip istemciniz eski bağlamayla çağırırsa:
| Değişiklik | İstemci görür |
|---|---|
İşlemin schemaVersion değeri arttı | 400 invalid_argument (bilinmeyen şema sürümü). x-crx-schema-version eskiyi gönderiyordu |
| İşlem ya da mesaj türü kalktı | 404 not_found |
| Girdi şemasına zorunlu alan eklendi | 400 invalid_argument (şemaya uymayan yük) |
| Çıktı şeması değişti | SDK yanıtı bağlamaya göre çözemez; contract türünde hata |
| Eklenti ortamınız için etkin değil / sürüm yetkisi uyuşmuyor | 403 forbidden |
Çözüm hep aynıdır: güncel şema belgesini alın, bilinçli olarak yeni schemaSha256’yı manifestinize yazın, bağlamayı yeniden üretin ve istemciyi yayınlayın. Eski istemcilerin bir süre daha çalışması gerekiyorsa sunucuda eski şema sürümünün korunup korunmadığını eklenti sağlayıcınıza sorun; bunun garantisi platformda yoktur.
Unity bağlaması
Unity SDK’sında bağlama yalnız package-ride eklentisi için hazır gelir (OpenPlay.PackageRide: PackageRideBinding, PackageRideClient). Başka bir eklenti için Unity bağlaması bugün üretilemez: SDK’nın üretici betiği depo içi bir araçtır ve yayımlanan pakette gelmez. Üretici yalnız access değeri player ya da public olan işlemleri paketler (sunucuya özel işlem pakete girmez) ve yalnız plugin-v1-decimal-int64 kodlamasını kabul eder; int64 alanlar C# long olur.
Kullanım için Unity SDK sayfasındaki PluginCall örneğine bakın.
Sıradaki adım
- Sürüm kavramlarının tamamı (protokol, şema, kullanımdan kalkma): sürümleme.
- Sayı kodlaması: sayılar ve kodlama.