Oğlum dokuz yaşında ve bilgisayarda oyun oynuyor. Bir akşam aklıma geldi: hazır oyun oynayacaksa, bizim oyunlarımızı oynasa?
Ona söylemeden başladım. Ortaya arminefe.com çıktı.
İçinde ne var
- Tavla — iki kişilik, gerçek kural seti, zar sunucuda atılıyor
- Okey, pişti, yedili, uno — masa kurulur, davet edilir, oynanır
- Hangman ve battleship — basit ama iki kişilik
- Robot Savaşçıları — tick tabanlı bir dövüş RPG’si: on sınıf, kâğıt-bebek görsel sistemi, zırh kademesi, üretim (craft), ve iki para birimi
Sonradan kapsam genişledi: site artık sadece çocuklara değil, dört yaş grubuna birden açık. Yetişkin masaları ayrı.
WebSocket yok — ve olmaması gerekiyordu
Site paylaşımlı bir hostingte duruyor. Orada kalıcı bağlantı tutmak mümkün değil; her istek gelir, çalışır, ölür.
Bu bir kısıt gibi görünüyor. Ama oyunların türüne bakınca öyle değil: tavlada sıra sende ya da değil. Rakibin hamlesini 200 milisaniye sonra görmekle 900 milisaniye sonra görmek arasında oyun açısından hiçbir fark yok.
O yüzden istemci düzenli aralıklarla sunucuya soruyor: “bir şey değişti mi?”
istemci → sunucu : durum? (son_olay_id = 412)
sunucu → istemci: yeni olay yok ← ucuz cevap
…
istemci → sunucu : durum? (son_olay_id = 412)
sunucu → istemci: olay 413 — rakip 6-3 attı, pul B24'ten B18'e
Kritik nokta, sunucunun tüm durumu değil, son olaydan sonrasını göndermesi. Yoksa her sorguda bütün tahtayı yollarsın ve trafik boşa gider.
Aralık da sabit değil: hamle beklenirken sık soruyor, sıra rakipteyken seyrekleşiyor, sekme arka plana geçince duruyor. Bu üç kural, sunucu yükünü aynı deneyimle üçte birine indiriyor.
Robot dövüşü: tick
Dövüş oyunu farklı bir problem. Orada gerçek zamanlı bir şey oluyor ve iki tarafın da aynı şeyi görmesi gerekiyor.
Çözüm dövüşü ayrık adımlara bölmek oldu. Dövüş gerçek zamanda akmıyor; sunucu onu bir kerede baştan sona hesaplayıp bir olay listesi üretiyor:
tick 1 : A saldırır → 14 hasar (kritik değil)
tick 2 : B savunur → 6 hasar emilir
tick 3 : A yetenek → "aşırı yükleme" (3 tick sürer)
tick 4 : B saldırır → ıskalar
…
tick 21: B düşer
İstemci bu listeyi alıp oynatıyor — animasyon, hasar sayıları, ses. Yani gördüğün şey canlı bir dövüş, hesaplanan şey bir kayıt.
Bunun iki faydası var: iki oyuncu da aynı dövüşü görüyor (çünkü aynı listeyi izliyorlar), ve dövüş tekrar izlenebiliyor.
Kapıyı kapalı tutmak
Bir çocuk sitesinde kayıt formu açık bırakılmaz. Giriş QR koduyla: ben bir davet üretiyorum, ekranda QR çıkıyor, çocuk telefonuyla okutuyor ve hesabı açılıyor. Kod tek kullanımlık ve süreli.
Böylece kimin girdiğini biliyorum ve rastgele biri kayıt olamıyor. İki yönetici var: ben ve babası olduğum kişi — yani ikimiz de panele bakabiliyoruz.
Kullanıcısı tek kişi olan bir yazılımda geri bildirim acımasız oluyor. Bir şey yavaşsa “yavaş” demiyor, oynamayı bırakıyor. Bir kural karmaşıksa “anlamadım” demiyor, o oyunu açmıyor. Hiçbir müşteri bana bu kadar net sinyal vermedi.
Sonuç
Ona gösterdiğim gün ilk sorduğu şey “bunu sen mi yaptın?” oldu. İkinci sorusu “arkadaşımı davet edebilir miyim?” — ki davet sistemi tam da o yüzden vardı.
Teknik olarak öğrendiğim şey şu: kısıt tasarımı basitleştiriyor. WebSocket olmadığı için olay tabanlı bir protokol yazmak zorunda kaldım; o protokol sonradan her oyuna aynen uydu. Kalıcı bağlantı olsaydı muhtemelen her oyuna ayrı bir çözüm yazardım.