O modelu mreže u igricama za početnike

O modelu mreže u igricama za početnike
Zadnje dvije sedmice sam radio na mrežnom motoru za svoju igru. Prije toga nisam znao ništa o umrežavanju u igricama, pa sam pročitao mnogo članaka i napravio mnogo eksperimenata kako bih razumio sve koncepte i mogao napisati svoj vlastiti mrežni mehanizam.

U ovom vodiču, želio bih podijeliti s vama različite koncepte koje trebate naučiti prije nego što napišete vlastiti motor za igre, kao i najbolje resurse i članke za njihovo učenje.

Općenito, postoje dvije glavne vrste mrežnih arhitektura: peer-to-peer i klijent-server. U peer-to-peer (p2p) arhitekturi, podaci se prenose između bilo kojeg para povezanih igrača, dok se u arhitekturi klijent-server podaci prenose samo između igrača i servera.

Iako se peer-to-peer arhitektura još uvijek koristi u nekim igrama, klijent-server je standard: lakši je za implementaciju, zahtijeva manju širinu kanala i olakšava zaštitu od varanja. Stoga ćemo se u ovom vodiču fokusirati na arhitekturu klijent-server.

Posebno nas najviše zanimaju autoritarni serveri: u takvim sistemima server je uvijek u pravu. Na primjer, ako igrač misli da je na (10, 5), a server mu kaže da je na (5, 3), tada bi klijent trebao zamijeniti svoju poziciju onom koju server prijavljuje, a ne obrnuto. Upotreba autoritativnih servera olakšava prepoznavanje varalica.

Postoje tri glavne komponente u mrežnim sistemima za igre:

  • Transportni protokol: kako se podaci prenose između klijenata i servera.
  • Aplikacioni protokol: šta se prenosi od klijenata do servera i od servera do klijenata i u kom formatu.
  • Logika aplikacije: kako se preneseni podaci koriste za ažuriranje stanja klijenata i servera.

Vrlo je važno razumjeti ulogu svakog dijela i teškoće povezane s njima.

Transportni protokol

Prvi korak je odabir protokola za prijenos podataka između servera i klijenata. Za to postoje dva internet protokola: TCP и UDP. Ali možete kreirati vlastiti transportni protokol na temelju jednog od njih ili koristiti biblioteku koja ih koristi.

Poređenje TCP-a i UDP-a

I TCP i UDP su zasnovani na IP. IP omogućava da se paket prenese od izvora do primaoca, ali ne garantuje da će poslati paket prije ili kasnije stići do primaoca, da će do njega doći barem jednom i da će slijed paketa stići u ispravan redosled. Štaviše, paket može sadržavati samo ograničenu veličinu podataka, koju daje vrijednost MTU.

UDP je samo tanak sloj na vrhu IP-a. Dakle, ima ista ograničenja. Nasuprot tome, TCP ima mnogo karakteristika. Pruža pouzdanu uređenu vezu između dva čvora sa provjerom grešaka. Stoga je TCP vrlo zgodan i koristi se u mnogim drugim protokolima, na primjer u HTTP, FTP и SMTP. Ali sve ove karakteristike imaju svoju cijenu: odlaganje.

Da bismo razumjeli zašto ove funkcije mogu uzrokovati kašnjenje, moramo razumjeti kako TCP funkcionira. Kada host koji šalje paket odašilje paket hostu primaocu, očekuje da će primiti potvrdu (ACK). Ako ga nakon određenog vremena ne primi (jer je izgubljen paket ili potvrda ili iz nekog drugog razloga), onda ponovo šalje paket. Štaviše, TCP garantuje da su paketi primljeni ispravnim redosledom, tako da sve dok se ne primi izgubljeni paket, svi ostali paketi ne mogu biti obrađivani, čak i ako ih je čvor primao već primio.

Ali kao što verovatno razumete, kašnjenje u igricama za više igrača je veoma važno, posebno u takvim aktivnim žanrovima kao što je FPS. Zbog toga mnoge igre koriste UDP sa sopstvenim protokolom.

Izvorni protokol baziran na UDP-u može biti efikasniji od TCP-a iz različitih razloga. Na primjer, može označiti neke pakete kao pouzdane, a druge kao nepouzdane. Stoga ga nije briga da li je nepouzdani paket stigao do primaoca. Ili može obraditi više tokova podataka tako da paket izgubljen u jednom toku ne uspori druge tokove. Na primjer, može postojati nit za unos igrača i druga nit za poruke ćaskanja. Ako se poruka ćaskanja koja nije hitan podatak izgubi, onda to neće usporiti unos koji je hitan. Ili bi vlasnički protokol mogao implementirati pouzdanost drugačije od TCP-a kako bi bio efikasniji u okruženju video igrica.

Dakle, ako je TCP loš, onda ćemo izgraditi vlastiti transportni protokol baziran na UDP-u?

Sve je malo komplikovanije. Iako je TCP skoro neoptimalan za mrežne sisteme za igre, može raditi prilično dobro za vašu specifičnu igru ​​i uštedjeti vam dragocjeno vrijeme. Na primjer, kašnjenje možda nije problem za igru ​​baziranu na potezu ili igru ​​koja se može igrati samo na LAN mrežama, gdje su kašnjenje i gubitak paketa mnogo manji nego na Internetu.

Mnoge uspješne igre, uključujući World of Warcraft, Minecraft i Terraria, koriste TCP. Međutim, većina FPS-ova koristi svoje UDP-bazirane protokole, tako da ćemo o njima više govoriti u nastavku.

Ako odlučite koristiti TCP, provjerite je li onemogućen Nagleov algoritam, jer baferuje pakete prije slanja, što znači da povećava kašnjenje.

Da biste saznali više o razlikama između UDP-a i TCP-a u kontekstu igara za više igrača, pogledajte članak Glenna Fiedlera UDP vs. TCP.

Proprietary protokol

Dakle, želite da kreirate sopstveni transportni protokol, ali ne znate odakle da počnete? Imate sreće, jer je Glenn Fiedler napisao dva nevjerovatna članka o tome. U njima ćete naći mnogo pametnih ideja.

Prvi članak Umrežavanje za programere igara 2008, lakši od drugog Izgradnja mrežnog protokola igre 2016. Preporučujem da počnete sa starijim.

Imajte na umu da je Glenn Fiedler veliki zagovornik korištenja vašeg vlastitog protokola zasnovanog na UDP-u. I nakon čitanja njegovih članaka, vjerovatno ćete usvojiti njegovo mišljenje da TCP ima ozbiljne nedostatke u video igrama, te ćete poželjeti implementirati svoj vlastiti protokol.

Ali ako ste novi u umrežavanju, učinite sebi uslugu i koristite TCP ili biblioteku. Da biste uspješno implementirali svoj vlastiti transportni protokol, morate unaprijed mnogo naučiti.

Mrežne biblioteke

Ako vam treba nešto efikasnije od TCP-a, ali ne želite da se zamarate implementacijom sopstvenog protokola i ulazite u mnogo detalja, možete koristiti mrežnu biblioteku. Ima ih puno:

Nisam ih sve isprobao, ali više volim ENet jer je jednostavan za korištenje i pouzdan. Osim toga, ima jasnu dokumentaciju i tutorijal za početnike.

Zaključak transportnog protokola

Da rezimiramo, postoje dva glavna transportna protokola: TCP i UDP. TCP ima mnogo korisnih karakteristika: pouzdanost, očuvanje redosleda paketa, otkrivanje grešaka. UDP nema sve to, ali TCP, po svojoj prirodi, ima veliko kašnjenje koje je neprihvatljivo za neke igre. Odnosno, da biste osigurali nisko kašnjenje, možete kreirati vlastiti protokol baziran na UDP-u ili koristiti biblioteku koja implementira transportni protokol na UDP i prilagođena je za video igre za više igrača.

Izbor između TCP, UDP i biblioteke zavisi od nekoliko faktora. Prvo, iz potreba igre: da li joj je potrebna mala latencija? Drugo, iz zahtjeva aplikacijskog protokola: da li mu je potreban pouzdan protokol? Kao što ćemo vidjeti u sljedećem dijelu, moguće je kreirati aplikacijski protokol za koji je nepouzdan protokol sasvim prikladan. Konačno, također morate uzeti u obzir iskustvo programera mrežnog motora.

imam dva savjeta:

  • Apstrahujte transportni protokol što je više moguće od ostatka aplikacije tako da se može lako zamijeniti bez ponovnog pisanja cijelog koda.
  • Nemojte previše optimizirati. Ako niste stručnjak za mrežu i niste sigurni da li vam je potreban vlastiti transportni protokol zasnovan na UDP-u, možete početi s TCP-om ili bibliotekom koja pruža pouzdanost, a zatim testirati i mjeriti performanse. Ako imate problema i sigurni ste da je u pitanju transportni protokol, možda je vrijeme da kreirate svoj vlastiti transportni protokol.

Na kraju ovog dijela preporučujem da pročitate Uvod u programiranje igara za više igrača Brian Hook, koji pokriva mnoge teme o kojima se ovdje raspravlja.

Application Protocol

Sada kada možemo razmjenjivati ​​podatke između klijenata i servera, moramo odlučiti koje podatke ćemo prenijeti iu kojem formatu.

Klasična šema je da klijenti šalju ulaz ili akcije serveru, a server klijentima šalje trenutno stanje igre.

Server šalje ne puno, već filtrirano stanje sa entitetima koji su u blizini igrača. On to radi iz tri razloga. Prvo, ukupno stanje može biti preveliko za prijenos na visokoj frekvenciji. Drugo, klijente uglavnom zanimaju vizuelni i audio podaci, jer se većina logike igre simulira na serveru igre. Treće, u nekim igrama igrač ne mora znati određene podatke, kao što je položaj neprijatelja na drugoj strani mape, jer u suprotnom može nanjušiti pakete i znati gdje tačno da se kreće da ga ubije.

Serijalizacija

Prvi korak je pretvaranje podataka koje želimo poslati (ulaz ili stanje igre) u format pogodan za prijenos. Ovaj proces se zove serijalizacija.

Odmah mi pada na pamet ideja da se koristi format čitljiv ljudima, kao što je JSON ili XML. Ali ovo će biti potpuno neefikasno i uzalud će zauzeti većinu kanala.

Umjesto toga, preporučuje se korištenje binarnog formata, koji je mnogo kompaktniji. To jest, paketi će sadržavati samo nekoliko bajtova. Ovdje moramo uzeti u obzir problem red bajtova, koji se mogu razlikovati na različitim računarima.

Za serijalizaciju podataka možete koristiti biblioteku, na primjer:

Samo se pobrinite da biblioteka kreira prenosive arhive i vodi računa o endiannessu.

Alternativno rješenje bi bilo da ga sami implementirate, nije tako teško, pogotovo ako koristite pristup usmjeren na podatke u svom kodu. Osim toga, omogućit će vam da izvršite optimizacije koje nisu uvijek moguće kada koristite biblioteku.

Glenn Fiedler je napisao dva članka o serijalizaciji: Čitanje i pisanje paketa и Strategije serijalizacije.

Kompresija

Količina podataka koji se prenose između klijenata i servera ograničena je propusnim opsegom kanala. Kompresija podataka će vam omogućiti da prenesete više podataka u svakom snimku, povećate brzinu osvježavanja ili jednostavno smanjite zahtjeve za propusnost.

Pakovanje bitova

Prva tehnika je pakovanje bitova. Sastoji se od korištenja tačnog broja bitova koji je neophodan za opisivanje željene vrijednosti. Na primjer, ako imate enum koji može imati 16 različitih vrijednosti, onda umjesto cijelog bajta (8 bitova), možete koristiti samo 4 bita.

Glenn Fiedler objašnjava kako to implementirati u drugom dijelu članka. Čitanje i pisanje paketa.

Pakovanje bitova posebno dobro funkcioniše sa diskretizacijom, što će biti tema sledećeg odeljka.

Uzorkovanje

Uzorkovanje je tehnika kompresije s gubicima koja koristi samo podskup mogućih vrijednosti za kodiranje vrijednosti. Najlakši način za implementaciju diskretizacije je zaokruživanje brojeva s pomičnim zarezom.

Glenn Fiedler (opet!) u svom članku pokazuje kako primijeniti diskretizaciju u praksi Snapshot Compression.

Algoritmi kompresije

Sljedeća tehnika će biti algoritmi kompresije bez gubitaka.

Evo, po mom mišljenju, tri najzanimljivija algoritma koje trebate znati:

  • Huffman kodiranje sa unapred izračunatim kodom, koji je izuzetno brz i može dati dobre rezultate. Korišćen je za komprimovanje paketa u Quake3 mrežnom mehanizmu.
  • zlo je algoritam kompresije opće namjene koji nikada ne povećava količinu podataka. Kako vidite ovdje, koristio se u raznim aplikacijama. Za ažuriranje stanja može biti suvišno. Ali može biti od koristi ako klijentima sa servera treba da pošaljete sredstva, dugačke tekstove ili teren.
  • Kopiranje dužine serije je vjerovatno najjednostavniji algoritam kompresije, ali je vrlo efikasan za određene tipove podataka i može se koristiti kao korak pre obrade prije zlib-a. Posebno je pogodan za sabijanje terena koji se sastoji od pločica ili voksela u kojima se ponavljaju mnogi susjedni elementi.

delta kompresija

Konačna tehnika kompresije je delta kompresija. Ona leži u činjenici da se prenose samo razlike između trenutnog stanja igre i zadnjeg stanja koje je primio klijent.

Prvi put je korišten u Quake3 mrežnom mehanizmu. Evo dva članka koja objašnjavaju kako ga koristiti:

Glenn Fiedler ga je također koristio u drugom dijelu svog članka. Snapshot Compression.

Encryption

Osim toga, možda ćete morati šifrirati prijenos informacija između klijenata i servera. Postoji nekoliko razloga za to:

  • Privatnost/povjerljivost: Poruke može pročitati samo primalac i nijedan drugi mrežni njuškač neće ih moći pročitati.
  • autentifikacija: osoba koja želi igrati ulogu igrača mora znati svoj ključ.
  • prevencija varanja: zlonamjernim igračima će biti mnogo teže da kreiraju sopstvene pakete za varanje, moraće da repliciraju šemu šifrovanja i pronađu ključ (koji se menja pri svakoj konekciji).

Toplo preporučujem korištenje biblioteke za ovo. Predlažem korištenje libsodijum, jer je posebno jednostavan i ima sjajne tutorijale. Od posebnog interesa je tutorijal o razmjena ključeva, koji vam omogućava da generišete nove ključeve na svakoj novoj vezi.

Protokol aplikacije: Zaključak

Ovim se završava aplikacijski protokol. Vjerujem da je kompresija potpuno opciona i odluka o njenom korištenju ovisi samo o igrici i potrebnoj širini pojasa. Šifriranje je, po mom mišljenju, obavezno, ali u prvom prototipu možete bez njega.

Logika aplikacije

Sada smo u mogućnosti ažurirati stanje u klijentu, ali možemo naići na probleme sa kašnjenjem. Igrač, nakon što izvrši unos, treba da sačeka ažuriranje stanja igre sa servera da vidi kakav je to efekat imao na svet.

Štaviše, između dva ažuriranja stanja, svijet je potpuno statičan. Ako je stopa ažuriranja stanja niska, tada će pokreti biti vrlo trzavi.

Postoji nekoliko tehnika za ublažavanje uticaja ovog problema i o njima ću govoriti u sledećem odeljku.

Tehnike zaglađivanja odlaganja

Sve tehnike opisane u ovom odeljku detaljno su razmotrene u seriji. Brza igra za više igrača Gabriel Gambetta. Toplo preporučujem čitanje ove odlične serije članaka. Takođe uključuje interaktivni demo da vidite kako ove tehnike rade u praksi.

Prva tehnika je da se direktno primeni rezultat unosa bez čekanja odgovora od servera. To se zove predviđanje na strani klijenta. Međutim, kada klijent primi ažuriranje od servera, mora provjeriti da li je njegovo predviđanje bilo ispravno. Ako to nije slučaj, onda on samo treba da promijeni svoje stanje prema onome što je dobio od servera, jer je server autoritaran. Ova tehnika je prvi put korištena u Quakeu. Više o tome možete pročitati u članku. Pregled koda Quake Engine Fabien Sanglars [prevod na Habre].

Drugi skup tehnika se koristi za izglađivanje kretanja drugih entiteta između dva ažuriranja stanja. Postoje dva načina za rješavanje ovog problema: interpolacija i ekstrapolacija. U slučaju interpolacije, uzimaju se posljednja dva stanja i prikazuje se prijelaz iz jednog u drugo. Njegov nedostatak je što uzrokuje mali dio kašnjenja, jer klijent uvijek vidi šta se dogodilo u prošlosti. Ekstrapolacija se odnosi na predviđanje gdje bi se entiteti sada trebali nalaziti na osnovu posljednjeg stanja koje je primio klijent. Njegov nedostatak je što ako entitet potpuno promijeni smjer kretanja, onda će doći do velike greške između prognoze i stvarne pozicije.

Poslednja, najnaprednija tehnika, korisna samo u FPS-u, jeste kompenzacija zaostajanja. Kada koristi kompenzaciju kašnjenja, server uzima u obzir kašnjenja klijenta kada puca na cilj. Na primjer, ako je igrač izveo udarac u glavu na svom ekranu, ali je u stvarnosti njegova meta bila na drugoj lokaciji zbog kašnjenja, tada bi bilo nepravedno uskratiti igraču pravo na ubijanje zbog kašnjenja. Dakle, server premota vrijeme unazad do trenutka kada je igrač pucao kako bi simulirao ono što je igrač vidio na svom ekranu i provjerio ima li sudara između njihovog udarca i mete.

Glenn Fiedler (kao i uvijek!) napisao je članak 2004. godine Mrežna fizika (2004), u kojem je postavio temelje za sinhronizaciju simulacija fizike između servera i klijenta. 2014. godine napisao je novu seriju članaka fizika umrežavanja, u kojem je opisao druge tehnike za sinkronizaciju simulacija fizike.

Postoje i dva članka na Valveovoj wiki, Izvor Mreža za više igrača и Metode kompenzacije kašnjenja u dizajnu i optimizaciji protokola klijent/server u igri rješavanje naknade za kašnjenje.

Prevencija varanja

Postoje dvije glavne tehnike prevencije varanja.

Prvo, otežava prevarantima slanje zlonamjernih paketa. Kao što je gore spomenuto, dobar način za implementaciju je enkripcija.

Drugo, autoritativni server treba da prima samo komande/ulaz/akcije. Klijent ne bi trebao biti u mogućnosti promijeniti stanje na serveru osim slanjem unosa. Zatim server, svaki put kada primi ulaz, mora provjeriti valjanost prije nego što ga primijeni.

Logika aplikacije: Zaključak

Preporučujem vam da implementirate način za simulaciju velike latencije i niske stope osvježavanja kako biste mogli testirati ponašanje vaše igre u lošim uvjetima, čak i kada klijent i server rade na istoj mašini. Ovo uvelike pojednostavljuje implementaciju tehnika izglađivanja kašnjenja.

Drugi korisni resursi

Ako želite istražiti druge resurse mrežnog modela, možete ih pronaći ovdje:

izvor: www.habr.com

Dodajte komentar