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

Yeniden bağlanma ve limitler

Bu sayfa, realtime soketi koptuğunda durumun nasıl kurtarıldığını, ResyncRequired geldiğinde ne yapılacağını, boşta kapanma (4100) davranışını ve sunucunun uyguladığı limitleri anlatır. Mobil ya da dengesiz ağda oynanan bir oyun yazıyorsanız bu sayfayı baştan sona okuyun.

Kopma sonrası kurtarma

Soket her zaman kopabilir (ağ, dağıtım, yük dengeleyici). Open Play’de kurtarma yeni soketle yeniden katılma (re-join) ile yapılır; eski soketin durumu sunucuda yeniden kullanılmaz.

  1. Yeni bilet al. Her bağlantı yeni bir bilet ister (tek kullanımlık, 30 sn); eski bilet yeniden kullanılamaz (protokol).
  2. Authenticate, Ready. Yeni sessionEpoch gelir. Yeni soket aynı kaynağa yazmaya başladığında oyuncunun eski soketi artık yazar değildir: eski soketin girdileri Ack: Stale alır.
  3. Her abonelik için yeniden Join.
    • Kalıcı (durable) akışlarda: son aldığınız cursor’ı Join.cursor olarak verin. Sunucu kaldığınız yerden DurableEvent’leri tekrar oynatır ve yeni bir DurableRecovery bariyeri yollar (snapshot boştur).
    • Geçici (ephemeral) durumda: cursor yoktur; Join taze bir Snapshot getirir. Gönderilmiş ama Ack almamış Input’lar tekrar oynatılmaz: sonucu belirsizdir, oyun mantığınız en son Snapshot’a göre devam etmelidir.
    • Kalıcı komutlarda (Command): Ack gelmeden kopan komutun sonucu belirsizdir. Aynı idempotencyKey ile yeniden gönderin ya da sonucu sorgulayın (eklentileri çağırma); yeni anahtar üretmeyin.
  4. ResyncRequired gelirse (imleç yok, süresi dolmuş, başka oyuncuya ait, boşluklu ya da tekrar bütçesini aşmış) imleci atın ve cursor olmadan tekrar Join edin; bu taze bir anlık görüntü getirir. Aynı imleçle ısrar etmeyin.

İmleç (cursor) kuralları

  • Opaktır ve oyuncuya bağlıdır; başka oyuncunun imleci kullanılamaz. İçeriğini ayrıştırmayın.
  • Her DurableRecovery ve her DurableEvent yeni bir imleç taşır; en sonuncusunu saklayın.
  • Tek kurtarmada en çok 256 kalıcı olay tekrar oynatılır; fazlası ResyncRequired ile taze anlık görüntüye çevrilir.
  • İmleç ve teslimat saklama penceresi 24 saattir; ondan eski imleç ResyncRequired alır.
  • coveredEventIds’deki olaylar taze anlık görüntüde zaten vardır; bunları ayrıca uygulamayın.

TypeScript SDK ne yapar

SDK bu akışı sizin yerinize yürütür (RealtimeClient, client.realtime):

DavranışVarsayılanAyar
Otomatik yeniden bağlanmaaçıkautoReconnect
Deneme gecikmesimin(reconnectBaseMs · 2^(n−1), 30 000) ms + [0, reconnectBaseMs) jitterreconnectBaseMs: 1000 (100 ile 10 000 arası)
En çok deneme8maxReconnectAttempts: 0 ile 20 arası
Kimlik doğrulama zaman aşımı5 snauthTimeoutMs (en çok 5 sn)
Uygulama Ping aralığı15 snheartbeatMs: 10 ms ile 60 sn
Kalıcı komut zaman aşımı15 sncommandTimeoutMs: 1 ms ile 120 sn
Cevap bekleyen girdi penceresi4maxPendingInputs: 1 ile 8
Çerçeve boyutu16 KiBmaxFrameBytes (en çok 16 KiB)

Durum makinesi idle, ticketing, connecting, authenticating, ready, recovering, live, backoff, reauthRequired, upgradeRequired, idleClosed ve closed değerlerinden geçer. Önemli olanlar:

  • recovering / backoff: SDK yeniden bağlanıyor; aboneliklerin imleçleri korunur.
  • reauthRequired: bilet ya da kimlik reddedildi (AuthenticationFailed, 401/403). Oturumu yeniden kurun; SDK kendiliğinden sonsuz döngüye girmez.
  • upgradeRequired: sunucu protokol sürümünü ya da çerçeveyi tanımadı (UnsupportedProtocol, 426). İstemciyi güncelleyin.
  • Sunucu PayloadTooLarge ya da CapacityExceeded gönderirse SDK bunu geri basınç (backpressure) hatası olarak yüzeye çıkarır; bağlantı durumunu değiştirmez.

Not

Tamamlanmamış girdiler soket koptuğunda “belirsiz sonuçlu” olarak reddedilir ve yeniden bağlanınca otomatik tekrar gönderilmez. Sinyal (best-effort) mesajları da tekrar edilmez.

Boşta kapanma (4100)

Sunucu, girdi kabul eden (etkileşimli) bir aboneliği olan ve belirli süre oyuncu etkinliği göndermeyen soketi kapatır.

  • Kapanış kodu 4100, neden idle_timeout. Tipli Error çerçevesi yoktur; yalnız WebSocket kapanış çerçevesi.
  • Süre varsayılan 5 dakikadır ve operatör tarafından değiştirilebilir ya da kapatılabilir. Kontrol aralığı min(süre/4, 15 sn) olduğundan kapanış eşikten en çok bu kadar geç gelir.
  • Menüde ya da oyun öncesinde (girdi kabul eden abonelik yokken) sayaç işlemez.
  • Etkinlik sayılan şey eklentinin tanımıdır: girdi çerçevesi genellikle sayılır; uygulama Ping, Authenticate ve protokol düzeyi ping sayılmaz. Oyununuz duraklatılmış ekranda da bağlı kalacaksa eklentinin tanımladığı canlı tutma girdisini gönderin; duraklatma, garaj gibi ekranlarda canlı tutma girdisi göndermek AFK sayılmamasını sağlar.
  • Açık bir soket oturumu yaşatmaz: oturum ömrü ancak oyuncu REST isteği yaptıkça uzar (güvenlik); sunucu soketteki oturumu 25 saniyede bir yeniden doğrular.
  • Bu kapanışı bir hata olarak ele almayın: oyuncuya “Devam et” gösterin, onayında yeni bilet alıp yeniden bağlanın; imleçler sayesinde abonelikler kurtarılır.

SDK’da onIdleClose geri çağrısını verirseniz istemci idleClosed durumuna geçer ve kendiliğinden yeniden bağlanmaz; kullanıcı onayında connect() çağırın. Geri çağrıyı vermezseniz kapanış sıradan bir kopma gibi işlenir ve SDK kendisi yeniden bağlanır.

import { createPlayerClient, type PlayerClient } from "@ferdiunal/open-play-ts";

/**
 * Sunucu boşta kapanışında (`4100`, `idle_timeout`) SDK kendiliğinden yeniden bağlanmaz; oyuncuya "Devam et"
 * gösterin ve onayında `connect()` çağırın. Abonelikler imleçleriyle kurtarılır.
 */
export function createClientWithIdlePrompt(
  baseUrl: string,
  environmentId: string,
  showContinueButton: (onContinue: () => void) => void,
): PlayerClient {
  const client: PlayerClient = createPlayerClient({
    baseUrl,
    environmentId,
    realtime: {
      reconnectBaseMs: 250, // ilk yeniden bağlanma denemesi 250 ms (varsayılan 1000)
      onIdleClose: () => showContinueButton(() => void client.realtime.connect()),
    },
  });
  return client;
}

Limitler

LimitDeğerAşılınca
Bilet ömrü30 sn, tek kullanım, Origin’e bağlıAuthenticationFailed
Authenticate bekleme süresi5 snAuthenticationFailed, soket kapanır
Kimlik doğrulama fazı çerçevesi4 KiBPayloadTooLarge, kapanır
Çerçeve (kimlik doğrulama sonrası)16 KiBPayloadTooLarge, kapanır
Taşıma katmanı mesajı64 KiB16 KiB ile 64 KiB arası tipli hata alır; 64 KiB üstü tipli hata olmadan soket hatasıyla düşer
Input yüküen çok 4 KiB ve modülün maxBytes değeriInvalidPayload
Command yüküen çok 16 KiBInvalidPayload
Bağlantı başına çerçeve hızı40 çerçeve/sn ortalama, 4 sn’lik (160 çerçeve) patlama payıCapacityExceeded (retryable: true) ve soket kapanır
Oyuncu başına açık soket4CapacityExceeded (retryable: false), soket kapanır
Adres başına açık soket8CapacityExceeded (retryable: false), soket kapanır (operatör gözlem kipine alırsa yalnız sayılır)
Adres başına yeni bağlantı30/dakikayükseltme reddedilir
Soket başına abonelik32 (sunucu varsayılanı)Join reddedilir
Oturum yeniden doğrulama25 sn’de biroturum iptal edilmişse AuthenticationFailed ve kapanış
Modül kapısı yeniden kontrol2 sn’de bir1008 / 1013 ile kapanış
Kurtarmada tekrar oynatılan olay256ResyncRequired
İmleç saklama24 saatResyncRequired
Boşta kapanma5 dk (varsayılan)4100 idle_timeout

Notlar:

  • Çerçeve hızı bütçesi Input’la sınırlı değildir; alınan her metin çerçevesi ve protokol düzeyi Ping bir çerçeve sayılır (Pong sayılmaz). Dürüst bir istemci yaklaşık 30 ile 35 çerçeve/sn gönderir; 40/sn’nin altında kalın ve bir duraksamadan sonra biriken çerçeveleri toplu göndermeyin.
  • Sayılar sunucu varsayılanlarıdır; operatör bazılarını (boşta süresi, bağlantı sınırları, çerçeve bütçesi) ortam yapılandırmasıyla değiştirebilir. Barındırılan bir Open Play kullanıyorsanız değerleri sağlayıcınıza sorun.
  • Input hızı ayrıca modülün şema belgesindeki ratePerSecond ve burst değerleriyle tanımlıdır.

Sıkıştırma

  • Sunucu permessage-deflate (RFC 7692) sıkıştırmasını destekler, yalnız istemci teklif ederse devreye girer. Tarayıcılar bunu kendiliğinden teklif eder; ayrı bir şey yapmanız gerekmez. Benzer JSON çerçevelerinde belirgin küçülme sağlar.
  • Operatör sıkıştırmayı kapatabilir; kapalıysa soket sıkıştırmasız çalışır, protokol değişmez.
  • Unity yerel (native) WebSocket istemcisinde sıkıştırma henüz yoktur; bu bilinen bir kısıttır. WebGL’de tarayıcı bunu üstlenir (Unity SDK).

Sunucu tarafı canlılık sondası

Sunucu, bağlantı gecikmesini ölçmek için yaklaşık 5 saniyede bir protokol düzeyi WebSocket Ping gönderir. Tarayıcı ve standart istemci kütüphaneleri Pong’u kendiliğinden yanıtlar; elle WebSocket yazıyorsanız Pong yanıtını unutmayın. Bu sonda oyuncu etkinliği sayılmaz.

Sorun giderme

BelirtiOlası nedenÇözüm
Her bağlanışta hemen AuthenticationFailedBilet tekrar kullanıldı ya da 30 sn geçti; Origin bilet isteğinden farklıHer bağlantıda yeni bilet alın; bileti aldığınız origin’den bağlanın
CapacityExceeded ile kapanmaÇerçeve hızı ya da bağlantı sınırıÇerçeve gönderim hızını düşürün; eski sekmeleri kapatın (oyuncu başına 4 soket)
ResyncRequired döngüsüEski imleçle ısrarİmleci atın, cursor olmadan Join edin
5 dakika sonra kopmaBoşta kapanma (4100)“Devam et” akışı kurun ya da canlı tutma girdisi gönderin
Yükseltme 401Sorgu dizesi var, alt protokol istenmedi ya da Origin izinli değilProtokol: soketi aç

Daha fazlası için sorun giderme.