1 puan yazan GN⁺ 2024-06-14 | 1 yorum | WhatsApp'ta paylaş
  • Serious Engine 1, tek oyunculu, çok oyunculu ve demo oynatmayı aynı deterministik simülasyonun üzerine kurarak, her tikte tüm durumu kaydetmek/iletmek yerine oyuncu eylemlerini ve oyun akışı bloklarını kaydedip iletir
  • Demo ve çok oyunculu mod, başlangıç oyun durumunu paylaştıktan sonra CPlayerAction gibi girdi deltalarını uygular; bu yüzden rastgele sayı tohumu ve oyun mantığının determinizmi senkronizasyonun anahtarıdır
  • Ağ katmanı, yavaş hatlarda bile oyunu sürdürebilmek için UDP üzerinde sıra numarası, ACK, yeniden gönderim, bant genişliği sınırlaması ve bağlantı başına tamponları doğrudan yönetir
  • CNetworkMessage serileştirme, LZ77/LZRW1 sıkıştırma ve XOR tabanlı delta kodlama sağlar; ancak sohbet dâhil mesajlar şifrelenmez, yalnızca sıkıştırma uygulanabilir
  • Serious Sam’in istemci-sunucu modeli, her istemcinin kendi simülasyonunu sürdürmesiyle bant genişliğinden tasarruf eder; buna karşılık senkronizasyon doğrulama, tahmin, yeniden gönderim ve CRC kontrolü gibi yardımcı mekanizmalara ihtiyaç duyar

Serious Engine’in ortak simülasyon yapısı

  • Serious Engine 1 kaynak kodu 2016’da GNU GPL v2 ile yayımlandı; bu analiz, yayımlanan bu kod tabanını okuma ve hata ayıklama çalışmalarına dayanıyor
  • Serious Sam en baştan çok oyunculu bir oyun olarak tasarlandı; tek oyunculu kampanya da içeride çok oyunculu yapının özel bir durumu gibi çalışır
  • Motorun desteklediği modlar şunlardır
    • Çevrimdışı tek oyunculu kampanya
    • Çevrimiçi, LAN, yerel co-op oynanış ve çeşitli oyun modları
    • Tek istemcide birden fazla oyuncunun katıldığı bölünmüş ekran
    • Demo kaydı ve oynatma

Demo kaydı: tüm durum yerine eylemleri kaydetme

  • Demoyu her tikte tüm oyun durumu olarak kaydetmek dosyayı büyüteceği için Serious Engine, kayıt başlangıcındaki tüm oyun durumunu bir kez kaydeder ve sonrasında her tikte oyun akışı bloklarını kaydeder
  • Oyun akışı bloklarında şu mesaj tipleri bulunur
    • MSG_SEQ_ALLACTIONS: oyuncu eylemleri
    • MSG_SEQ_ADDPLAYER: oyuncu ekleme
    • MSG_SEQ_REMPLAYER: oyuncu kaldırma
    • MSG_SEQ_PAUSE: duraklatma veya devam ettirme
    • MSG_SEQ_CHARACTERCHANGE: oyuncu karakteri özellik değişikliği
  • Kilit unsur MSG_SEQ_ALLACTIONS’tır; motor, etkin oyuncu başına CPlayerAction nesnesini ters serileştirip CPlayerTarget’a uygular
  • CPlayerAction oyuncu durumunu içerir
    • Dünya uzayı bazında hareket hızı pa_vTranslation
    • Dünya uzayı bazında karakter dönüşü pa_aRotation
    • Dünya uzayı bazında görüş dönüşü pa_aViewRotation
    • O anda basılı düğmeler pa_ulButtons
    • TSC tabanlı milisaniye zaman damgası pa_llCreated
  • Oynatma sırasında başlangıç oyun durumu okunur ve her tikteki oyuncu eylemleri gerçek oynanıştaki gibi uygulanır

Determinizmin gerekli olma nedeni

  • Bu yapı, oyun içindeki her şeyin tamamen öngörülebilir olduğu ve oyunu yalnızca oyuncu eylemlerinin değiştirdiği varsayımına dayanır
  • Rastgele sayılar da oyun durumunun parçası olan tohumu kullanan sözde rastgele sayı üreteciyle işlenir
    • CEntity::IRnd(), CSessionState::Rnd() kullanır
    • ses_ulRandomSeed, oyun durumunun ters serileştirilmesi sırasında başlatılır
  • Gerçek bir rastgele sayı üreteci veya farklı tohumlar kullanılırsa aynı demo oynatıldığında bile sonuç değişebilir; bu da senkronizasyon uyumsuzluğuna yol açar

Kayan nokta ve tik işleme

  • Serious Sam’in PC sürümü başlangıçta Windows’a özel çıktığı için, aynı derleyici ve çalışma zamanının kullanıldığı varsayımı kayan nokta senkronizasyon sorunlarını azalttı
  • Renderer bir DLL’dir ve OpenGL ya da DirectX API çağrıları FPU hassasiyetini değiştirebildiğinden Serious Engine, CSetFPUPrecision FPUPrecision(FPT_24BIT) gibi hassasiyet koruyucuları kullanır
  • Yuvarlama kontrolünün açıkça ayarlandığı bir yer bulunamadı; ancak _controlfp ile _RC_NEAR durumunu kontrol eden assert’ler vardır
  • Oyun mantığı render kare hızından ayrıdır
    • Render işlemi donanıma ve ayarlara göre değişir; içeride 500 FPS ile sınırlandığı görülüyor
    • Oyun mantığı saniyede 20 tik olarak sabittir
  • Akıcı hareket, mevcut tik ile önceki tik arasında doğrusal enterpolasyon yapılarak oluşturulur; konsoldan /net_bLerping=0 ile enterpolasyon kapatılabilir

UDP üzerine kurulan özel paket katmanı

  • Serious Engine çok oyunculu kodunda StartPeerToPeer_t gibi fonksiyon adları kalsa da gerçek model istemci-sunucu yapısıdır
  • Sunucu istemci mesajlarını alıp işler ve ilgili bilgileri tüm istemcilere iletir
  • Her oyuncu, demo sistemine benzer biçimde kendi simülasyonunu çalıştırır ve diğer oyuncuların eylem bilgilerini alarak durumu ilerletir
  • Ağ UDP kullanır; sıralama bozulması ve kayıpları çözmek için üzerine özel bir protokol bindirilir
  • CPacket paket sırasını ve güvenilirliği yönetir
    • pa_ulSequence: sıralama ve yinelenenleri eleme için kullanılan sıra numarası
    • pa_ubReliable: güvenilirlik bayrağını içerir
    • pa_ubRetryNumber: yeniden gönderim sayısını izler
    • pa_tvSendWhen: planlanan gönderim zamanı ve tıkanıklık kontrolü için kullanılır
  • Güvenilir paketler ACK bekler; ACK yoksa yeniden gönderilir
    • Maksimum yeniden deneme sayısı net_iMaxSendRetries ile ayarlanır ve varsayılan değerin 10 olduğu görülüyor
    • Yeniden gönderim aralığı net_fSendRetryWait ile ayarlanır ve varsayılan değerin 0.5f olduğu görülüyor
  • Güvenilir paketler, birden çok pakete bölünmüş akış oluşturabilir
    • İlk paket UDP_PACKET_RELIABLE_HEAD
    • Son paket UDP_PACKET_RELIABLE_TAIL
    • Tek bir güvenilir paket iki bayrağa da sahiptir
  • Güvenilir olmayan paketler kaybolduğunda akış bozulabileceği için akış oluşturmaz

Bağlantı yaşam döngüsü ve paket yönlendirme

  • CCommunicationInterface paket katmanı iletişiminden sorumludur ve sunucu, istemci ve yayın için arayüz fonksiyonlarına sahiptir
  • Sunucu ve istemci arayüzleri, bağlantı hedefinin zaten bilindiğini varsayar; broadcast arayüzü ise rastgele adreslerle gönderme/alma için kullanılır
  • CCommunicationInterface iki ana tampon tutar
    • cci_pbMasterInput: gelen UDP paketlerini CPacket olarak ters serileştirip saklar
    • cci_pbMasterOutput: gönderilecek CPacket’ları serileştirip soket API’sine iletir
  • İstemci başına gerçek iletişim soyutlamasını CClientInterface üstlenir
    • Sunucu, cm_aciClients dizisiyle her oyuncu arayüzünü tutar
    • İstemci, cm_ciLocalClient ile sunucuyla iletişim kurar
    • Hem istemci hem sunucu bağlantı kurmak için cm_ciBroadcast kullanır
  • CAddress içindeki adr_uwID, istemciye özgü tanımlayıcı veya broadcast paketi işareti olarak kullanılır
    • Değer '//' veya 0 ise broadcast paketidir
    • Diğer değerler oturum içindeki istemci ID’sidir

Bağlantı kurma ve temel güvenlik önlemleri

  • İstemci, sunucuya bağlanmak için UDP_PACKET_CONNECT_REQUEST bayraklı güvenilir broadcast paketi gönderir
  • Sunucu, aynı adres ve porta sahip bir istemci zaten bağlıysa isteği yok sayar
  • Yeni istemciyse sunucu boş bir istemci arayüzü bulur ve şu işlemleri yapar
    • İlgili istemci için benzersiz tanımlayıcı oluşturur
    • Tanımlayıcıyı UDP_PACKET_CONNECT_RESPONSE güvenilir broadcast paketiyle istemciye gönderir
  • Tanımlayıcı yalnızca sabit indeks kullanmaz; zamanlayıcı değerinin bir kısmı ile istemci indeksini birleştirerek oluşturulur
  • Başka bir oyuncunun kimliğine bürünmek için uwID’yi tahmin etmek gerektiğinden saldırı yüzeyi azalır
  • Bağlı olmayan bir oyuncu broadcast olmayan paket gönderirse Serious Engine konsola uyarı yazdırabilir

Tek oyunculu ve demo, yerel bağlantının özel durumlarıdır

  • Tek oyunculu ve demo oynatma da içeride sunucu ve istemci barındırır, ancak aynı süreç içinde çalışır
  • Aynı süreç içindeki bileşenlerin soket kullanması gerekmediği için Client_OpenLocal(), yerel istemci arayüzü ile sunucu tarafı arayüzünü birbirine bağlar
  • Bağlanan iki CClientInterface, ExchangeBuffers ile bir tarafın çıkış tamponundaki paketleri diğer tarafın giriş tamponuna taşır
  • Yerel oynanışın ana giriş/çıkış tamponlarından ve gerçek ağ soketinden geçmesi gerekmez

Ağ mesajı katmanı

  • CNetworkMessage, paketlerin üzerindeki mesaj soyutlamasıdır; akış gibi okunup yazılabilir
  • Mesajlar Read, Write, ReadBits, WriteBits ve <<, >> operatörleriyle serileştirilir/ters serileştirilir
  • Alt mesajlar da içerebilir; gerekli veriler yazıldıktan sonra Shrink ile tampon boyutu veri boyutuna uydurulabilir
  • CNetworkMessage tamponu AllocMemory ile ayrılır ve içeride malloc çağırdığı görülüyor
  • CLinearAllocator mevcut olsa da kullanıldığı yer bulunamadı; mesaj tamponları sık sık ayrılıp yeniden ayrılır

Sıkıştırma ve delta kodlama

  • Mesajlar, belirtilen sıkıştırıcıyla veya mesaj tipine özel varsayılan sıkıştırıcıyla sıkıştırılabilir
  • MESSAGETYPE içinde alt 6 bit tipi, kalan 2 bit ise sıkıştırma yöntemini gösterir
    • LZ77 CzlibCompressor
    • LZRW1 CLZCompressor
    • Sıkıştırmasız
  • Varsayılan sıkıştırmanın LZRW1 olduğu görülüyor; net_iCompression kabuk değişkeniyle değiştirilebilir
  • CPlayerAction olduğu gibi gönderilmez; mevcut eylem ile son eylemin XOR’lanmasıyla oluşturulan delta gönderilir
  • Alıcı taraf, son eyleme deltayı yeniden XOR’layarak özgün CPlayerAction’ı geri oluşturur
  • Delta, veri değişimi küçük olduğunda daha iyi sıkıştırılır
    • Basılı tuşlar çoğu zaman birçok frame boyunca aynı kalır
    • Hız ve görüş dönüşü de kayan nokta aralığının tamamında büyük sıçramalar yapmaz
  • Sunucu birden fazla oyuncu eylemini MSG_SEQ_ALLACTIONS ile topluca gönderdiğinde bu yöntemin etkisi daha da artabilir

Mesaj şifreleme ve sohbet

  • Serious Engine mesajları şifrelenmez
  • net_iCompression=0 ile sıkıştırma kapatılırsa oyun içi sohbet mesajları UDP paket payload’unda düz metin olarak görünür
  • Gerçek durumda sıkıştırma açıksa paket dinleyen kişinin LZ sıkıştırma akışını anlaması ve açması gerekir; ancak gerekli veri paketin içindedir
  • O dönemin oyunları çoğu zaman şifrelemeyle uğraşmazdı; kimlik doğrulama ve anahtar değişimi gibi mekanizmalar karmaşıklığı artırabilirdi
  • O dönemde web’in de büyük kısmı HTTP idi

Oyun oturumu katmanı

  • CNetworkLibrary, adına rağmen oyun durumu CSessionState dâhil oyun oturumunu yönetir
  • CNetworkLibrary, daha önce bahsedilen CMessageDispatcher’dan kalıtım alır
  • Sunucu başlatılırken motor şu adımları uygular
    • Bağlanan istemcilerin sunucuyla aynı dosyalara sahip olup olmadığını doğrulamaya hazırlanmak için CRC toplamayı başlatır
    • Yeni bir CSessionState oluşturur, serileştirir ve temel durum ga_pubDefaultState olarak saklar
    • Yerel dünya örneğini yükler
    • Küresel iletişim arayüzünü başlatır
    • Yerel oturum durumunu başlatır ve istemci bağlandığında temel durum ile sunucunun mevcut durumu arasındaki durum deltasını gönderebilecek hâle getirir
    • CRC toplamayı bitirip ga_ulCRC içine kaydeder
  • CRC kontrolü, hile önlemeden çok senkronizasyon uyumsuzluğunu erken saptamaya yakındır
  • İstemci katılım süreci şu akışı izler
    • Boş yerel oturum durumu ve iletişim arayüzü başlatılır
    • MSG_REQ_CONNECTREMOTESESSIONSTATE ile derleme sürümü, mod adı, sunucu parolası, yerel oyuncu sayısı ve CSessionSocketParams gönderilir
    • MSG_REP_CONNECTREMOTESESSIONSTATE ile mesaj, dünya dosya adı, zorluk/oyun modu bayrakları ve oturum özellikleri alınır
    • Referans oyun durumu başlatılır
    • MSG_REQ_STATEDELTA gönderilerek sunucunun mevcut durumuyla fark istenir
    • MSG_REP_STATEDELTA alındıktan sonra ters diff ile oyun durumu akışı yeniden oluşturulur
    • CSessionState::Read_t() ile yerel oturum durumu başlatılır
    • CRC kontrolü yapılır; uyuşmazlık varsa bağlantı kesilir

Ana döngü ve oyun akışı yeniden gönderimi

  • İstemci ve sunucunun ana döngüleri genel olarak benzerdir; ancak sunucu ek işler yapar
  • Döngü, yerel istemci arayüzünü ve broadcast arayüzünü günceller; yerel oturum durumu da gelen ağ mesajlarını işler
  • Sunucu ayrıca eşleştirilmiş istemci arayüzleri arasında tampon değişimini, sunucu tarafı istemci arayüzü güncellemesini, GameAgent güncellemesini ve uzaktan yönetim kabuğu komutlarının işlenmesini üstlenir
  • SessionStateLoop() güvenilir olmayan ve güvenilir mesajları ayrı işler
    • Güvenilir olmayan: MSG_GAMESTREAMBLOCKS, MSG_KEEPALIVE, MSG_INF_PINGS, MSG_CHAT_OUT
    • Güvenilir: MSG_INF_DISCONNECTED, MSG_ADMIN_RESPONSE
  • MSG_GAMESTREAMBLOCKS güvenilir olmayan bir mesajdır; ancak eksik kalırsa senkronizasyon bozulabilir
  • Serious Engine, oyun akışı işleme aşamasında eksik dizileri kontrol eder ve yeniden gönderim ister
    • Beklenen sonraki sıra bloğu varsa işler
    • Sonraki blok yoksa ve daha yeni blok da yoksa o döngüde hiçbir şey yapmaz
    • Sonraki blok yok ama daha yeni blok varsa eksik olma ihtimaline karşı zaman aşımı ayarlar
    • Zaman aşımından sonra MSG_REQUESTGAMESTREAMRESEND ile eksik blok sırası ve sayısı istenir
  • Sunucu istenen oyun akışı bloklarını yeniden gönderir

Oyun akışı bloklarının işlenmesi

  • MSG_SEQ_ADDPLAYER, oyuncu oyuna girdiğinde gönderilir ve oyuncu indeksi ile CPlayerCharacter tanımlayıcısını içerir
  • MSG_SEQ_REMPLAYER, oyuncu bağlantısı kesildiğinde gönderilir ve yalnızca oyuncu indeksini taşır
  • MSG_SEQ_CHARACTERCHANGE, oyuncu adı, takım ve görünüm değişikliklerini iletir
    • Serious Sam’de görünüm tamponu CPlayerSettings yapısını içerir
    • Bunun içinde oyuncu model dosya adı, silah otomatik seçim politikası, nişangâh tipi ve çeşitli bayraklar bulunur
  • MSG_SEQ_PAUSE, duraklatma veya devam ettirmeyi iletir ve istekte bulunan oyuncu adını konsola yazdırır
  • MSG_SEQ_ALLACTIONS, mevcut tik zamanını ve tüm oyuncu eylemlerini içerir
    • Her CPlayerTarget’a CPlayerAction uygular
    • Ardından zamanlayıcılar, olaylar, hareketli entity’ler ve fizik işlemleri yapılır
  • Senkronizasyon kontrolü MakeSynchronisationCheck() ile yapılır
    • Entity’ler ve oyuncu hedefleri gibi öğelerin ChecksumForSync() çıktılarıyla CSyncCheck oluşturulur
    • İstemci MSG_SYNCCHECK mesajını sunucuya gönderir; sunucu durumuyla uyuşmazsa bağlantı kesilir

Tahminle girdi gecikmesini azaltma

  • Tahmin, internet gecikmesi nedeniyle hızlı oyunların hantal hissettirmesi sorununu azaltmaya yarayan bir mekanizmadır
  • Yerel oyuncu tahmini, sunucuya gönderilen eylemleri kullanır
  • Uzak oyuncu tahmini, sunucudan en son alınan eylemi kullanır
  • İstemci sunucu yanıtını beklemeden gerçek oyun durumunu doğrudan ilerletirse diğer oyuncu eylemlerini bilemediği için senkronizasyon bozulabilir
  • Serious Engine gerçek durum ile tahmin durumunu karıştırmamak için predictor kullanır
    • Predictor, normal entity’ye bağlı bir “hayalet” kopyaya yakındır
    • Temporary predictor tahmin sırasında oluşturulur ve gerçek oyun durumunda bağlı bir entity’si yoktur
  • Tahmin tikleri işlenirken yalnızca predictor entity’leri işlenir
  • İstemci sunucudan oyuncu eylemlerini aldığında mevcut predictor’ı yok eder ve yeni bir tahmin döngüsü başlatır
  • Render sırasında tahmin edilen özgün entity çizilmez, predictor çizilir; böylece gerçek durumu fazla değiştirmeden hareket ilerliyormuş gibi görünür
  • Yerel oyuncu yalnızca plt_abPrediction içinde saklanan sunucuya gönderilmiş eylem sayısı kadar tahmin yapabilir
  • Uzak oyuncu tahmininde cli_bLerpActions kapalıysa son alınan eylem tekrarlanır
  • cli_bLerpActions açıksa son iki eylem arasında doğrusal enterpolasyon yapılır; ancak varsayılan değer kapalıdır

Doom ve Quake ile karşılaştırma

  • Doom’un ağ yapısı pratikte peer-to-peer idi; ancak istemciler CPlayerAction benzeri yapıları değiş tokuş eder ve her biri bağımsız simülasyon çalıştırırdı
  • Doom da demo kaydı ve oynatma için benzer bir sistem kullanır
  • Quake farklı bir yapı kullanıyordu; istemci büyük oyun mantığını doğrudan işlemez, daha çok sunucudan durum güncellemeleri alırdı
  • Quake yaklaşımında senkronizasyon sorunları daha az dert edilir; sunucunun duvar arkasındaki entity bilgilerini göndermemesi gibi yollarla hile önleme de daha kolay olabilir
  • Serious Sam’de Quake’e göre çok daha fazla etkin düşman ve nesne içeren oturumlar yaygın olduğundan, her tikte çok sayıda nesne durumunu göndermek bant genişliği açısından ağır olabilirdi

Taşınabilirlik ve yapısal sınırlar

  • Bazı ağ mesajları, struct’ları reinterpret cast’e yakın bir yöntemle serileştirir
  • Tek derleyici ve tek platform varsayımında işe yarayabilir; ancak çapraz platform oyunlarda struct yerleşimi ve padding farklı olabilir
  • 32 bit çalıştırılabilir dosyalar 4 bayt sınırına, 64 bit çalıştırılabilir dosyalar ise 8 bayt sınırına hizalamayı deneyebilir
  • Endian sorunu da vardır
    • x86 PC little-endian’dır
    • PS3 big-endian’dır
  • Serious Engine’in yapısı, ağ ve dosya gibi aktarım ortamları arasındaki farkı oyun mantığından soyutlaması bakımından zariftir; ancak tüm istemcilerin oyun durumunun bir kopyasına sahip olduğu bir model olduğu için hile mümkündür
  • Örneğin değiştirilmiş bir istemci, deathmatch’te duvar arkasındaki diğer oyuncuların konturlarını gösterebilir

1 yorum

 
GN⁺ 2024-06-14
Hacker News yorumları
  • Serious Sam’in ağ kodu uygulamasını yapan geliştiricilerden biriydim
    Croteam ofisinde masaların altında sık sık uyur, Usenet’i karıştırırdım; özellikle de QuakeWorld’ün tahmin sistemini açıklayan bir yazıdan ilham almıştım
    O gece çalışma arkadaşım Dan, gecikmeyi simüle edebilen eski bir 486 Unix makinesini yönlendirici olarak kullanıp test yaparken, ben de basit bir minimum uygulanabilir sürümü kodladım
    Bu, asıl oyunun onun üzerine inşa edilmesinden çok önceydi

    • Kafaları patlayan adamların neden çığlık atarak koştuğunu ve yaklaştıkça seslerinin neden yükseldiğini merak ediyorum
      O sesi hâlâ duyuyorum
    • Efsanevi bir oyunun nasıl yapıldığına dair bir yazının altında birinin çok sıradan biçimde “ha evet, onu ben yapmıştım, eğlenceliydi” demesi havasını gerçekten seviyorum
    • O oyunu gerçekten çok severdim
      Co-op oyunları ve nişancı oyunlarını çok severdim ama arkadaşlarım hep Counter-Strike oynamak isterdi
      Serious Sam sayesinde ara sıra sevdiğim oyunu birlikte oynamaya onları ikna edebiliyordum
    • Serious Sam, amcamın en sevdiği oyunlardan biriydi; Duke Nukem 3D’yi de severdi
      Amcam hayatımda gerçekten önemli bir insandı ve onun sevdiği oyunları oynamak, onunla ilgili anılarıma bağlanmanın güzel bir yolu
      Harika bir oyun, harika çok oyunculu deneyim ve çok güzel anılar olarak kaldı
    • Bölünmüş ekran oynama için gerçekten minnettardım
  • Serious Sam her zaman güçlü bir LAN partisi oyunu oldu
    Bunun nedeni döneminin en gösterişli oyunu olması ya da birinin bunu önceden planlaması değildi
    Diğer oyunlar sürücü sorunları, ısınma sorunları, güncelleme sorunları vb. yüzünden can çekişirken Serious Sam’i çalıştırınca öylece çalıştığı için LAN partilerine hükmediyordu
    Devam oyunlarında da bu özellik sürdü; birinin PC’si tamamen dağılsa bile bölünmüş ekranı istikrarlı biçimde destekliyor, giriş aygıtlarını da iyi yönetiyordu
    Oyunun sistem tarafı güvenilirlik açısından gerçekten olağanüstüydü

    • Serious Sam berbat donanımlarda bile hızlı çalışırken oldukça da iyi görünüyordu
      Benzer şekilde Counter-Strike’ın grafikleri de çok iyi değildi ama tost makinesi gibi PC’lerde bile iyi çalıştığı için uzun süre popüler kaldı
    • Birden fazla hoparlörden gelen “aaaaaaaaaaaaah” sesi keyifliydi
    • 90’ların sonunda EA’in teknik destek web sitesini yönetiyordum; destek/QA ekibi mesai sonrası büyük gruplar hâlinde Serious Sam oynardı
      İş bilgisayarlarında istikrarlı çalışan tek birinci şahıs nişancı oyunuydu ve gerçekten çok eğlenceliydi
      O dönemde EA’de QA ile teknik destek epey iç içeydi; destek çalışanları yazın yıl sonu çıkışlarını yetiştirmek için kurum içi beta testçisi olarak çalışır, Noel civarı telefonlar artınca kışın teknik destek yaparlardı
  • Vigilante 8’in Game Boy Color portunda çok oyunculuyu uygularken deterministik oynanış kullandım
    GBC link kablosu aynı anda iki yönde 1 bayt gönderip alıyordu ve kablonun iki yanında birbirini dolduran bir çift kaydırma yazmacı gibi çalışıyordu
    Oyun GBC’nin kare hızına kilitliydi; pratikte her V-Blank’te çok fazla ekran yenileme işi gerekiyordu ve bunu kaçırırsanız akıcı kaydırma bozuluyordu
    Çok oyunculu başlarken seed’leri değiş tokuş ediyorduk ve çalışma şekli şöyleydi: A karesinde girişi okuyup 1 bayta sıkıştırıyor ve gönderim tamponuna koyuyorduk. B karesi render edilirken aktarım gerçekleşiyordu. C karesinin başında, A karesinde gönderilen yerel girişe ve B karesinde alınan karşı taraf girişine sahip oluyorduk
    Bu girişleri oyun durumuna uygulayıp C karesini render ediyorduk; dolayısıyla hem yerel hem de uzaktan girişler 1 kare gecikmeyle uygulanıyordu
    Yerel oyunda giriş gecikmesi yoktu; yani çok oyunculuda kaybettiyseniz suçu gecikmeye atabilirsiniz, gerekirse özel olarak beni de suçlayabilirsiniz

    • Birkaç hafta önce bu kartuşu aldım; eski GBC rumble kartuşlarını sevdiğim için link kablosu çok oyunculu desteği olmasına hayran kaldım
      Gerçekten iyi bir oyun ve çok oyunculu uygulamasına dair teknik açıklama da çok güzel
  • Croteam gerçekten yetenekli bir oyun geliştirme ekibi
    The Talos Principle’ın 1. ve 2. oyunlarından çok keyif aldım; ilk oyunda tamamen özel bir Vulkan oyun motorunu erkenden yapan öncülerden biriydiler

    • The Talos Principle 2’de kendi motorlarını bırakıp Unreal Engine kullanmalarına çok üzüldüm
    • Talos 2 DLC’sinin bu cuma Steam’e geldiğini az önce öğrendim
  • Age of Empires’taki “28.8K’da 1500 okçu” fikriyle aynı şey mi, merak ediyorum
    https://www.gamedeveloper.com/programming/1500-archers-on-a-...

    • Evet. İkisi de deterministik lockstep sistemi
      Pek çok oyun uzun süre bu sistemi kullandı, ama bugün çeşitli nedenlerle eskisine göre daha az yaygın gibi görünüyor
    • Bu sayı, Tempest Rising’de birim sınırı olmasının ne kadar tuhaf olduğunu gösteriyor
      Kaynağın ya vardır ya yoktur; sırf sınır koymak için sınır koymaya gerek yok
  • Bant genişliği 10 kat fazla olan oyunlar bile bu kadar çok düşmanı desteklemekte zorlanıyor
    Şimdi fark ettim: teknik kaynakların artışı, bilgisayar biliminin verimliliği ve yaratıcılığı üzerinde ters etki yapıyor gibi
    Bant genişliği, depolama, bellek ve işlem gücü arttıkça yazılım, kaynak birimi başına daha yavaş, daha şişkin ve daha beceriksiz hâle gelerek tepki veriyor
    Buna Benjamin Button tarzı yazılım tasarımı etkisi denebilir

    • “Bu kadar çok düşman” derken kaç kişiden söz edildiğini merak ediyorum
      Yazıda bir sayı varsa bulamadım
      Modern oyunlar arasında da çok oyunculu olup düşman sayısı yeterince “kitlesel” sayılabilecek pek çok örnek var; önemli olan oyuncu sayısıysa, çok büyük oyuncu sayılarını destekleyen oyunlar da var
    • Bu genelde daha çok yazılım şişkinliği olarak bilinir
    • Evet, bu Wirth Yasası olarak bilinir
  • İleri gitmekten çok geriye doğru hareket ederek daha fazla zaman geçirilen bir oyundu bence

    • Hatta bunu konu alan bir oyun bile var; adı “I Hate Running Backwards”
      Steam’de var ve aynı yapımcı mı bilmiyorum ama Serious Sam evreninde geçiyor
    • Sen ve Netrisca birlikteydiniz, ama o binlerce düşman yalnızdı
    • Bazı silahlar, fare düğmesine basmadan saniyenin kesri kadar önce ateş ediyormuş gibi hissettirirdi
    • O şekilde geri çekilirken ortaya çıkan cephaneyi çaresizce toplamaya çalıştığımı da hatırlıyorum
  • Factorio’nun mimarisi de benzer şekilde neredeyse yalnızca giriş olaylarını gönderiyor ve lockstep simülasyon çekirdeğine dayanıyor
    Bunun dikkat çeken istisnalarından biri demiryolu planlama aracı gibi kısımlar

    • Bir gün böyle bir lockstep mimarisiyle çalışmak isterim
      Tatmin edici ve test etmesi güzel bir tasarım kısıtı gibi görünüyor
  • Çocukken PC Gamer demosuyla Serious Sam oynadığımı hatırlıyorum
    O zaman bile eski DOOM ve Quake günlerine dönüş gibi görülen retro tarzı bir oyun sayılıyordu
    Şimdi kelimenin tam anlamıyla 20 yıl geçti ve kendisi de bir klasik oldu

  • Starsiege: Tribes, 56K bağlantıda bile büyük ölçekli ve saçma derecede eğlenceliydi

    • Tribes geliştiricileri benzer ağ kodu kavramlarını ele alan bir teknik rapor yazmıştı
      https://www.gamedevs.org/uploads/tribes-networking-model.pdf
    • Çocukken en sevdiğim oyun olabilir, özellikle Tribes 2
      Hatta kısa süre önce Tribes 2’yi indirip birkaç ay önce botlara karşı oynadım
      Eski bir oyun ama hâlâ eğlenceliydi; sık sık Unity gibi bir şeyle yeniden yapmak istediğimi düşünüyorum
      Belki bir gün yaparım
    • Tribes harikaydı
      1999’da prosedürel olarak üretilmiş arazide kayak yapar gibi aşağı kaymak, başkalarının onun üzerinden kayak yapar gibi yukarı çıktığını görmek, üstelik büyük haritalar ve çok sayıda oyuncu olması