O omrežnem modelu v igrah za začetnike

O omrežnem modelu v igrah za začetnike
V zadnjih dveh tednih sem delal na omrežnem mehanizmu za svojo igro. Pred tem sem imel zelo malo znanja o omrežnih tehnologijah v igrah, zato sem prebral številne članke in opravil številne eksperimente, da bi razumel vse koncepte in bil sposoben napisati svoj omrežni mehanizem.

V tem priročniku bi želel deliti z vami različne koncepte, ki se jih morate naučiti, preden začnete pisati svoj lastni mehanizem igre, ter najboljše vire in članke za njihovo učenje.

Na splošno obstajata dva osnovna tipa omrežnih arhitektur: peer-to-peer in odjemalec-strežnik. V arhitekturi peer-to-peer (p2p) se podatki prenašajo med kakršnimi koli pari povezanih igralcev, medtem ko se v arhitekturi odjemalec-strežnik podatki prenašajo samo med igralci in strežnikom.

Čeprav se arhitektura peer-to-peer še vedno uporablja v nekaterih igrah, je standard arhitektura odjemalec-strežnik: ta je lažja za implementacijo, zahteva manj širokopasovnega kanala in olajša zaščito pred goljufanjem. Zato se bomo v tem priročniku osredotočili na arhitekturo odjemalec-strežnik.

Zlasti nas zanimajo avtoritativni strežniki: v takih sistemih je strežnik vedno pravi. Na primer, če igralec misli, da se nahaja na koordinatah (10, 5), strežnik pa mu pove, da je na (5, 3), mora odjemalec zamenjati svojo pozicijo s tisto, ki jo posreduje strežnik, in ne obratno. Uporaba avtoritativnih strežnikov poenostavi prepoznavanje goljufov.

V omrežnih sistemih iger so trije osnovni sestavni deli:

  • Transportni protokol: kako se podatki prenašajo med odjemalci in strežnikom.
  • Protokol aplikacij: kaj se prenaša od odjemalcev do strežnika in od strežnika do odjemalcev ter v kakšni obliki.
  • Logika aplikacije: kako se prenašani podatki uporabljajo za posodabljanje stanja odjemalcev in strežnika.

Zelo pomembno je razumeti vlogo vsakega dela in povezane izzive.

Transportni protokol

Prvi korak je izbira protokola za prenos podatkov med strežnikom in odjemalci. Za to sta na voljo dva internetna protokola: TCP in UDP. Toda lahko ustvarite tudi svoj transportni protokol na osnovi enega od njiju ali uporabite knjižnico, v kateri sta vključena.

Primerjava TCP in UDP

Tako TCP kot UDP temeljita na IPIP omogoča prenos paketov od vira do prejemnika, vendar ne zagotavlja, da bo poslan paket nekega dne dosegel prejemnika, da bo prišel do njega vsaj enkrat in da bo zaporedje paketov prišlo v pravem vrstnem redu. Poleg tega paket lahko vsebuje le omejeno količino podatkov, določeno z velikostjo. MTU.

UDP je le tanek sloj nad IP. Zaradi tega ima iste omejitve. Po drugi strani pa TCP nudi številne funkcionalnosti. Zagotavlja zanesljivo urejeno povezavo med dvema vozliščema s preverjanjem napak. Zato je TCP zelo priročen in se uporablja v številnih drugih protokolih, na primer, v HTTP, FTP in SMTP. Vendar pa imajo vse te funkcije svojo ceno: zamuda.

Da bi razumeli, zakaj te funkcije lahko povzročajo zamudo, moramo razumeti, kako deluje TCP. Ko vozlišče-pošiljatelj pošlje paket vozlišču-prejemniku, pričakuje, da bo prejelo potrdilo (ACK). Če ga po določenem času ne prejme (ker je bil paket ali potrdilo izgubljeno ali iz nekega drugega razloga), pošlje paket znova. Poleg tega TCP zagotavlja prejem paketov v pravem vrstnem redu, zato dokler izgubljeni paket ni prejet, vsi ostali paketi ne morejo biti obdelani, tudi če so že prejeli vozlišče-prejemnik.

Kot verjetno razumete, je zamuda v večigralskih igrah zelo pomembna, še posebej v tako aktivnih zvrsteh, kot je FPS. Zato številne igre uporabljajo UDP z lastnim protokolom.

Lastni protokol, ki temelji na UDP, je lahko učinkovitejši od TCP iz različnih razlogov. Na primer, lahko označuje nekatere pakete kot zanesljive, druge pa kot nezanesljive. Zato ga ne skrbi, ali je nezanesljiv paket dosegel prejemnika. Lahko obravnava tudi več pretočnih podatkov, tako da izgubljen paket v enem toku ne upočasni drugih tokov. Na primer, lahko obstaja tok za vnos igralca in še en tok za klepet. Če se izgubi sporočilo klepeta, ki ni nujno pomembno, ne bo upočasnilo delovanja vnosa, ki je nujno. Ali pa lastni protokol lahko implementira zanesljivost na drugačen način kot TCP, da bi bil bolj učinkovit v pogojih video iger.

Torej, če je TCP tako slab, bomo ustvarili svoj transportni protokol na osnovi UDP?

Vse je nekoliko bolj zapleteno. Čeprav je TCP skoraj suboptimalen za igralne omrežne sisteme, lahko povsem dobro deluje v vaši igri in prihrani vaše dragocene ure. Na primer, zamuda morda ne bo težava za potezno igro ali igro, ki jo je mogoče igrati le v LAN omrežju, kjer so zamude in izgube paketov precej manjše kot na spletu.

V mnogih uspešnih igrah, vključno z World of Warcraft, Minecraft in Terraria, se uporablja TCP. Vendar v večini FPS-jev uporabljajo lastne protokole na osnovi UDP, zato bomo o njih spodaj podrobneje spregovorili.

Če se odločite uporabiti TCP, se prepričajte, da je izklopljen Nagleov algoritem, ker ta bufira pakete pred pošiljanjem, kar pomeni, da povečuje zamudo.

Za več informacij o razlikah med UDP in TCP v kontekstu večigralskih iger lahko preberete članek Glena Fidlerja UDP vs. TCP.

Lastni protokol

Torej, želite ustvariti svoj transportni protokol, a ne veste, kje začeti? Imate srečo, saj je Glen Fidler napisal o tem dva odlična članka. V njih boste našli številne pametne misli.

Prvi članek, Networking for Game Programmers iz leta 2008 je lažji od drugega, Building A Game Network Protocol iz leta 2016. Priporočam, da začnete s starejšim.

Upoštevajte, da je Glen Fidler velik zagovornik uporabe lastnega protokola na osnovi UDP. In po branju njegovih člankov se boste gotovo strinjali z njegovim mnenjem, da ima TCP v videoinstalacijah resne slabosti in boste želeli uresničiti svoj protokol.

Toda če ste začetnik pri delu z omrežji, si naredite uslugo in uporabite TCP ali knjižnico. Za uspešno uresničitev lastnega transportnega protokola se morate prej veliko naučiti.

Omrežne knjižnice

Če potrebujete nekaj bolj učinkovitega kot TCP, vendar se ne želite ukvarjati z uresničevanjem lastnega protokola in se spuščati v mnoge podrobnosti, lahko uporabite omrežno knjižnico. Obstaja jih veliko:

Nisem preizkusil vseh, a raje uporabljam ENet, ker je enostavna za uporabo in zanesljiva. Poleg tega ima jasna navodila in tutorial za začetnike.

Transportni protokol: sklep

Izvedimo povzetek: obstajata dva osnovna transportna protokola: TCP in UDP. TCP ima številne koristne značilnosti: zanesljivost, ohranjanje zaporedja paketov, odkrivanje napak. UDP teh lastnosti nima, vendar pa ima TCP zaradi svoje narave višje zakasnitve, kar je neprimerno za nekatere igre. To pomeni, da za zagotavljanje nizkih zakasnitev lahko ustvarite svoj protokol na osnovi UDP ali uporabite knjižnico, ki implementira transportni protokol na UDP in je prilagojena za večigralske vide igre.

Izbira med TCP, UDP in knjižnico je odvisna od več dejavnikov. Prvič, od potreb igre: ali potrebuje nizke zakasnitve? Drugič, od zahtev protokola aplikacije: ali potrebuje zanesljiv protokol? Kot bomo videli v naslednjem delu, lahko ustvarimo aplikacijski protokol, za katerega bo povsem ustrezal nezanesljiv protokol. Končno je treba upoštevati tudi izkušenost razvijalca omrežnega motorja.

Imam dva nasveta:

  • Kar se da močno abstrahirajte transportni protokol od preostalega dela aplikacije, da ga boste lahko enostavno zamenjali, ne da bi morali prepisovati celoten kodo.
  • Ne ukvarjajte se s prezgodnjo optimizacijo. Če niste strokovnjak za omrežja in niste prepričani, ali potrebujete lasten transportni protokol na osnovi UDP, lahko začnete s TCP ali knjižnicami, ki zagotavljajo zanesljivost, nato pa preizkusite in izmerite zmogljivost. Če se pojavijo težave in ste prepričani, da je razlog v transportnem protokolu, je morda čas za ustvarjanje lastnega transportnega protokola.

Na koncu tega dela vam priporočam, da preberete Introduction to Multiplayer Game Programming Bryana Huka, ki obravnava številne teme, ki so tukaj razpravljene.

Protokol aplikacije

Zdaj, ko lahko izmenjujemo podatke med strankami in strežnikom, je treba odločiti, katere podatke prenašati in v kakšnem formatu.

Klasična shema je ta, da stranke pošiljajo strežniku vhod ali dejanja, strežnik pa strankam pošilja trenutno stanje igre.

Server sends a filtered state with entities that are nearby the player, rather than the complete state. It does this for three reasons. First, the complete state can be too large to transmit at a high frequency. Second, clients are mainly interested in visual and audio data, as most of the game logic is simulated on the game server. Third, in some games, a player should not know certain data, such as the position of an opponent on the other side of the map, because otherwise they might sniff packets and know exactly where to move to eliminate them.

Serializacija

Prvi korak će biti pretvaranje podataka koje želimo poslati (ulaz ili stanje igre) u odgovarajući format za prijenos. Ovaj proces se naziva serializacija.

Na pamet odmah pada ideja da se koristi ljudski čitljiv format, kao što su JSON ili XML. No, to će biti potpuno neučinkovito i nepotrebno zauzeti veliku većinu propusnosti.

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

Za serializaciju podataka može se koristiti biblioteka, na primjer:

Samo se pobrinite da biblioteka stvara prenosive arhive i da se brine o redoslijedu bajtova.

Alternativno rješenje može biti vlastita implementacija, koja nije osobito složena, posebno ako u kodu koristite pristup usmjeren na podatke. Osim toga, omogućit će vam optimizacije koje nisu uvijek moguće kada koristite biblioteku.

Glenn Fidler je napisao dva članka o serializaciji: Reading and Writing Packets in Serialization Strategies.

Komprimiranje

Količina podataka koja se prenosi između klijenata i servera ograničena je propusnošću kanala. Komprimiranje podataka će omogućiti prijenos veće količine podataka u svakom snimku, povećati učestalost ažuriranja ili jednostavno smanjiti zahtjeve za propusnost.

Bit packing

Prva tehnika je bitno pakiranje. Ona se sastoji od korišćenja tačno onog broja bitova koji je potreban za opisivanje željene veličine. Na primer, ako imate enumeraciju koja može imati 16 različitih vrednosti, umesto celog bajta (8 bitova) možete koristiti samo 4 bita.

Glen Fidler objašnjava kako to implementirati u drugom delu članka. Reading and Writing Packets.

Bitno pakiranje posebno dobro funkcioniše sa diskretizacijom, koja će biti tema sledećeg dela.

Diskretizacija

Diskretizacija je tehnika kompresije sa gubicima, koja se sastoji u tome da se za kodiranje veličine koristi samo podskup mogućih vrednosti. Najlakše je implementirati diskretizaciju zaokruživanjem brojeva sa pomičnim zarezom.

Glen Fidler (ponovo!) pokazuje kako primeniti diskretizaciju u praksi u svom članku. Snapshot Compression.

Algoritmi kompresije

Sledeća tehnika biće algoritmi kompresije bez gubitaka.

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

  • Hafmanovo kodiranje sa unapred izračunatim kodom, koji je izuzetno brz i može dati dobre rezultate. Koristio se za kompresiju paketa u mrežnom engine-u Quake3.
  • zlib je algoritam kompresije opšte namene, koji nikada ne povećava obim podataka. Kao što možete videti, ovdeprimenjivan je u mnogim oblastima. Za ažuriranje stanja može biti suvišan. Ali može biti koristan ako treba da šaljete resurse klijentima sa servera, duge tekstove ili reljef.
  • Kopiranje dugih serija je, verovatno, najjednostavniji algoritam kompresije, ali je veoma efikasan za određene tipove podataka i može se koristiti kao korak predobrada pre zlib-a. Posebno je prikladan za kompresiju reljefa koji se sastoji od tilova ili vokseli, u kojima se mnogo susednih elemenata ponavlja.

Delta kompresija

Poslednja metodika kompresije je delta kompresija. Sastoji se u tome da se prenose samo razlike između trenutnog stanja igre i poslednjeg stanja koje je primio klijent.

Prvi put je primenjena u mrežnom engine-u Quake3. Evo dva članka koja objašnjavaju način njene primene:

Glen Fiedler je to uporabljal tudi v drugem delu svojega članka. Snapshot Compression.

Šifriranje

Poleg tega boste morda želeli šifrirati prenos informacij med odjemalci in strežnikom. Obstaja več razlogov za to:

  • zasebnost: sporočila lahko bere le prejemnik, nobena druga oseba, ki izvaja omrežno snufanje, jih ne more prebrati.
  • avtentikacija: oseba, ki želi prevzeti vlogo igralca, mora poznati njegov ključ.
  • preprečevanje goljufij: zlonamernim igralcem bo veliko težje ustvariti svoje lastne pakete za goljufanje, morali bodo reproducirati shemo šifriranja in najti ključ (ki se spremeni ob vsakem povezovanju).

Iskreno priporočam uporabo knjižnice za to. Predlagam uporabo libsodium, saj je še posebej enostavna in ima odlične vajnice. Še posebej zanimiva je vadnica o izmenjavi ključev, ki omogoča generiranje novih ključev pri vsakem novem povezovanju.

Protokol aplikacije: zaključek

S tem zaključujemo s protokolom aplikacije. Menim, da stiskanje ni obvezno, odločitev o njegovi uporabi pa je odvisna le od igre in potrebne pasovne širine kanala. Šifriranje je po mojem mnenju nujno, vendar se mu v prvem prototipu lahko izognemo.

Logika aplikacije

Zdaj lahko posodabljamo stanje v odjemalcu, vendar se lahko srečamo s težavami z zamudo. Igralec, ki vnese ukaz, mora čakati na posodobitev stanja igre s strežnika, da vidi, kakšen vpliv je imel na svet.

Poleg tega je svet popolnoma statičen med dvema posodobitvama. Če je frekvenca posodobitev nizka, so gibi zelo trzavi.

Obstaja več tehnik za zmanjšanje vpliva te težave, o katerih bom govoril v naslednjem razdelku.

Tehnike za izravnavo zamud

Vse tehnike, opisane v tem razdelku, so podrobno obravnavane v seriji Fast-Paced Multiplayer Gabriela Gambette. Iskreno priporočam branje te odlične serije člankov. V njej je tudi interaktivna demonstracija, ki prikazuje, kako te tehnike delujejo v praksi.

Prva tehnika je, da uporabimo rezultat vnosa neposredno, brez čakanja na odgovor strežnika. To se imenuje napovedovanje na strani odjemalca. Vendar, ko stranka prejme posodobitev s strežnika, mora zagotoviti, da je njegova napoved pravilna. Če ni, mora preprosto spremeniti svoje stanje v skladu s prejetim s strežnika, saj je strežnik avtoritativen. Ta tehnika je bila prvič uporabljena v igri Quake. Več o tem lahko preberete v članku Pregled kode Quake Engine Fabiena Sanglara [prevod na Habr-u].

Drugi sklop tehnik se uporablja za izravnavo gibanja drugih entitet med dvema posodobitvama stanja. Obstajata dva načina za reševanje te naloge: interpolacija in ekstrapolacija. V primeru interpolacije se vzameta zadnji dva stanja in prikaže prehod iz enega v drugo. Njena pomanjkljivost je, da povzroča majhno zakasnitev, ker stranka vedno vidi tisto, kar se je dogajalo v preteklosti. Ekstrapolacija pomeni napovedovanje, kje bi zdaj morale biti entitete na podlagi zadnjega stanja, ki ga je prejela stranka. Njena pomanjkljivost je, da če entiteta popolnoma spremeni smer gibanja, lahko pride do velike napake med napovedjo in dejansko pozicijo.

Zadnja in najbolj napredna tehnika, uporabna samo v FPS, je kompenzacija zakasnitve. Pri uporabi kompenzacije zakasnitve strežnik upošteva zakasnitev stranke, ko strelja v cilj. Na primer, če je igralec izvedel headshot na svojem zaslonu, vendar je v resnici njegov cilj zaradi zakasnitve bil na drugem mestu, bi bilo nepravično, da bi igralcu zavrnili pravico do ubijanja zaradi zakasnitve. Zato strežnik vrne čas nazaj, na tisti trenutek, ko je igralec streljal, da bi simuliral, kar je igralec videl na svojem zaslonu, in preveril kolizijo med njegovim strelom in ciljem.

Glenn Fiddler (kot vedno!) je leta 2004 napisal članek Fizika omrežja (2004), v katerem je postavil temelje sinhronizacije simulacije fizike med strežnikom in stranko. Leta 2014 je napisal novo serijo člankov Omrežna fizika, v kateri je opisal druge tehnike za sinhronizacijo simulacije fizike.

Prav tako v wiki podjetja Valve obstajata dva članka, Omrežno večigralstvo Source in Metode kompenzacije zakasnitev pri načrtovanju in optimizaciji protokola v igri klijent/strežnik , v katerih obravnavajo kompenzacijo zakasnitev.

Preprečevanje goljufij

Obstajata dve osnovni tehniki za preprečevanje goljufij.

Prva: zaplet slanja zlonamernih paketa od strane prevaranata. Kao što je gore navedeno, dobar način za to je enkripcija.

Druga: autoritarni server treba da prima samo komande/ulaz/delovanje. Klijent ne bi trebao da ima mogućnost da menja stanje na serveru osim slanjem ulaza. Server bi trebao svaki put, prilikom primanja ulaza, prvo da proveri njegovu validnost pre nego što ga primeni.

Logika aplikacije: zaključak

Preporučujem da implementirate način simulacije velikih kašnjenja i niskih frekvencija osvežavanja kako biste testirali ponašanje vaše igre u lošim uslovima, čak i kada su klijent i server pokrenuti na istom računaru. Ovo značajno olakšava primenu tehnike ublažavanja kašnjenja.

Drugi korisni resursi

Ako želite da istražite druge resurse posvećene mrežnim modelima, možete ih pronaći ovde:

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster