EYDefterEmre Yakut
← tüm yazılar
Sistem · Hayat

Oğlum için bir oyun platformu kurdum

Dokuz yaşındaki oğluma sürpriz olsun diye başladım. Tavla, okey, pişti, hangman ve bir robot dövüş oyunu. Hepsi çok oyunculu, hepsi gerçek sunucuda — ve en zor kısmı oyunlar değildi.

tarayıcı oyun ekranı sunucu PHP · tek istek durum? (son=412) yeni olay yok durum? (son=412) olay 413 — 6-3 atıldıkalıcı bağlantı yok — istemci düzenli aralıklarla soruyorsunucu tüm durumu değil, SON OLAYDAN SONRASINI gönderiyorhamle beklenirken sık · rakipteyken seyrek · sekme arkada durur

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.

bu projenin bana öğrettiği

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.

SistemHayat