Yeni başlayanlar için oyunlarda ağ modeli hakkında

Yeni başlayanlar için oyunlarda ağ modeli hakkında
Son iki haftadır oyunumun çevrimiçi motoru üzerinde çalışıyorum. Bundan önce oyunlarda ağ oluşturma hakkında hiçbir şey bilmiyordum, bu yüzden tüm kavramları anlamak ve kendi ağ oluşturma motorumu yazabilmek için birçok makale okudum ve birçok deney yaptım.

Bu kılavuzda, kendi oyun motorunuzu yazmadan önce öğrenmeniz gereken çeşitli kavramların yanı sıra bunları öğrenmek için en iyi kaynakları ve makaleleri sizlerle paylaşmak istiyorum.

Genel olarak iki ana ağ mimarisi türü vardır: eşler arası ve istemci-sunucu. Eşler arası (p2p) mimaride, veriler bağlı oynatıcı çiftleri arasında aktarılırken, istemci-sunucu mimarisinde veriler yalnızca oynatıcılar ve sunucu arasında aktarılır.

Bazı oyunlarda eşler arası mimari hâlâ kullanılsa da istemci-sunucu standarttır: Uygulanması daha kolaydır, daha küçük kanal genişliği gerektirir ve hileye karşı korunmayı kolaylaştırır. Bu nedenle bu derste istemci-sunucu mimarisine odaklanacağız.

Özellikle otoriter sunucularla daha çok ilgileniyoruz: bu tür sistemlerde sunucu her zaman haklıdır. Örneğin, bir oyuncu kendisinin (10, 5) koordinatlarında olduğunu düşünüyorsa ve sunucu ona (5, 3) konumunda olduğunu söylerse, o zaman istemci kendi konumunu sunucu tarafından bildirilen konumla değiştirmelidir; tam tersi. Yetkili sunucuların kullanılması hilecilerin tespit edilmesini kolaylaştırır.

Ağ oyun sistemlerinin üç ana bileşeni vardır:

  • Aktarım protokolü: Verilerin istemciler ve sunucu arasında nasıl aktarıldığı.
  • Uygulama protokolü: İstemcilerden sunucuya ve sunucudan istemcilere neyin ve hangi formatta aktarıldığı.
  • Uygulama mantığı: aktarılan verilerin istemcilerin ve sunucunun durumunu güncellemek için nasıl kullanıldığı.

Her bir parçanın rolünü ve onlarla ilişkili zorlukları anlamak çok önemlidir.

Taşıma protokolü

İlk adım, sunucu ve istemciler arasında veri aktarımı için bir protokol seçmektir. Bunun için iki İnternet protokolü vardır: TCP и UDP. Ancak bunlardan birine dayalı olarak kendi aktarım protokolünüzü oluşturabilir veya bunları kullanan bir kitaplığı kullanabilirsiniz.

TCP ve UDP'nin karşılaştırılması

Hem TCP hem de UDP dayanmaktadır IP. IP, bir paketin kaynaktan alıcıya iletilmesine izin verir, ancak gönderilen paketin er ya da geç alıcıya ulaşacağını, ona en az bir kez ulaşacağını ve paket dizisinin doğru şekilde ulaşacağını garanti etmez. emir. Ayrıca bir paket yalnızca değerle verilen sınırlı miktarda veri içerebilir. MTU.

UDP, IP'nin üzerinde yalnızca ince bir katmandır. Bu nedenle aynı sınırlamalara sahiptir. Buna karşılık TCP'nin birçok özelliği vardır. Hata kontrolü ile iki düğüm arasında güvenilir, düzenli bir bağlantı sağlar. Bu nedenle TCP çok kullanışlıdır ve diğer birçok protokolde kullanılır; HTTP, FTP и SMTP. Ancak tüm bu özelliklerin bir bedeli var: gecikme.

Bu işlevlerin neden gecikmeye neden olabileceğini anlamak için TCP'nin nasıl çalıştığını anlamamız gerekir. Gönderen düğüm, alıcı düğüme bir paket gönderdiğinde, bir onay (ACK) almayı bekler. Belirli bir süre sonra onu almazsa (paket veya onayın kaybolması veya başka bir nedenden dolayı), paketi yeniden gönderir. Ayrıca TCP, paketlerin doğru sırayla alındığını garanti eder, böylece kayıp paket alınana kadar, diğer tüm paketler, alıcı ana bilgisayar tarafından alınmış olsa bile işlenemez.

Ancak tahmin edebileceğiniz gibi çok oyunculu oyunlarda gecikme çok önemlidir, özellikle de FPS gibi aksiyon dolu türlerde. Bu nedenle birçok oyun UDP'yi kendi protokolüyle kullanır.

Yerel bir UDP tabanlı protokol, çeşitli nedenlerden dolayı TCP'den daha verimli olabilir. Örneğin, bazı paketleri güvenilir, bazılarını güvenilmez olarak işaretleyebilir. Bu nedenle güvenilmeyen paketin alıcıya ulaşıp ulaşmaması umurunda değildir. Veya birden fazla veri akışını işleyebilir, böylece bir akıştaki kayıp paket, kalan akışları yavaşlatmaz. Örneğin, oyuncu girişi için bir başlık ve sohbet mesajları için başka bir başlık olabilir. Acil olmayan bir sohbet mesajı kaybolursa, acil olan girişi yavaşlatmaz. Veya özel bir protokol, bir video oyunu ortamında daha verimli olmak için güvenilirliği TCP'den farklı şekilde uygulayabilir.

Peki, eğer TCP bu kadar berbatsa, o zaman UDP'ye dayalı olarak kendi aktarım protokolümüzü oluşturacağız, öyle mi?

Biraz daha karmaşık. TCP, oyun ağ sistemleri için neredeyse idealin altında olsa da, oyununuz için oldukça iyi çalışabilir ve değerli zamanınızdan tasarruf etmenizi sağlayabilir. Örneğin, sıra tabanlı bir oyun veya yalnızca gecikmenin ve paket kaybının İnternet'e göre çok daha düşük olduğu LAN ağlarında oynanabilen bir oyun için gecikme bir sorun olmayabilir.

World of Warcraft, Minecraft ve Terraria dahil birçok başarılı oyun TCP kullanıyor. Ancak çoğu FPS kendi UDP tabanlı protokollerini kullanır, bu nedenle aşağıda onlar hakkında daha fazla konuşacağız.

TCP kullanmaya karar verirseniz devre dışı bırakıldığından emin olun Nagle'ın algoritmasıçünkü paketleri göndermeden önce arabelleğe alır, bu da gecikmeyi artırdığı anlamına gelir.

Çok oyunculu oyunlar bağlamında UDP ve TCP arasındaki farklar hakkında daha fazla bilgi edinmek için Glenn Fiedler'in makalesini okuyabilirsiniz. UDP'ye karşı TCP.

Kendi protokolü

Yani kendi aktarım protokolünüzü oluşturmak istiyorsunuz ancak nereden başlayacağınızı bilmiyor musunuz? Şanslısınız çünkü Glenn Fiedler bu konuda iki harika makale yazmış. İçlerinde pek çok akıllı düşünce bulacaksınız.

İlk makale Oyun Programcıları için Ağ Oluşturma 2008 ikincisinden daha kolay, Oyun Ağı Protokolü Oluşturma 2016. Daha eski olanla başlamanızı tavsiye ederim.

Glenn Fiedler'in UDP'ye dayalı özel bir protokol kullanmanın büyük bir savunucusu olduğunu unutmayın. Ve yazılarını okuduktan sonra muhtemelen TCP'nin video oyunlarında ciddi eksiklikleri olduğu fikrini benimseyecek ve kendi protokolünüzü uygulamak isteyeceksiniz.

Ancak ağ oluşturma konusunda yeniyseniz kendinize bir iyilik yapın ve TCP veya bir kitaplık kullanın. Kendi aktarım protokolünüzü başarılı bir şekilde uygulamak için önceden çok şey öğrenmeniz gerekir.

Ağ kitaplıkları

TCP'den daha verimli bir şeye ihtiyacınız varsa ancak kendi protokolünüzü uygulama ve çok fazla ayrıntıya girme zahmetine katlanmak istemiyorsanız, bir ağ kitaplığı kullanabilirsiniz. Orada oldukça fazla var:

Hepsini denemedim ama kullanımı kolay ve güvenilir olduğu için ENet'i tercih ediyorum. Ayrıca, yeni başlayanlar için anlaşılır belgeler ve bir eğitim içerir.

Taşıma Protokolü: Sonuç

Özetlemek gerekirse: iki ana aktarım protokolü vardır: TCP ve UDP. TCP'nin birçok kullanışlı özelliği vardır: güvenilirlik, paket sırasının korunması, hata tespiti. UDP'de bunların hepsi yoktur, ancak TCP doğası gereği gecikmeyi artırmıştır ve bu bazı oyunlar için kabul edilemez. Yani, gecikmenin düşük olmasını sağlamak için UDP'ye dayalı olarak kendi protokolünüzü oluşturabilir veya UDP üzerinde bir aktarım protokolü uygulayan ve çok oyunculu video oyunları için uyarlanmış bir kitaplık kullanabilirsiniz.

TCP, UDP ve kütüphane arasındaki seçim çeşitli faktörlere bağlıdır. Öncelikle oyunun ihtiyaçlarından yola çıkarak: Düşük gecikmeye ihtiyacı var mı? İkincisi, uygulama protokolü gereksinimlerinden: Güvenilir bir protokole ihtiyacı var mı? Bir sonraki bölümde göreceğimiz gibi güvenilmeyen bir protokolün oldukça uygun olduğu bir uygulama protokolü oluşturmak mümkündür. Son olarak ağ motoru geliştiricisinin deneyimini de hesaba katmanız gerekir.

İki tavsiyem var:

  • Taşıma protokolünü uygulamanın geri kalanından mümkün olduğunca soyutlayın, böylece kodun tamamını yeniden yazmaya gerek kalmadan kolayca değiştirilebilir.
  • Aşırı optimizasyon yapmayın. Ağ uzmanı değilseniz ve özel bir UDP tabanlı aktarım protokolüne ihtiyacınız olup olmadığından emin değilseniz, TCP veya güvenilirlik sağlayan bir kitaplıkla başlayabilir ve ardından performansı test edip ölçebilirsiniz. Sorunlar ortaya çıkarsa ve sorunun aktarım protokolünden kaynaklandığından eminseniz, kendi aktarım protokolünüzü oluşturmanın zamanı gelmiş olabilir.

Bu bölümün sonunda okumanızı tavsiye ederim. Çok Oyunculu Oyun Programlamaya Giriş Burada tartışılan konuların çoğunu kapsayan Brian Hook tarafından yazılmıştır.

Uygulama protokolü

Artık istemciler ve sunucu arasında veri alışverişi yapabildiğimize göre, hangi veriyi hangi formatta aktaracağımıza karar vermemiz gerekiyor.

Klasik şema, istemcilerin sunucuya girdi veya eylemler göndermesi ve sunucunun mevcut oyun durumunu istemcilere göndermesidir.

Sunucu tam durumu değil, oynatıcının yakınında bulunan varlıkların filtrelenmiş durumunu gönderir. Bunu üç nedenden dolayı yapıyor. Birincisi, tam durum yüksek frekansta iletilemeyecek kadar büyük olabilir. İkinci olarak, oyun mantığının çoğu oyun sunucusunda simüle edildiğinden, müşteriler esas olarak görsel ve işitsel verilerle ilgilenirler. Üçüncüsü, bazı oyunlarda oyuncunun haritanın diğer tarafındaki düşmanın konumu gibi belirli verileri bilmesine gerek yoktur, aksi takdirde paketleri koklayabilir ve onu öldürmek için tam olarak nereye hareket etmesi gerektiğini bilir.

Serileştirme

İlk adım, göndermek istediğimiz verileri (giriş veya oyun durumu) aktarıma uygun bir formata dönüştürmektir. Bu süreç denir serileştirme.

Hemen akla gelen düşünce, JSON veya XML gibi insan tarafından okunabilen bir format kullanmaktır. Ancak bu tamamen etkisiz olacak ve kanalın çoğunu boşa harcayacaktır.

Bunun yerine çok daha kompakt olan ikili formatın kullanılması tavsiye edilir. Yani paketler yalnızca birkaç bayt içerecektir. Burada dikkate alınması gereken bir sorun var bayt sırasıfarklı bilgisayarlarda farklılık gösterebilir.

Verileri serileştirmek için bir kitaplık kullanabilirsiniz, örneğin:

Kütüphanenin taşınabilir arşivler oluşturduğundan ve endianness'a önem verdiğinden emin olun.

Alternatif bir çözüm de bunu kendiniz uygulamaktır; özellikle kodunuza veri merkezli bir yaklaşım kullanıyorsanız, özellikle zor değildir. Ayrıca kütüphaneyi kullanırken her zaman mümkün olmayan optimizasyonları gerçekleştirmenize olanak sağlayacaktır.

Glenn Fiedler serileştirmeyle ilgili iki makale yazdı: Paketleri Okuma ve Yazma и Serileştirme Stratejileri.

sıkıştırma

İstemciler ve sunucu arasında aktarılan veri miktarı kanalın bant genişliği ile sınırlıdır. Veri sıkıştırma, her anlık görüntüde daha fazla veri aktarmanıza, güncelleme sıklığını artırmanıza veya yalnızca kanal gereksinimlerini azaltmanıza olanak tanır.

Bit paketleme

İlk teknik bit paketlemedir. İstenilen değeri tanımlamak için tam olarak gerekli olan bit sayısını kullanmaktan oluşur. Örneğin, 16 farklı değere sahip olabilen bir numaralandırmanız varsa, tam bayt (8 bit) yerine yalnızca 4 bit kullanabilirsiniz.

Glenn Fiedler makalenin ikinci bölümünde bunun nasıl uygulanacağını açıklıyor Paketleri Okuma ve Yazma.

Bit paketleme, bir sonraki bölümün konusu olacak olan örneklemede özellikle iyi çalışır.

Örnekleme

Örnekleme bir değeri kodlamak için yalnızca olası değerlerin bir alt kümesini kullanan kayıplı bir sıkıştırma tekniğidir. Ayrıklaştırmayı uygulamanın en kolay yolu, kayan nokta sayılarını yuvarlamaktır.

Glenn Fiedler (bir kez daha!) makalesinde örneklemenin nasıl hayata geçirileceğini gösteriyor Anlık Görüntü Sıkıştırma.

Sıkıştırma algoritmaları

Bir sonraki teknik kayıpsız sıkıştırma algoritmaları olacaktır.

Bana göre bilmeniz gereken en ilginç üç algoritmayı burada bulabilirsiniz:

  • Huffman kodlaması Son derece hızlı olan ve iyi sonuçlar üretebilen önceden hesaplanmış kodla. Quake3 ağ motorundaki paketleri sıkıştırmak için kullanıldı.
  • zlib veri miktarını hiçbir zaman artırmayan genel amaçlı bir sıkıştırma algoritmasıdır. Nasıl görebilirsin burada, çeşitli uygulamalarda kullanılmıştır. Durumları güncellemek için gereksiz olabilir. Ancak sunucudan istemcilere varlık, uzun metin veya arazi göndermeniz gerekiyorsa yararlı olabilir.
  • Çalıştırma uzunluklarının kopyalanması - Bu muhtemelen en basit sıkıştırma algoritmasıdır, ancak belirli veri türleri için çok etkilidir ve zlib'den önce bir ön işleme adımı olarak kullanılabilir. Birçok bitişik elemanın tekrarlandığı fayans veya voksellerden oluşan araziyi sıkıştırmak için özellikle uygundur.

Delta sıkıştırma

Son sıkıştırma tekniği delta sıkıştırmadır. Yalnızca mevcut oyun durumu ile istemci tarafından alınan son durum arasındaki farkların iletilmesinden oluşur.

İlk kez Quake3 ağ motorunda kullanıldı. Nasıl kullanılacağını açıklayan iki makale:

Glenn Fiedler de makalesinin ikinci bölümünde bunu kullanmış. Anlık Görüntü Sıkıştırma.

Шифрование

Ayrıca istemciler ile sunucu arasındaki bilgi aktarımını şifrelemeniz gerekebilir. Bunun birkaç nedeni var:

  • gizlilik/gizlilik: mesajlar yalnızca alıcı tarafından okunabilir ve ağı koklayan başka hiç kimse bunları okuyamaz.
  • kimlik doğrulama: Oyuncu rolünü oynamak isteyen kişinin anahtarını bilmesi gerekir.
  • Hile önleme: Kötü niyetli oyuncuların kendi hile paketlerini oluşturmaları çok daha zor olacak, şifreleme şemasını yeniden üretmeleri ve anahtarı bulmaları gerekecek (ki bu her bağlantıda değişir).

Bunun için bir kütüphane kullanmanızı şiddetle tavsiye ederim. kullanmanızı öneririm libsodyum, çünkü özellikle basittir ve mükemmel eğitimlere sahiptir. Özellikle ilgi çekici olan şey şu: anahtar değişimiher yeni bağlantıda yeni anahtarlar oluşturmanıza olanak tanır.

Uygulama Protokolü: Sonuç

Bu, uygulama protokolümüzü tamamlıyor. Sıkıştırmanın tamamen isteğe bağlı olduğuna ve onu kullanma kararının yalnızca oyuna ve gerekli bant genişliğine bağlı olduğuna inanıyorum. Bana göre şifreleme zorunludur, ancak ilk prototipte onsuz da yapabilirsiniz.

Uygulama mantığı

Artık istemcideki durumu güncelleyebiliyoruz ancak gecikme sorunlarıyla karşılaşabiliriz. Oyuncunun girişi tamamladıktan sonra, dünya üzerinde nasıl bir etki yarattığını görmek için oyun durumunun sunucudan güncellenmesini beklemesi gerekir.

Üstelik iki durum güncellemesi arasında dünya tamamen statiktir. Durum güncelleme oranı düşükse hareketler çok sarsıntılı olacaktır.

Bu sorunun etkisini azaltmak için çeşitli teknikler var ve bunları bir sonraki bölümde ele alacağım.

Gecikme Düzeltme Teknikleri

Bu bölümde açıklanan tüm teknikler seride ayrıntılı olarak tartışılmaktadır. Hızlı Tempolu Çok Oyunculu Gabriel Gambetta. Bu mükemmel makale serisini okumanızı şiddetle tavsiye ederim. Ayrıca bu tekniklerin pratikte nasıl çalıştığını görmenizi sağlayan etkileşimli bir demo da içerir.

İlk teknik, giriş sonucunu sunucudan yanıt beklemeden doğrudan uygulamaktır. denir istemci tarafı tahmin. Ancak istemci sunucudan bir güncelleme aldığında tahmininin doğru olduğunu doğrulamalıdır. Durum böyle değilse, sunucu otoriter olduğu için durumunu sunucudan aldığı şeye göre değiştirmesi yeterlidir. Bu teknik ilk olarak Quake'te kullanıldı. Makalede bununla ilgili daha fazla bilgi edinebilirsiniz Quake Engine kod incelemesi Fabien Sanglarsçeviri Habré'de].

İkinci teknik grubu, diğer varlıkların iki durum güncellemesi arasındaki hareketini yumuşatmak için kullanılır. Bu sorunu çözmenin iki yolu vardır: enterpolasyon ve ekstrapolasyon. Enterpolasyon durumunda son iki durum alınır ve birinden diğerine geçiş gösterilir. Dezavantajı ise müşteri her zaman geçmişte olanları gördüğü için az miktarda gecikmeye neden olmasıdır. Ekstrapolasyon, istemci tarafından alınan son duruma göre varlıkların şimdi nerede olması gerektiğini tahmin etmekle ilgilidir. Dezavantajı ise varlığın hareket yönünü tamamen değiştirmesi durumunda tahmin ile gerçek konum arasında büyük bir hata oluşmasıdır.

Yalnızca FPS'de yararlı olan en yeni, en gelişmiş teknik gecikme telafisi. Gecikme telafisini kullanırken sunucu, istemcinin hedefe ateş ederken yaşadığı gecikmeleri dikkate alır. Örneğin, bir oyuncu ekranında kafadan vuruş yaptıysa ancak gerçekte hedefi gecikme nedeniyle farklı bir konumdaysa, o zaman oyuncunun gecikme nedeniyle öldürme hakkını reddetmek adil olmaz. Bu nedenle sunucu, oyuncunun ekranında gördüklerini simüle etmek ve atış ile hedef arasındaki çarpışmayı kontrol etmek için oyuncunun ateş ettiği ana kadar zamanı geri sarar.

Glenn Fiedler (her zamanki gibi!) 2004'te bir makale yazdı Ağ Fiziği (2004)Sunucu ve istemci arasındaki fizik simülasyonlarını senkronize etmenin temelini attı. 2014 yılında yeni bir dizi makale yazdı Ağ FiziğiFizik simülasyonlarını senkronize etmek için diğer teknikleri açıklayan.

Ayrıca Valve wiki'sinde iki makale var: Kaynak Çok Oyunculu Ağ İletişimi и İstemci/Sunucu Oyun İçi Protokol Tasarımı ve Optimizasyonunda Gecikme Telafi Yöntemleri gecikmeler için tazminat almayı düşünenler.

Hile yapmayı önleme

Hileyi önlemenin iki ana tekniği vardır.

Birincisi: Hile yapanların kötü amaçlı paketler göndermesini zorlaştırmak. Yukarıda belirtildiği gibi, bunu uygulamanın iyi bir yolu şifrelemedir.

İkincisi: Otoriter bir sunucu yalnızca komutları/girdileri/eylemleri almalıdır. İstemci, girdi göndermek dışında sunucudaki durumu değiştirememelidir. Daha sonra, sunucu her girdi aldığında, onu kullanmadan önce geçerli olup olmadığını kontrol etmelidir.

Uygulama mantığı: sonuç

İstemci ve sunucu aynı bilgisayarda çalışırken bile oyununuzun davranışını kötü koşullarda test edebilmeniz için yüksek gecikme sürelerini ve düşük yenileme hızlarını simüle edecek bir yöntem uygulamanızı öneririm. Bu, gecikme yumuşatma tekniklerinin uygulanmasını büyük ölçüde basitleştirecektir.

Diğer Faydalı Kaynaklar

Ağ modelleriyle ilgili diğer kaynakları keşfetmek isterseniz bunları burada bulabilirsiniz:

  • Glenn Fiedler'in Blogu – blogunun tamamı okumaya değer, orada pek çok harika makale var. öyle Ağ teknolojileri ile ilgili tüm makaleler toplanmıştır.
  • Harika Oyun Ağı M. Fatih MAR tarafından yazılan, çevrimiçi video oyunu motorlarıyla ilgili makale ve videoların kapsamlı bir listesidir.
  • В r/gamedev alt dizininin wiki'si Ayrıca birçok faydalı bağlantı da mevcut.

Kaynak: habr.com

Yorum ekle