YuDe - Authoritative(Yetkili) Multiplayer

GAME, SOFTWARE, NETWORKING AND MORE...

Contact us for cooperation

 

Authoritative(Yetkili/Güvenli) Multiplayer

Merhabalar. Multiplayer oyun sistemleri konusunda türkçe kaynak kıtlığı çektiğimiz bir gerçek. Kaynak kıtlığını az da olsa azaltmak için bu yazıyı yazıyorum. Şunu baştan belirtmeliyim ki bu yazı Multiplayer dünyasına yeni giriş yapanlara yönelik değildir. Client-Server, Master Server, Latency/Lag terimlerinin teknik anlamlarını biliyorsanız yazıyı okumaya devam edebilirsiniz.

Genel olarak Client-Server mimarisinden, Pozisyon interpolasyonundan, Lag ile savaşmaktan, Client tabanlı olay tahmini ve yapılan tahminlerin server tarafından teyitlendirilmesiden bahsedeğiz. Ve ayrıca yazı sadece unity ya da başka bir oyun motoru için değil, genel yazılmıştır. İngilizce bir kaynağı türkçe'ye çevirip, gereksiz bazı kısımları kesip kendi yorumlarımı da katarak bu yazıyı yazıyorum.

Faydalandığım kaynak:http://www.gabrielgambetta.com/client-server-game-architecture.html


 

Client-Server Mimarisi

Her harikülade de oyun geliştirmek bizi zorluyor. Çok oyunculu(multiplayer) oyunları ele alırsak tamamen yeni bir takım problemler ortaya çıkmaktadır. İlginçtir ki temel problemler insan doğası ve fiziksel konumdur!

 

Hile Problemi

Her şey hile ile başlar.

Bir oyun geliştiricisi olarak, genellikle bir oyuncunun Single Player bir oyunda hile yapıp yapmadığını pek önemsemezsiniz. Çünkü oyuncunun yaptığı eylemler diğer oyuncuları etkilemez.

Çok oyunculu Multiplayer bir oyunda; hile yapan oyuncu sadece kendi deneyimlerini daha iyi hale getirmez, aynı zamanda diğer oyuncuların deneyimini kötüleştirir. Bir oyun geliştiricisi olarak muhtemelen bunu engellemek istersiniz çünkü bu diğer oyuncularınızın oyununuzdan uzaklaşmalarına neden olacaktır.

Hileleri önlemek için yapabilecek pek çok şey vardır. Burda en önemli olan şey: Oyuncuya güvenmeyin. Her zaman en kötü durumu düşünün - oyuncularınız hile yapmaya çalışacaktır!

 

Yetkili Servarlar ve *Sersem Clientlar

Görünüşte basit bir çözüm: Oyununuzdaki her şeyi kontrolünüz altında sunucuda gerçekleştirin ve clientları sadece ayrıcalıklı seyirciler yapın.

Clientlar sunucuya girdiler(tuş komutları, joystick değerleri vs) gönderir ve sunucu clientlardan aldığı bu girdilere göre clienları yönlendirip sonuçları clientlara geri gönderir. Bu sisteme genel olarak authoritative server(Yetkili Sunucu) denir.

Sunucunuzda güvenlik açığı olabilir tabi; bu probleme yazıda değilmemektedir. Yetkili bir sunucu kullanmak geniş bir yelpazede hileleri önler. Örneğin client'ınızın sağlık değerine(can değerine) güvenmiyorsunuz; client hile yapıp local'da(client ın cihazı) tutulan can değerini düzenleyebilir ve %10000 gibi uçuk bir değer yapabilir. Ancak sunucu o client ın o an sağlığının %10 olduğunu bilir. Olaylar sunucuda gerçekleşeceği için o oyuncu o an başka bir oyuncu tarafından saldırıya uğrarsa %10 olan can'ı %0'a düşecek ve oyuncunun local deki sağlık değerlerine bakmaksızın oyuncu ölecektir.

Ayrıca oyuncunun oyundaki pozisyonun koordinatılarına güvenmeyebilirsiniz; oyuncu sunucuya önce "Ben (10,10) konumundayım" diyecek, yarım saniye sonra "Ben (20,10) konumundayım" diyecek bu sefer; muhtemelen oyuncunuz bir duvardan geçecek ya da diğer oyunculardan çok daha hızlı hareket edecektir çünkü oyuncu hile yaptı! Bunun için şöyle bir çözüm yoluna başvurabilirsiniz; Sunucu zaten başlangıçta oyuncunun (10,10) pozisyonunda olduğunu biliyor. Client hareket etmek istediği zaman sunucuya yeni konumunu göndermek yerine "Ben bir birim sağ'a taşınmak istiyorum" komutunu  gönderecek, sunucu gelen komutu işleyecek ve oyuncuyu bir birim sağ'a taşıyacak, ardından güncellenen yeni pozisyonunu  "Yeni konumun (11,10)" mesajıyla client'a gönderecek. Mesajı alan client kendi konumunu mesajdaki konum ile güncelleyecek. Artık client pozisyon değiştirme konusunda hile yapamayacak!

Basit bir Client-Server etkileşimi.

Kısaca oyunun durumu sadece server tarafından yönetiliyor. Clientlar eylemlerini sunucuya gönderir, sunucu periyodik olarak oyun durumunu günceller ve sonra yeni oyun durumunu  tekrar client'lara gönderir. 

 

Ağlar ile Uğraşmak

Clientlar sıra tabanlı oyunlarda(satranç, puzzle, kart oyunlar vs) problemsizdir. Ayrıca LAN ortamında iletişim pratik ve hızlı olduğunu için burada da problemsizdir. Ancak internet gibi bir ağ üzerinden hızlı tempolu bir  oyun için kullanıldığında client lar problemler çıkartmaktadır.

Fiziksel konum hakkında biraz konuşalım. San Francisco da olduğunuzu farzedelim, New York'taki bir sunucuya bağlısınız. Sunucu bizden yaklaşık 4000 km uzaktadır. Hiçbir şey ışıktan daha hızlı hareket edemez, internetteki baytlar bile. Işığın yaklaşık 300.000 km/s hızda seyrettiğini hesaba katarsak 4 ms'lik(milli saniye) yolculuk yapmak 13 ms sürüyor.

Gerçek hayatta veri, yönlendiriciden yönlendiriciye bir dizi atlayışından(Ağ terminolojisinde atlamalar) geçer. Bunların çoğu ışık hızında yapılamaz. Yönlendiriciler, paketlerin denetlenmesi ve yeniden yönlendirimesi gerektiğinden biraz gecikmeye neden olur.

Bu bağlamda, verilerin client'tan server'a ulaşmasını 50 ms olarak var sayalım. Bu en iyi durum senaryosuna yakındır. Eğer New York'taysanız ve Tokyo daki bir sunucuya bağlıysanız ne olur? Ağ tıkanıklığı olursa ne olur? 100,200, hatta 500 ms'lik gecikmeler meydana gelebilir.

Örneğe geri dönelim. Client, server a "Ben sağ yön(ok) tuşuna bastım" girdisini gönderdi. Sunucuya bu girdi 50 ms sonra ulaştı. Sunucu aldığı bu isteği işleme koydu ve güncellenen durumu client'a geri gönderdi. Client 50 ms sonra "Yeni konumun (11,10)" mesajını alıyor.

Client bakış açısından; sağ yön(ok) tuşuna basıyor ama bastıktan 50 ms sonraya kadar hiç bir değişiklik olmadığını fark ediyor client. Tam 50 ms sonunda karakteri bir birim sağ a taşınacaktır. Girdileriniz ve sonuçlarınız arasında yaşanan bu gecikme çok fazla gelmeyebilir size ama dikkat çekicidir. Eğer 50 ms değil de 500 ms yani yarım saniyelik bir gecikme olursa sadece fark edilmez ayrıca oyunu oynanılmaz hale getirir. Hadi bunun için çözüm üretelim!

Client Tabanlı Tahmin ve Server Uzlaştırma

İnternet gibi bir ağ ortamında gecikmelerin saniyenin onda biri olduğu bir oyunda (bile),  gecikmeler hissedilebilir. Bu problem oyunu oynanmaz hale getirebilir.

Client Tabanlı Tahmin

Bazen hileci oyuncularınız olsa da neticede sunucunuz çoğu zaman geçerli istekleri işler. Bu, sunucunun aldığı girdileri onaylayıp oyunun durumunu beklendiği gibi güncelleneceği anlamına gelir. Yani eğer karakteriniz (10,10) pozisyonundaysa ve sağ ok tuşuna basılırsa yeni pozisyon (11,10) olacaktır.

Eğer oyun dünyası yeterince deterministikse, bunu avantaj olarak kullanabiliriz.(Deterministikten kasıt; oyunun durumu ve gönderilen girdilerin tahmin edilebilmesi).

100 ms Gecikme olduğunu ve bir pozisyondan diğer bir pozisyona hareket eden karakterin animasyonunun 100 ms sürdüğünü varsayalım. Bunu çok basit bir oyun motorunda(ya da herhangi bir geliştirme ortamında) kullanırsak(sistemi) tüm eylem 200 ms sürer:

Ağ gecikmesi + animasyon

Oyun dünyamız deterministik olduğundan, sunucuya gönderdiğimiz girdilerin başarılı bir şekilde gerçekleşeceğini varsayabiliriz. Bu bağlamda; client, girdilerini işledikten sonra(gönderdikten sonra) oyunun durumunu tahmin edebilir(Burada oyunun durumundan kasıt, local client ın davranışlarıdır). Ve bu tahminler çoğu zaman doğru olacaktır.

Girişleri göndermek ve yeni oyun durumunu serverdan beklemek yerine; girişleri gönderebilir ve aynı zamanda bu girdilerin sonucunu başarıya ulaşmış gibi gösterebiliriz serverdan "doğru" sonuçları beklerken(başarıya ulaşmış gibi göstermekten kasıt; sunucuya gönderdiğimiz girdiyi oluduğu gibi local de karaktere uygulamaktır. Ve bu yapılırken serverdan bir sonuç beklenmiyor. Sonuç serverdan geldiği zaman, gelen girdi tekrar karaktere uygulanacak). 

Server dan bir sonuç beklerken animasyon oynatılır

Artık oyuncunun eylemleri(girdileri) ile ekrandaki sonuçlar arasında hiç bir gecikme yok ve üstelik sunucumuz hala yetkili! Hile yapan bir oyuncu hükümsüz girdiler uygulayıp karakterinin pozisyonunu değiştirebilir, kendi ekranında(cihazında) istediğini o an oluşturabilir ama sunucu bu durumdan etkilenmez(Yukarıda yazıldığı gibi, biz sunucuya koordinat göndermiyoruz, "sağ ok(yön) tuşuna bastım" gibi mesajlar gönderiyoruz). Tabi diğer oyuncular da, bu hile yapan oyuncunun koordinatlarını serverdan aldığı için, hile bu oyunculara da işlemeyecektir. Bu arada Hile yapan oyuncu çok geçmeden serverdan sonuç alacak ve olması gereken pozisyona gerı ışınlanacak :)

Senkronizasyon Sorunları

Yukarıdaki örnekte, her şeyin iyi çalışmasını sağlamak için sayıları dikkatlice seçtim. Biraz değiştirilmiş bir senaryo düşünün: 250 ms gecikmemizin var olduğunu ve bir pozisyondan diğer pozisyona geçmenin 100 ms sürdüğünü var sayalım. Ayrıca oyuncunun üst üste 2 kez sağ tuşuna bastığını ve karakterini iki birim sağa hareket ettirdiğini varsayalım.

Şimdiye kadarki tekniklerimizi uygularsak:

Tahmin edilen durum ve yetkili(server) durum uyuşmazlığı

t = 250 ms Anında ilginç bir problem ile karşılaşıyoruz. Client ta tahmin edilen durum x = 12 dir ama serverdan gelen yanıt, client ın x = 11 pozisyonunda olduğunu söylemesi. Sunucu yetkili olduğundan dolayı, client ı x = 11 e taşıması gerekir. Ama çok kısa bir süre sonra t = 350 ms anında server, client ın  x = 12 pozisyonunda olduğunu söyler. Bu sefer karakter bir birim ileri atlayacak(bir öncekinde bir birim geriye atmıştı yanıt geç geldiğinden dolayı, bir diğer yanıtta t=350ms anında geldiği için karakteri bir birim ileri taşıyor )

Oyuncu bakış açısından: sağ ok tuşuna iki defa bastı; karakter iki birim sağa kaydı(buraya kadar henüz serverdan yanıt gelmemiş). Orda 50 ms durdu ve bir kare geriye(sola) atlayacal, oyuncu 100 ms daha hiç bir şey yapmadan bekliyor, hemen sonra bir birim sağa kayacak. Bu elbetteki kabul edilemez! 

Server'ı Client ile Uzlaştırma

Bu sorunu çözmenin anahtarı; client'ın, oyun dünyasını şimdiki zamanda görmesini sağlamaktır. Ama gecikme(lag) nedeniyle, sunucudan aldığı sonuçlar aslında geçiş oyunun durumudur(sonuçlarıdır). Sunucu güncellenen oyun durumunu gönderdiği zaman, client tarafından gönderilen tüm komutları işlememiştir(gecikmeden dolayı).

Yinede bunu çözmek çok zor değil. İlk olarak; client, gönderdiği her isteğe/mesaja bir sıra numarası ekler. Örneğimizde; ilk tuşa basma isteğinin sıra numarası #1 ve ikinci tuşa basma isteğinin sıra numarası ise #2 dir. Ardından sunucu yanıt verdiğinde; her verdiği yanıta, client'ın gönderdiği sıra numarasını ekler:

Client tabanlı tahmin + Server uzlaştırma

t = 250 anında client "#1 numaralı isteğine göre pozisyonun x = 11" mesajını alır sunucudan. Sunucu yetkili olduğundan karakterin pozisyonunu x = 11 e ayarlar. Şimdi; client'ın sunucuya gönderdiği isteklerin bir kopyasını saklı tuttuğunu varsayalım. Yeni oyun durumunda; sunucunun zaten #1 numaralı isteği işleme koyduğunu biliyor, bu neden bu kopyayı silebilir. Aynı zamanda sunucuya gönderdiği #2 numaralı isteğin sunucu tarafından işleyip geri dönüş yapılması gerektiğini de biliyor. Client tabanlı tahmini bu durumda da uygulayarak, sunucu tarafından gönderilen son isteği(mesajı) işleyerek oyunun mevcut/gerçek durumunu ve sunucunun henüz işleme koymadığı girdileri hesaplayabilir.

Yani; t = 250 anında client "x = 11, son işlenen istek = #1" mesajını alır. #1'e kadar olan girişlerin kopyasını siler. Ancak sunucu tarafından henüz onaylanmayan #2 numaralı kopyayı saklar. Sunucunun gönderdiği veri ile oyunun durumunu günceller ve daha sunucu tarafından görülmeyen tüm girdileri uygular. Bu durumda; #2 girdisi "sağ a bir birim kay" olduğu için client x= 12 ye hareket edecek.

Örneğe devam edersek;  t = 350 anında sunucudan yeni bir oyun durumu gelir. bu seferki mesaj: "x = 12, son işlenen istek = #2". Bu noktada client, #2'ye kadar olan girişlerin kopyalarını siler ve oyun durumu x = 12 ye göre güncellenir. Yeniden oynatılacak işlenmemiş giriş yoktur bu nedenle işlem burada doğru sonuçla biter.

Ekleme: Peki bu durumda oyuncu, pozisyonunu hile ile değiştirirse buna nasıl engel olacağız? Cevap basit aslında; bütün hareket eylemlerimiz numaralandırılıp servera gönderiliyor. Bu numaraların kopyaları bizde duruyor ta ki sunucudan aynı numaranın teyidini aldığımız zaman. Mesela atıyorum #5 numaralı bir sonuç geldi bize server tarafından, bu #5 numara ve altı numaraların kopyalarını hafızadan silmeden önce, bizdeki kopyalardaki #5 numaralı istek ile serverdan gelen #5 numaralı isteği karşılaştıralım. Numaraları kayıt ederken yanlarında bide o anki karakter pozisyonunu kayıt ettirelim(bu pozisyon verisi local de tutulacak, servera gönderilmeyecek). Karşılaştırmayı yaparken; kopyalarımızdaki  #5 numaralı pozisyon ile serverdan gelen  #5 numaralı istekteki pozisyonun farkını alın. Sonucun ya 0 ya da çok düşük bir bir değere eşit olması gerekiyor. Eğer fark fazlaysa bilin ki oyuncunuz hile yapmıştır. karakterin pozisyonunu serverdan gelen isteğin pozisyonuna eşitleyip onu geri ışınlayabilirsiniz doğru konuma.

Varlık İnterpolasyonu (Ara Değer Tespiti)

Server Zaman Adımı

Şimdiye kadar bahsettiğimiz sunucunun davranışı oldukça basitti – Sunucu client girdilerini okudu, oyun durumunu güncelledi ve client’a geri gönderdi. Birden fazla oyuncu bağlandığında, ana sunucu döngüsü biraz daha farklıdır.

Bu senaryoda, birkaç oyuncu aynı anda ve hızlı bir şekilde veri gönderiyor olabilir (oyuncunun ok tuşlarına basarak, fareyi hareket ettirerek komut verebildiği kadar hızlı). Oyun dünyasını, her bir oyuncudan veri geldiğinde güncellemek ve daha sonra oyunun durumunu yayınlamak çok fazla CPU ve bant genişliği tüketecektir.

Oyuncu verilerini, geldiklerinde işlemeden sıraya koymak daha iyi bir yöntemdir. Oyun dünyası düşük sıklıkta düzenli olarak, örneğin saniyede 10 defa, güncellenir. Her güncelleme arasındaki gecikme(bu örnekte 100ms) zaman adımı olarak adlandırılır. Her güncelleme döngüsü yinelemesinde, tüm işlenmemiş client verileri uygulanır (oyun fiziğini daha tahmin edilebilir hale getirmek için muhtemelen zaman adımından daha küçük zaman aralıklarında) ve yeni oyun durumu oyunculara gönderilir.

Özetle; oyun dünyası, oyuncu verisi sayısı ve varlığından bağımsız olarak tahmin edilebilir bir oranda güncellenir.

Düşük Frekanslı Güncellemelerin Üstesinden Gelmek

Oyuncunun (client ın) bakış açısından; bu yöntem önceki kadar pürüzsüz çalışır – client tabanlı tahmini, güncelleme gecikmesinden bağımsız olarak çalışır böylece (göreceli olarak daha seyrek olsa da) tahmin edilebilir durum güncellemeleri altında çalışır. Bununla birlikte, oyun durumu düşük bir frekansta yayınlandığından (aynı örnekle devam edersek her 100 ms'de bir), oyuncu, oyun dünyasında hareket eden diğer varlıklar hakkında çok seyrek(aralıklılar halinde) bilgiye sahiptir.

İlk uygulama, bir durum güncellemesi aldığında/geldiğinde diğer karakterlerin konumunu günceller; bu durum çok düzensiz harekete, yani, her 100 ms'de bir yumuşak hareketler yerine farklı sıçramalara yol açar.

Client 2 tarafından Client 1 in görünümü

Geliştirmekte olduğunuz oyunun türüne bağlı olarak, bu durumla başa çıkmanın birçok yolu vardır; genel olarak, oyun varlıklarınız ne kadar öngörülebilirse(tahmin edilebilirse), her şeyin yolunda gitmesi o kadar kolay olur.

Konum Hesaplama (Başlangıca Göre)

Bir araba yarışı oyunu yaptığınızı varsayalım. Gerçekten hızlı giden bir otomobil oldukça öngörülebilirdir - örneğin, saniyede 100 metre gidiyorsa, bir saniye sonra, başladığı yerden tahminen 100 metre ileride olacaktır.

Neden “tahminen”? Bu bir saniye zarfında otomobil biraz hızlandırılmış veya yavaşlatılmış veya biraz sağa veya biraz sola döndürülmüş olabilir - buradaki anahtar kelime “biraz”dır. Bir aracın manevra kabiliyeti, oyuncunun gerçekte ne yaptığından bağımsız olarak, yüksek hızlarda, zamanın herhangi bir noktasında pozisyonunun, önceki konumuna, hızına ve yönüne büyük ölçüde bağımlıdır. Diğer bir deyişle, bir yarış arabası birden bire 180º dönüş yapamaz.

Bu durum, her 100 ms'de bir güncelleme gönderen bir sunucu ile nasıl işler? Client, her bir rakip arabanın yetklili(gerçek) hız ve yön bilgisini alır; sonraki 100 ms boyunca yeni bir bilgi almayacak, ancak yine de arabaların yarışa devam ettiğini göstermesi gerekir. Yapılacak en basit şey, aracın yönünün ve hızının 100 ms boyunca sabit kalacağını varsayıp arabanın konumunu bu parametrelerle yerel olarak hesaplamaktır. 100 ms sonra, sunucu güncellemesi geldiğinde, aracın konumu düzeltilir.

Düzeltme, birçok faktöre bağlı olarak büyük veya nispeten küçük olabilir. Oyuncu arabayı düz bir çizgide tutuyorsa ve arabanın hızını değiştirmiyorsa, tahmini konum tam olarak düzeltilmiş konum ile aynı olacaktır. Öte yandan, eğer oyuncu bir şeye çarparsa, tahmin edilen pozisyon oldukça hatalı olacaktır.

Başlangıca göre konum hesaplamanın düşük hız koşullarında uygulanabileceğini unutmayın, örneğin savaş gemileri için.  Aslında, “başlangıca göre konum hesaplama” terimi, eskiden denizde konum tayin etmek için kullanılırdı.

Varlık İnterpolasyonu

Başlangıca göre konum hesaplamanın hiç bir şekilde uygulanamayacağı bazı durumlar vardır - özellikle oyuncunun yönünün ve hızının birdenbire değişebileceği tüm senaryolarda. Örneğin, bir 3D silahla ateş etme oyununda, oyuncular genellikle koşar, durur ve köşeleri çok yüksek hızlarda dönerler, bu da başlangıca göre konum hesaplamayı esasen işe yaramaz hale kılar, çünkü pozisyonlar ve hızlar artık önceki verilere dayanarak tahmin edilemez.

Sunucu, yetkili(gerçek) verileri gönderdiğinde oyuncu konumlarını öylece güncelleyemezsiniz; her 100 ms'de kısa mesafelere ışınlanan oyuncular ortaya çıkar ve oyun oynanamaz hale gelir.

Elinizde olan her 100 ms'de bir gelen yetkili pozisyon verisidir; esas iş oyuncuya bu arada neler olduğunu göstermektir. Çözümün anahtarı, diğer oyuncuları, kullanıcının oyuncusuna göre geçmişte göstermektir.

t = 1000 anında konum verilerini aldığınızı varsayalım. t = 900 anında zaten veri almıştınız, bu yüzden oyuncunun t = 900 ve t = 1000 anlarında nerede olduğunu biliyorsunuz. Yani, t = 1000 ve t = 1100 arasında, diğer oyuncunun t = 900 ve t = 1000 arasında yaptıklarını göstereceksiniz. Bu şekilde daima oyuncunun gerçek hareket verisini (sadece 100 ms geç olarak) gösterebilirsiniz.

Client 2 nin, Client 1'i bilinen son geçmiş pozisyonuna interpolasyon ile taşıması

t = 900'den t = 1000'e interpolasyon yapmak için kullandığınız konum verileri oyuna göre değişir. İnterpolasyon genellikle gayet iyi çalışır. Şayet çalışmazsa sunucunun her güncellemede daha detaylı hareket verisi göndermesini sağlayabilirsiniz; örneğin client tarafından takip edilen bir düz segmentler(doğrusal bir çizgi gibi düşünebilirsiniz) dizisi veya her 10 ms'lik konum verilerinin interpolasyon yapıldığında daha iyi görünür (10 kat fazla veri göndermenize gerek yok – zaten küçük hareketler için deltaları(veri aralıkları) gönderdiğinizden dolayı, iletim formatı, bu özel durum için büyük ölçüde optimize edilebilir).

Bu tekniği kullanarak, her oyuncunun, oyun dünyasının biraz farklı bir versiyonunu gördüğünü unutmayın. Çünkü her oyuncu kendinin şu anki (şimdiki) halini ama diğer varlıkların ise geçmişteki halini görür. Hızlı tempolu bir oyunda bile, diğer varlıkları 100 ms'lik bir gecikme ile görmek genellikle farkedilemez.

Bu durumun istisnaları da mevcuttur; çok fazla mekansal ve zamansal kesinliğe/doğruluğa ihtiyacınız olduğunda, mesela oyuncu başka bir oyuncuya ateş ettiğinde, diğer oyuncular geçmişte görüldüğünden, 100 ms'lik bir gecikme ile nişan alırsınız. Yani hedefinizin 100 ms önce bulunduğu yere ateş ediyorsunuz!

Ekleme: Interpolasyon; basitçe iki bilenen değer arasında bir bilinmeyen değeri bulmaktır. Konu üzerinden gidersek; diyelim ki A client'ıyız düşmanımız ise B client'ı, B yi şu anda (10,10) pozisyonunda gördüğümüzü farz edelim. Sunucudan gelen yeni bir mesaj, B client ının (20,40) pozisyonunda olduğunu söylüyor(Oyun atmosferinin hızlı olduğunu ve client'ın hile yapmadığını düşünelim). B nin x pozisyonunu 10 dan 20 ye ve y pozisyonunu 10 dan 40 a ışınlamak göze çok batar, onun yerine smooth(yumuşak) bir şekilde adımlarla x'ini 10 dan 20 ye ve y'sini 10'dan 40'a yükseltmek çok daha makul. Bunu ise interpolasyon ile yapabiliriz. (10,10) pozisyonundan (20,40) pozisyonuna client'ı toplamda 10 adımda hedefine ulaştırabiliriz(Tabi adımlar arası çok az da olsa bir bekleme süresi olmalı ki oyuncu ışınlamayı fark etmesin). 

İnterpolasyonun genel formülü:

Bu formüldeki x ve y aslında tam olarak bize gerekli olan x ve y değildir! İnterpolasyon hakkında daha fazla bilgi için şuraya bakabilirsiniz.

Bu formülden yola çıkarak adımlarımızı hesaplarsak şöyle sonuçlar elde ederiz:

Şuanki pozisyon => (10,10)

1.Adım => (11,13)

2.Adım => (12,16)

3.Adım => (13,19)

4.Adım => (14,22)

5.Adım => (15,25)

6.Adım => (16,28)

7.Adım => (17,31)

8.Adım => (18,34)

9.Adım => (19,37)

10.Adım => (20,40)

Artık B clientı daha stabil ve smooth(yumuşak) hareket ediyor! Adım sayısını daha da artırarak yumuşaklığı artırabilirsiniz. Unity oyun motorunun kütüphanesine dahil edilen Lerp komutu tam da bu işlemi yani interpolasyonu yapmaktadır.

Lag İle Savaşma

Keskin nişancı tüfeğinizle hedefin başına nişan alıyorsunuz...ateş ettiniz tam onikiden...Çok güzel bir atıştı kaçırmadınız!

Ama bir dk, düşman hala hareket halinde, ne yazık ki kaçırmışsınız...

Ama bu nasıl olur? Onun başından vurdunuğunuza eminsiniz?

Bütün eylemler server da gerçekleşiyor dedik, siz ateş ederken server a şu mesajı gönderdiniz: "mevcut pozisyonumdan ve rotasyonumdan ateş etmek istiyorum". Server bu mesajı aldığında hemen gerekli hazırlıkları yapıp mevcut pozisyonunuzdan sizin yerinize ateş ediyor. Ateş ederken merminin düşmana değmediğini, boşa gittiğini görüyor. Yani aslında bir duvara sıktınız! Sizin ya da düşman client ın ağ gecikmesinden dolayı, onu yanlış yerde gördünüz.

Bu bir bakıma oyunu ışık hızının çok yavaş olduğu bir dünayada oynamak gibidir. Düşmanın geçmiş pozisyonunu hedefliyorsunuz, ateş ettiğiniz zaman düşman nişan aldığınız bölgeden çoktan gitmiştir aslında.

Neyse ki çoğu oyuncu için çoğu zaman hoş olan basit bir çözüm var:

Atış yaptığınız zaman, client bu olayı sunucuya iletir atışınızın tam zamanın ve silahın tam amacı ile beraber. İşte önemli adım; Oyuncu tüm atış bilgilerini zaman bilgisi ile aldığından dolayı, geçmişte herhangi bir anda dünyayı otoriter(yetkili) olarak yeniden yapılandırabilir. Özellikle dünyayı her hangi bir zamanda, her hangi bir müşteriye olduğu gibi tam olarak yeniden düzenleyebilir.

Diyelimki siz local de t = 5000 anında bir düşmana ateş ettiniz. Server a t = 5000 anında ateş ettiğinizi söylüyorsunuz. Server bu bilgiyi aldığında, sizin 250 ms gecikmeniz olduğunu görür. Server o an sizin için bütün oyun dünyasını 250 ms geriye götürebilir, siz hariç diğer bütün client ların pozisyonları 250 ms önceki pozisyonlarına dönerse ve server o an sizin yerinize ateş ederse, düşmanı gerçekten vurup vurmadığınızı öğrenebilir. Bu kontrolü yaptıktan hemen sonra server, zamanı tekrar eski haline yani 250 ms sonrasına geri döndürmelidir. Aksi durumda bütün oyunun akışı bozulur! 

Ve herkes mutlu! Çünkü artık clientların gecikmelerinden ötürü bir problem yaşama ihtimali çok düşük.