Serious Sam, 56k modem bağlantısıyla büyük düşman kalabalıklarını nasıl işledi
(staniks.github.io)- 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
CPlayerActiongibi 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
CNetworkMessageserileş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 eylemleriMSG_SEQ_ADDPLAYER: oyuncu eklemeMSG_SEQ_REMPLAYER: oyuncu kaldırmaMSG_SEQ_PAUSE: duraklatma veya devam ettirmeMSG_SEQ_CHARACTERCHANGE: oyuncu karakteri özellik değişikliği
- Kilit unsur
MSG_SEQ_ALLACTIONS’tır; motor, etkin oyuncu başınaCPlayerActionnesnesini ters serileştiripCPlayerTarget’a uygular CPlayerActionoyuncu 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
- Dünya uzayı bazında hareket hızı
- 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ırses_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
_controlfpile_RC_NEARdurumunu 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=0ile enterpolasyon kapatılabilir
UDP üzerine kurulan özel paket katmanı
- Serious Engine çok oyunculu kodunda
StartPeerToPeer_tgibi 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
CPacketpaket sırasını ve güvenilirliği yönetirpa_ulSequence: sıralama ve yinelenenleri eleme için kullanılan sıra numarasıpa_ubReliable: güvenilirlik bayrağını içerirpa_ubRetryNumber: yeniden gönderim sayısını izlerpa_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_iMaxSendRetriesile ayarlanır ve varsayılan değerin10olduğu görülüyor - Yeniden gönderim aralığı
net_fSendRetryWaitile ayarlanır ve varsayılan değerin0.5folduğu görülüyor
- Maksimum yeniden deneme sayısı
- 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
- İlk paket
- 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
CCommunicationInterfacepaket 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
CCommunicationInterfaceiki ana tampon tutarcci_pbMasterInput: gelen UDP paketleriniCPacketolarak ters serileştirip saklarcci_pbMasterOutput: gönderilecekCPacket’ları serileştirip soket API’sine iletir
- İstemci başına gerçek iletişim soyutlamasını
CClientInterfaceüstlenir- Sunucu,
cm_aciClientsdizisiyle her oyuncu arayüzünü tutar - İstemci,
cm_ciLocalClientile sunucuyla iletişim kurar - Hem istemci hem sunucu bağlantı kurmak için
cm_ciBroadcastkullanır
- Sunucu,
CAddressiçindekiadr_uwID, istemciye özgü tanımlayıcı veya broadcast paketi işareti olarak kullanılır- Değer
'//'veya0ise broadcast paketidir - Diğer değerler oturum içindeki istemci ID’sidir
- Değer
Bağlantı kurma ve temel güvenlik önlemleri
- İstemci, sunucuya bağlanmak için
UDP_PACKET_CONNECT_REQUESTbayraklı 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_RESPONSEgü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,ExchangeBuffersile 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,WriteBitsve<<,>>operatörleriyle serileştirilir/ters serileştirilir - Alt mesajlar da içerebilir; gerekli veriler yazıldıktan sonra
Shrinkile tampon boyutu veri boyutuna uydurulabilir CNetworkMessagetamponuAllocMemoryile ayrılır ve içeridemallocçağırdığı görülüyorCLinearAllocatormevcut 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
MESSAGETYPEiç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
- LZ77
- Varsayılan sıkıştırmanın LZRW1 olduğu görülüyor;
net_iCompressionkabuk değişkeniyle değiştirilebilir CPlayerActionolduğ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_ALLACTIONSile topluca gönderdiğinde bu yöntemin etkisi daha da artabilir
Mesaj şifreleme ve sohbet
- Serious Engine mesajları şifrelenmez
net_iCompression=0ile 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 durumuCSessionStatedâhil oyun oturumunu yönetirCNetworkLibrary, daha önce bahsedilenCMessageDispatcher’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
CSessionStateoluşturur, serileştirir ve temel durumga_pubDefaultStateolarak 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_ulCRCiç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_CONNECTREMOTESESSIONSTATEile derleme sürümü, mod adı, sunucu parolası, yerel oyuncu sayısı veCSessionSocketParamsgönderilirMSG_REP_CONNECTREMOTESESSIONSTATEile mesaj, dünya dosya adı, zorluk/oyun modu bayrakları ve oturum özellikleri alınır- Referans oyun durumu başlatılır
MSG_REQ_STATEDELTAgönderilerek sunucunun mevcut durumuyla fark istenirMSG_REP_STATEDELTAalındıktan sonra ters diff ile oyun durumu akışı yeniden oluşturulurCSessionState::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
- Güvenilir olmayan:
MSG_GAMESTREAMBLOCKSgü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_REQUESTGAMESTREAMRESENDile 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 ileCPlayerCharactertanımlayıcısını içerirMSG_SEQ_REMPLAYER, oyuncu bağlantısı kesildiğinde gönderilir ve yalnızca oyuncu indeksini taşırMSG_SEQ_CHARACTERCHANGE, oyuncu adı, takım ve görünüm değişikliklerini iletir- Serious Sam’de görünüm tamponu
CPlayerSettingsyapı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
- Serious Sam’de görünüm tamponu
MSG_SEQ_PAUSE, duraklatma veya devam ettirmeyi iletir ve istekte bulunan oyuncu adını konsola yazdırırMSG_SEQ_ALLACTIONS, mevcut tik zamanını ve tüm oyuncu eylemlerini içerir- Her
CPlayerTarget’aCPlayerActionuygular - Ardından zamanlayıcılar, olaylar, hareketli entity’ler ve fizik işlemleri yapılır
- Her
- Senkronizasyon kontrolü
MakeSynchronisationCheck()ile yapılır- Entity’ler ve oyuncu hedefleri gibi öğelerin
ChecksumForSync()çıktılarıylaCSyncCheckoluşturulur - İstemci
MSG_SYNCCHECKmesajını sunucuya gönderir; sunucu durumuyla uyuşmazsa bağlantı kesilir
- Entity’ler ve oyuncu hedefleri gibi öğelerin
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_abPredictioniçinde saklanan sunucuya gönderilmiş eylem sayısı kadar tahmin yapabilir - Uzak oyuncu tahmininde
cli_bLerpActionskapalıysa son alınan eylem tekrarlanır cli_bLerpActionsaçı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
CPlayerActionbenzeri 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
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
O sesi hâlâ duyuyorum
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
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ı
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ü
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ı
İş 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
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
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-...
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
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
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
İleri gitmekten çok geriye doğru hareket ederek daha fazla zaman geçirilen bir oyundu bence
Steam’de var ve aynı yapımcı mı bilmiyorum ama Serious Sam evreninde geçiyor
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
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
https://www.gamedevs.org/uploads/tribes-networking-model.pdf
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
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ı