Prediction

Prediction (tahmin), çok oyunculu oyunlarda oyuncunun istemci ile sunucu arasındaki gecikmeyi (lag) hissetmemesini sağlamak için yaygın olarak kullanılan bir tekniktir.

Oyuncunun eylemlerini toplayarak, bu eylemleri sunucuya göndererek ve sunucu kendi işlemlerini yaparken istemcide ne olması gerektiğini simüle ederek çalışır.

Sunucu daha sonra istemciye ne olduğunu geri döndürür. Eğer istemcinin tahmini doğruysa, oyuncunun tahmin edilen durumu sunucunun döndürdüğü durumla eşleşmeli ve bu da çok akıcı bir oynanışla sonuçlanmalıdır.

Neden prediction'a ihtiyacımız var?

Prediction'a neden ihtiyacınız olduğunu açıklamaktan çok görmek daha kolaydır. Çok oyunculu/LAN bir oyunda, hileleri etkinleştirin, prediction'ı devre dışı bırakın ve ardından sahte gecikmeyi (fake lag) etkinleştirin:

sv_cheats 1 sv_client_predict 0 net_fakelag 500

Bir kaya gibi etrafta kayacaksınız ve kontrol etmek çok zorlaşacak. sv_client_predict 1 ile prediction'ı tekrar açtığınızda oyun aniden yeniden akıcı hale gelir.

Prediction olmadan, oyuncunun gerçekleştirdiği her eylemin önce sunucu tarafından onaylanması ve ardından istemciye (yani size, oyuncuya) geri gönderilmesi gerekir. Bu, bir tuşa bastığınız an ile eylemin ekranda gösterildiği an arasında bir girdi gecikmesine (input lag) neden olur.

Prediction ile, sunucudan bir yanıt beklemeden ek eylemler gerçekleştirirken sunucu oyuncunun eylemlerini doğrular.

Nasıl uygulanır?

Her tick'te, oyuncunun hangi tuşlara bastığı gibi bilgileri içeren bir CUserCmd oluşturulur. İstemci bu komutu (ve henüz gönderilmemiş olan daha önce oluşturulmuş komutları) sunucuya gönderir.

Sunucudan bir yanıt beklerken, istemci oyuncu hareketini simüle etmeye başlar.

Son olarak sunucu, alınan tüm komutları yeniden oynattıktan ve oyuncuyu sunucuda simüle ettikten sonra oyuncuyu soktuğu durumu geri gönderir.

Süreç yeniden başlar ve istemci daha fazla kullanıcı komutu gönderir.

Garry's Mod'da

CurTime'dan dönen sayı, oyuncunun ping'ine bağlı olarak her zaman sunucuyla senkronizedir; genel olarak bunu prediction'da gecikmeye dayalı her şey için kullanmalısınız. Tahmin edilmemiş (unpredicted) bir versiyon için UnPredictedCurTime'a bakın.

Tek oyunculu modda, istemci prediction simülasyonunu atlar çünkü istemci aynı zamanda sunucudur, bu nedenle yanıt süresi neredeyse sıfırdır (ancak bir listen sunucusu barındırılırken prediction yine de uygulanır).

Bu, GM:Move ve WEAPON:PrimaryAttack gibi hook'ların tek oyunculu modda istemci tarafında (clientside) ÇAĞRILMADIĞI anlamına gelir, çünkü buna gerek yoktur.

Bu kuralın tek istisnası WEAPON:Think'tir, çünkü Garry tek oyunculu modda bile istemci tarafında çağrılmasını sağlayan bir 'hack' eklemiştir.

Şimdi bunu bana herkesin anlayacağı dilde açıkla

Temel olarak, silahınızın veya hareket sisteminizin yüksek ping'li oyuncular için veya bir bağlantı sorunu olduğunda bozulmasını istemiyorsanız, prediction desteğiniz olsa iyi olur. Source motorunda, bozuk prediction genellikle titrek bir oyuncu görünümüne ve etrafta ışınlanmaya neden olur.

Prediction ne kadar güçlü?

Prediction her şeyi çözmez, hala internet bağlantıları iyileştikçe düzeltilebilecek ağır sınırlamalara sahiptir. Bu sınırlamaları maskeleyerek akıcı bir deneyim sağlamaya yardımcı olmak için vardır.

Örneğin, 300 ping'iniz olduğunu ve ölümsüzlük kazanmak için fareyi basılı tutmanızı gerektiren bir kalkan silahınız olduğunu varsayalım.

Prediction doğru bir şekilde uygulandığında, tıkladığınızda etkileri hemen göreceksiniz, ancak sunucuda paketler hala ulaşmakta olduğu için silahınız henüz hiçbir şey yapmamış olacak.

Eğer bir oyuncu o zaman diliminde size ateş ederse, öleceksiniz ve sunucu sizin "ama kalkanım zaten açıktı" şikayetinizi dinlemeyecektir. Kalkan sadece sizin tarafınızda açılmıştı ve ne sunucu ne de diğer oyuncular bunu henüz görmemişti.

Diğer oyun motorları bunu farklı şekilde ele alabilir ve böyle bir durumda kimi dinleyeceğini belirlemek -kalkanlı oyuncuyu mu, yoksa saldıran oyuncuyu mu- zor bir oyun tasarımı sorusudur.

Ancak, çoğu Source motoru oyununda cevap oldukça basittir: oyuncunun belirli bir tick'te yaptığı herhangi bir eylem, sunucuya geçerli göründüğü sürece kabul edilir.

Sıkça Sorulan Sorular

Bir oyuncuya ateş ettiğimde, bu prediction mıdır?

Hayır, bu gecikme telafisi (lag compensation) ve interpolasyondur. Prediction ile birlikte, özellikle silahlar için kullanılır.

İstemci tarafında tahmin edilen (clientside predicted) hook'lar neden sunucu tarafındakilerden daha fazla çağrılır?

Çünkü deneyimi akıcı hale getirmek için, istemci oyuncuyu farklı aralıklarda birden çok kez simüle eder ve sonuçları interpole eder.

Ancak istemci tüm sonuçları göndermez. Ne kadarının gönderileceği, istemcinin güncelleme hızına (update rate) bağlıdır. Genellikle sadece ilk kullanıcı komutu gönderilirken, diğerleri yalnızca akıcılığı sağlamak için kullanılır.

İstemci ayrıca bir hata oluştuğunu düşünürse oyuncuyu farklı değerlerle yeniden simüle etmeye çalışabilir. Bu, örneğin fizik nesneleriyle (physics props) etkileşime girerken sıkça olur.

Global.IsFirstTimePredicted nedir?

IsFirstTimePredicted, bu prediction'dan gelen komutun sunucuya gönderilip gönderilmeyeceğini döndürür. Eğer false dönerse, bu, yukarıda açıklandığı gibi, simülasyonu akıcılaştırmak için kullanılan bir komut olduğu anlamına gelir.

Bu, sunucu tarafında (server-side) her zaman true döndürür, çünkü sunucunun doğal olarak akıcılık sağlamasına gerek yoktur.

Tahmin edilen (predicted) değişkenler, fonksiyonun true veya false dönmesine bakılmaksızın ayarlanmalıdır.

Bu fonksiyon zaten SendWeaponAnim ve EmitSound için dahili olarak kullanılmaktadır, bu yüzden bunu yalnızca bunun gibi efekt gönderimlerini sarmalamak için kullanmalısınız.

function SWEP:PrimaryAttack() self:EmitSound( "Weapon_Pistol.Single" ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextPrimaryFire( CurTime() + 0.5 ) if ( IsFirstTimePredicted() ) then local effect = EffectData() effect:SetOrigin( self:GetOwner():GetEyeTrace().HitPos ) util.Effect( "Explosion" , effect ) end end

Fonksiyonu bu şekilde KULLANMAYIN.

-- BU YAPILMAMASI GEREKEN BİR ÖRNEKTİR function SWEP:PrimaryAttack() if ( not IsFirstTimePredicted() ) then return end self:EmitSound( "Weapon_Pistol.Single" ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextPrimaryFire( CurTime() + 0.5 ) local effect = EffectData() effect:SetOrigin( self:GetOwner():GetEyeTrace().HitPos ) util.Effect( "Explosion" , effect ) end

Bu, yalnızca silahtaki prediction'ı bozar.

IsFirstTimePredicted()'nin tüm hook'larda, özellikle GM:SetupMove gibi prediction ile ilgili olanlarda çalışmadığını unutmayın. Bunun yerine, CUserCmd:TickCount değerini kontrol edin—eğer 0 ise, bu eylemin tahmin edildiği anlamına gelir.

Silahım ateş ederken veya kullanıcıda lag olduğunda neden bozuluyor!?

Büyük ihtimalle tahmin edilmesi gereken (predicted) bir şey için zamanlayıcılar (timers) ve/veya ağa gönderilmeyen (non-networked) değişkenler kullanıyorsunuz.

Gördüğünüz gibi, Entity:NetworkVar ile herhangi bir prediction hatası olduğunda değişkenler geri yüklenecektir.

self.NextFire gibi değişkenler kullanırsanız bu OLMAZ, ki bu değişkenler büyük olasılıkla prediction hatalarınızın nedenidir.

Seri atışım (burst fire) için hala timer.Simple kullanabilir miyim?

Hayır, kesinlikle kullanmamalısınız, bunun yerine mantığınızı silahın Think hook'u içinde yapın. Zamanlayıcılar (timers) prediction sırasında hiç güvenilir değildir ve bir prediction hatası sırasında sunucu size neyin yanlış olduğunu söylediğinde geri yüklenmezler.

Ayrıca gecikme telafisi (lag compensation) bir zamanlayıcı içinde çalışmaz, bu yüzden bunu yapmamanız için bir neden daha var.

Örneğin, bir bekleme animasyonu (idle animation) için bir zamanlayıcıyı nasıl değiştireceğiniz aşağıda gösterilmiştir.

function SWEP:SetupDataTables() self:NetworkVar( "Float" , 0 , "NextIdle" ) end function SWEP:Initialize() self:SetNextIdle( 0 ) end function SWEP:PrimaryAttack() self:SetNextPrimaryFire( CurTime() + 1 ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextIdle( CurTime() + self:SequenceDuration() ) end function SWEP:Think() if self:GetNextIdle() ~= 0 and self:GetNextIdle() < CurTime() then self:SendWeaponAnim( ACT_VM_IDLE ) self:SetNextIdle( 0 ) end end

Bu aptalca, gereksiz kod, zamanlayıcılar sadece 1 satır kod gibi!

Zamanlayıcılar zaten dahili olarak bu şekilde çalışır, tek fark bu durumda prediction desteğine sahip olmalarıdır.

Bir think hook'unda bu şekilde birden fazla kontrol yapmak maliyetli değil mi?

Hayır, iki float'ı karşılaştırmak, bir HUDDraw hook'unda bir izleme (trace) ateşlemekle karşılaştırıldığında hiçbir şeydir.

Global.SuppressHostEvents nedir?

Bu fonksiyon, oyuncunun prediction sırasında yinelenen efektler almasını engeller.

Bir silah saldırısı yaptığınızda hem istemcide hem de sunucuda bir efekt yayarsınız (örneğin yukarıdaki örnekteki patlama).

Diğer oyuncular için bunlar yalnızca bir kez gönderilecektir, ancak tahmin eden (predicting) oyuncu bunları kendi istemcisinde de yaydığı için, sunucu o saldırı sırasında oyuncunun kendisine göndermek üzere olduğu her şeyi atarak bunun olmasını engeller.

Ancak bu, util.Effect'in CRecipientFilter özelliği kullanılarak, oyuncuları PVS'ye göre ekleyip sunucuda sadece silahın sahibini çıkararak da yapılabilir.

SuppressHostEvents zaten WEAPON:PrimaryAttack ve WEAPON:Think gibi tahmin edilen (predicted) silah çağrılarını sarmalamaktadır ve bunu kullanmanıza gerek yoktur.

Prediction ile ne sunucu tarafında (serverside) çağrılmalı, ne çağrılmamalıdır?

Bu çoğunlukla ne yaptığınıza bağlıdır.

  • Bir entity oluşturuyorsanız, bu açıkça yalnızca sunucu tarafında (serverside) olmalıdır.
  • Kapılar ve diğer oyuncular gibi diğer entity'leri etkiliyorsanız, bu da sunucu tarafında olmalıdır.
  • Entity:SetPredictable'ın etkin olduğu bir entity'yi etkiliyorsanız (ki bu silahlar ve Garry'nin yaptığı sürüş sisteminde kullanılan entity'ler için normal bir durumdur), örneğin dahili veya özel DT değişkenlerini değiştiriyorsanız, bu paylaşılmalıdır (shared).

Faydalı komutlar

cl_showerror (hile gerektirmez, 0, 1, 2 değerlerini alır)

Bir entity'nin neden olduğu prediction hatalarını gösterir. 1'e ayarlamak, mesajları oyuncunun HUD'unun sağ üst köşesinde (eski Lua hataları HUD'unun olduğu yerde) gösterir. Genellikle istediğiniz değer olan 2'ye ayarlamak, hatanın, etkilenen değişkenin ve sunucudan olan farkın yazdırılmasını sağlar.

cl_predictionlist (hile gerektirir, 0 veya 1 değerlerini alır)

İstemcide o anda tahmin edilen (predicted) tüm entity'leri, entity indeksini, sınıf adını (classname) ve kökenini (tahmin edilen veya istemci tarafından oluşturulan) gösterir. Ancak tek başına pek kullanışlı değildir.

cl_pdump (hile gerektirir, bir entity indeks numarası alır)

O anda istemciye ağ üzerinden gönderilen (networked) tüm datatable değişkenlerini gösterir, sağ altta bir açıklama bulunur.

Prediction örnekleri

Genel örnekler için Prediction Örnekleri sayfasına bakın.

Prediction'dan yoğun bir şekilde yararlanan ancak ondan ayrı olan hareketle ilgili konular için Oyun Hareketi (Game Movement) sayfasını okuyun.

Tüm sunucular · GmodTR Launcher · Rehber · GMod Wiki · GmodTR nedir? · Sunucu ekle · Discord