O mrežnom modelu u igrama za početnike

O mrežnom modelu u igrama za početnike
Posljednja dva tjedna radio sam na online motoru za svoju igru. Prije toga nisam znao ništa o umrežavanju u igrama, 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 s vama podijeliti različite koncepte koje trebate naučiti prije nego što napišete vlastiti pogon za igre, kao i najbolje izvore i članke za njihovo učenje.

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

Iako se peer-to-peer arhitektura još uvijek koristi u nekim igrama, klijent-poslužitelj je standard: lakše ju je implementirati, zahtijeva manju širinu kanala i olakšava zaštitu od varanja. Stoga ćemo se u ovom vodiču usredotočiti na arhitekturu klijent-poslužitelj.

Konkretno, najviše nas zanimaju autoritarni poslužitelji: u takvim sustavima poslužitelj je uvijek u pravu. Na primjer, ako igrač misli da je na koordinatama (10, 5), a server mu kaže da je na (5, 3), tada bi klijent trebao zamijeniti svoju poziciju onom koju je prijavio poslužitelj, a ne vice obrnuto. Korištenje autoritativnih poslužitelja olakšava prepoznavanje varalica.

Sustavi mrežnog igranja imaju tri glavne komponente:

  • Transportni protokol: kako se podaci prenose između klijenata i poslužitelja.
  • Aplikacijski protokol: što se prenosi od klijenata do poslužitelja i od poslužitelja do klijenata i u kojem formatu.
  • Logika aplikacije: kako se preneseni podaci koriste za ažuriranje stanja klijenata i poslužitelja.

Vrlo je važno razumjeti ulogu svakog dijela i izazove povezane s njima.

Transportni protokol

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

Usporedba TCP-a i UDP-a

I TCP i UDP temelje se na IP. IP omogućuje prijenos paketa od izvora do primatelja, ali ne jamči da će poslani paket prije ili kasnije doći do primatelja, da će do njega stići barem jednom i da će niz paketa stići u ispravnom obliku. narudžba. Štoviše, paket može sadržavati samo ograničenu količinu podataka, zadanu vrijednošću MTU.

UDP je samo tanki sloj na vrhu IP-a. Stoga ima ista ograničenja. Nasuprot tome, TCP ima mnogo značajki. Omogućuje pouzdanu, urednu vezu između dva čvora s provjerom pogrešaka. Stoga je TCP vrlo zgodan i koristi se u mnogim drugim protokolima, npr. HTTP, FTP и SMTP. Ali sve te značajke imaju svoju cijenu: odgoditi.

Da bismo razumjeli zašto te funkcije mogu uzrokovati kašnjenje, moramo razumjeti kako radi TCP. Kada čvor pošiljatelj odašilje paket čvoru primatelju, očekuje da će primiti potvrdu (ACK). Ako ga nakon određenog vremena ne primi (zbog gubitka paketa ili potvrde ili iz nekog drugog razloga), ponovno šalje paket. Štoviše, TCP jamči da su paketi primljeni ispravnim redoslijedom, tako da dok se izgubljeni paket ne primi, svi drugi paketi se ne mogu obraditi, čak i ako ih je primatelj već primio.

Ali kao što vjerojatno možete zamisliti, latencija u igrama za više igrača je vrlo važna, posebno u žanrovima prepunim akcije kao što je FPS. Zbog toga mnoge igre koriste UDP s vlastitim protokolom.

Izvorni protokol temeljen na UDP-u može biti učinkovitiji 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 hoće li nepouzdani paket doći do primatelja. Ili može obraditi više tokova podataka tako da izgubljeni paket u jednom toku ne uspori preostale tokove. Na primjer, može postojati nit za unos igrača i druga nit za chat poruke. Ako se poruka chata koja nije hitna izgubi, to neće usporiti unos koji je hitan. Ili bi vlasnički protokol mogao implementirati pouzdanost drugačije od TCP-a kako bi bio učinkovitiji u okruženju videoigara.

Dakle, ako je TCP toliko sranje, onda ćemo stvoriti vlastiti transportni protokol temeljen na UDP-u?

Malo je kompliciranije. Iako je TCP gotovo suboptimalan za mrežne sustave igara, može prilično dobro funkcionirati za vašu specifičnu igru ​​i uštedjeti vam dragocjeno vrijeme. Na primjer, kašnjenje možda nije problem za naizmjeničnu igru ​​ili igru ​​koja se može igrati samo na LAN mrežama, gdje su kašnjenje i gubitak paketa mnogo niži 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 vlastite protokole koji se temelje na UDP-u, pa ćemo o njima više govoriti u nastavku.

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

Kako biste saznali više o razlikama između UDP-a i TCP-a u kontekstu igara za više igrača, možete pročitati članak Glenna Fiedlera UDP vs. TCP.

Vlastiti protokol

Dakle, želite stvoriti vlastiti transportni protokol, ali ne znate odakle početi? Imate sreće jer je Glenn Fiedler napisao dva nevjerojatna članka o ovome. U njima ćete naći puno pametnih misli.

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

Imajte na umu da je Glenn Fiedler veliki zagovornik korištenja prilagođenog protokola temeljenog na UDP-u. A nakon što pročitate njegove članke, vjerojatno ćete usvojiti njegovo mišljenje da TCP ima ozbiljne nedostatke u video igrama, te ćete poželjeti implementirati vlastiti protokol.

Ali ako ste novi u umrežavanju, učinite si uslugu i koristite TCP ili biblioteku. Za uspješnu implementaciju vlastitog transportnog protokola potrebno je puno naučiti prije toga.

Mrežne knjižnice

Ako vam je potrebno nešto učinkovitije od TCP-a, ali ne želite prolaziti kroz gnjavažu implementacije vlastitog protokola i ulaziti u mnogo detalja, možete koristiti mrežnu biblioteku. Ima ih puno:

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

Transportni protokol: Zaključak

Ukratko: postoje dva glavna transportna protokola: TCP i UDP. TCP ima mnogo korisnih značajki: pouzdanost, očuvanje redoslijeda paketa, otkrivanje grešaka. UDP nema sve to, ali TCP po svojoj prirodi ima povećanu latenciju, što je nedopustivo za neke igre. Odnosno, kako biste osigurali nisku latenciju, možete izraditi vlastiti protokol temeljen na UDP-u ili koristiti biblioteku koja implementira transportni protokol na UDP-u i prilagođena je za videoigre za više igrača.

Izbor između TCP-a, UDP-a i biblioteke ovisi o nekoliko čimbenika. Prvo, od potreba igre: treba li niska latencija? Drugo, zahtjevi aplikacijskog protokola: treba li pouzdan protokol? Kao što ćemo vidjeti u sljedećem dijelu, moguće je kreirati aplikacijski protokol za koji je nepouzdani protokol sasvim prikladan. Konačno, morate također uzeti u obzir iskustvo programera mrežnog motora.

Imam dva savjeta:

  • Apstrahirajte transportni protokol od ostatka aplikacije što je više moguće tako da se može lako zamijeniti bez ponovnog pisanja cijelog koda.
  • Nemojte pretjerano optimizirati. Ako niste stručnjak za umrežavanje i niste sigurni trebate li prilagođeni prijenosni protokol temeljen na UDP-u, možete početi s TCP-om ili bibliotekom koja pruža pouzdanost, a zatim testirati i mjeriti performanse. Ako se pojave problemi i uvjereni ste da je uzrok transportni protokol, možda je vrijeme da izradite vlastiti transportni protokol.

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

Primjenski protokol

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

Klasična shema je da klijenti šalju unos ili akcije poslužitelju, a poslužitelj šalje trenutno stanje igre klijentima.

Poslužitelj ne šalje puno stanje, već filtrirano stanje s entitetima koji se nalaze u blizini igrača. On to čini iz tri razloga. Prvo, kompletno stanje može biti preveliko za prijenos visokom frekvencijom. Drugo, klijente uglavnom zanimaju vizualni i audio podaci, jer se većina logike igre simulira na poslužitelju igre. Treće, u nekim igrama igrač ne mora znati određene podatke, na primjer, položaj neprijatelja s druge strane karte, inače može nanjušiti pakete i točno znati gdje se treba pomaknuti da ga ubije.

Serijalizacija

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

Misao koja odmah pada na pamet je korištenje formata čitljivog čovjeka, kao što je JSON ili XML. Ali to će biti potpuno neučinkovito i potrošit će većinu kanala.

Umjesto toga preporuča se koristiti binarni format koji je mnogo kompaktniji. To jest, paketi će sadržavati samo nekoliko bajtova. Ovdje postoji problem koji treba razmotriti poredak bajtova, koji se može razlikovati na različitim računalima.

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

Samo se pobrinite da biblioteka stvara prijenosne arhive i vodi računa o endiannessu.

Alternativno rješenje je da ga sami implementirate; nije osobito teško, pogotovo ako svom kodu koristite pristup usmjeren na podatke. Osim toga, omogućit će vam izvođenje optimizacija koje nisu uvijek moguće pri korištenju biblioteke.

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

Kompresija

Količina podataka koja se prenosi između klijenata i poslužitelja ograničena je propusnošću kanala. Kompresija podataka omogućit će vam prijenos više podataka u svakoj snimci, povećanje učestalosti ažuriranja ili jednostavno smanjenje zahtjeva kanala.

Bitno pakiranje

Prva tehnika je pakiranje bitova. Sastoji se od korištenja točno onog broja bitova koji su potrebni za opis željene vrijednosti. Na primjer, ako imate enum koji može imati 16 različitih vrijednosti, tada umjesto cijelog bajta (8 bita), možete koristiti samo 4 bita.

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

Bit pakiranje posebno dobro funkcionira s uzorkovanjem, što će biti tema sljedećeg odjeljka.

Uzorkovanje

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

Glenn Fiedler (opet!) u svom članku pokazuje kako uzorkovanje staviti u praksu Kompresija snimke.

Algoritmi kompresije

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

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

  • Huffmanovo kodiranje s unaprijed izračunatim kodom, koji je iznimno brz i može dati dobre rezultate. Korišten je za komprimiranje paketa u Quake3 mrežnom pogonu.
  • zlib je algoritam za kompresiju opće namjene koji nikada ne povećava količinu podataka. Kako vidiš здесь, korišten je u raznim primjenama. Može biti suvišno za ažuriranje stanja. Ali može biti korisno ako trebate klijentima s poslužitelja poslati sredstva, dugačke tekstove ili teren.
  • Kopiranje duljina izvođenja - Ovo je vjerojatno najjednostavniji algoritam kompresije, ali je vrlo učinkovit za određene vrste podataka i može se koristiti kao korak predprocesiranja prije zliba. Posebno je prikladan za kompresiju terena sastavljenog od pločica ili voksela u kojima se ponavlja mnogo susjednih elemenata.

Delta kompresija

Posljednja tehnika kompresije je delta kompresija. Sastoji se od toga 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 pogonu. Evo dva članka koja objašnjavaju kako ga koristiti:

Glenn Fiedler također ga je koristio u drugom dijelu svog članka Kompresija snimke.

Šifriranje

Osim toga, možda ćete morati šifrirati prijenos informacija između klijenata i poslužitelja. Nekoliko je razloga za to:

  • privatnost/povjerljivost: poruke može čitati samo primatelj i nijedna druga osoba koja njuška po mreži neće ih moći pročitati.
  • autentifikacija: osoba koja želi igrati ulogu igrača mora znati njegov ključ.
  • Prevencija varanja: Zlonamjernim igračima bit će puno teže kreirati vlastite pakete varanja, morat će reproducirati shemu šifriranja i pronaći ključ (koji se mijenja sa svakom vezom).

Toplo preporučujem korištenje knjižnice za ovo. Predlažem korištenje libodij, jer je posebno jednostavan i ima izvrsne upute. Posebno je zanimljiv tutorial o razmjena ključeva, koji vam omogućuje generiranje novih ključeva sa svakom novom vezom.

Protokol primjene: Zaključak

Ovo zaključuje naš protokol prijave. Vjerujem da je kompresija potpuno opcionalna i odluka o njezinoj upotrebi ovisi samo o igri i potrebnoj propusnosti. Šifriranje je, po mom mišljenju, obavezno, ali u prvom prototipu možete bez njega.

Logika primjene

Sada možemo ažurirati stanje u klijentu, ali možemo naići na probleme s kašnjenjem. Igrač, nakon dovršetka unosa, treba pričekati da se stanje igre ažurira s poslužitelja kako bi vidio kakav je utjecaj imalo na svijet.

Štoviš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 smanjenje utjecaja ovog problema, a ja ću ih obraditi u sljedećem odjeljku.

Tehnike izglađivanja latencije

Sve tehnike opisane u ovom odjeljku detaljno se raspravljaju u seriji Brzi multiplayer Gabriel Gambetta. Toplo preporučam čitanje ove izvrsne serije članaka. Također uključuje interaktivni demo koji vam omogućuje da vidite kako ove tehnike funkcioniraju u praksi.

Prva tehnika je izravna primjena rezultata unosa bez čekanja na odgovor poslužitelja. To se zove predviđanje na strani klijenta. Međutim, kada klijent primi ažuriranje od poslužitelja, mora provjeriti je li njegovo predviđanje bilo točno. Ako to nije slučaj, onda samo treba promijeniti svoje stanje u skladu s onim što je primio od poslužitelja, jer je poslužitelj autoritaran. Ova tehnika je prvi put korištena u Quakeu. Više o tome možete pročitati u članku Pregled koda Quake Enginea Fabien Sanglars [prijevod na Habréu].

Drugi skup tehnika koristi se za glatko kretanje drugih entiteta između dva ažuriranja stanja. Postoje dva načina rješavanja ovog problema: interpolacija i ekstrapolacija. U slučaju interpolacije uzimaju se posljednja dva stanja i prikazuje prijelaz iz jednog u drugo. Nedostatak mu je što uzrokuje malo kašnjenja jer klijent uvijek vidi što se dogodilo u prošlosti. Ekstrapolacija se odnosi na predviđanje gdje bi entiteti sada trebali biti na temelju posljednjeg stanja koje je primio klijent. Nedostatak mu je što ako entitet potpuno promijeni smjer kretanja, tada će doći do velike pogreške između prognoze i stvarnog položaja.

Najnovija, najnaprednija tehnika korisna samo u FPS-u je kompenzacija kašnjenja. Kada koristi kompenzaciju kašnjenja, poslužitelj uzima u obzir kašnjenja klijenta kada puca u metu. Na primjer, ako je igrač pucao u glavu na svom zaslonu, ali je u stvarnosti njegova meta bila na drugoj lokaciji zbog kašnjenja, tada bi bilo nepravedno uskratiti igraču pravo da ubije zbog kašnjenja. Stoga poslužitelj premotava vrijeme unatrag do trenutka kada je igrač pucao kako bi simulirao ono što je igrač vidio na svom zaslonu i provjerio koliziju između njegovog udarca i mete.

Glenn Fiedler (kao i uvijek!) napisao je članak 2004 Mrežna fizika (2004.), u kojem je postavio temelje za sinkronizaciju fizičkih simulacija između poslužitelja i klijenta. Godine 2014. napisao je novu seriju članaka Fizika umrežavanja, koji je opisao druge tehnike za sinkronizaciju fizičkih simulacija.

Također postoje dva članka na Valve wikiju, Izvorno umrežavanje za više igrača и Metode kompenzacije latencije u dizajnu i optimizaciji protokola klijent/poslužitelj u igri koji razmatraju naknadu za kašnjenja.

Sprječavanje varanja

Postoje dvije glavne tehnike za sprječavanje varanja.

Prvo: otežava varalicama slanje zlonamjernih paketa. Kao što je gore spomenuto, dobar način za implementaciju ovoga je šifriranje.

Drugo: autoritarni poslužitelj treba primati samo naredbe/unos/akcije. Klijent ne bi trebao moći promijeniti stanje na poslužitelju osim slanjem unosa. Zatim, svaki put kada poslužitelj primi unos, mora provjeriti je li valjan prije nego što ga upotrijebi.

Logika primjene: zaključak

Preporučujem da implementirate način simulacije visokih latencija i niske stope osvježavanja kako biste mogli testirati ponašanje svoje igre u lošim uvjetima, čak i kada klijent i poslužitelj rade na istom računalu. Ovo će uvelike pojednostaviti implementaciju tehnika izglađivanja kašnjenja.

Drugi korisni izvori

Ako želite istražiti druge resurse o mrežnim modelima, možete ih pronaći ovdje:

Izvor: www.habr.com

Dodajte komentar