Võrgumudelist algajatele mõeldud mängudes

Võrgumudelist algajatele mõeldud mängudes
Viimased kaks nädalat olen töötanud oma mängu võrgumootori kallal. Enne seda ei teadnud ma mängude võrgundusest üldse midagi, nii et lugesin palju artikleid ja tegin palju katseid, et mõista kõiki mõisteid ja saada oma võrgumootorit kirjutada.

Selles juhendis tahaksin teiega jagada erinevaid kontseptsioone, mida peate enne oma mängumootori kirjutamist õppima, ning parimaid ressursse ja artikleid nende õppimiseks.

Üldiselt on kaks peamist tüüpi võrguarhitektuure: peer-to-peer ja klient-server. Peer-to-peer (p2p) arhitektuuris edastatakse andmeid mis tahes ühendatud mängijate paari vahel, samas kui klient-server arhitektuuris edastatakse andmeid ainult mängijate ja serveri vahel.

Kuigi mõnes mängus kasutatakse endiselt peer-to-peer arhitektuuri, on standardne klient-server: seda on lihtsam rakendada, see nõuab väiksemat kanalilaiust ja muudab petmise eest kaitsmise lihtsamaks. Seetõttu keskendume selles juhendis klient-server arhitektuurile.

Eelkõige oleme huvitatud autoritaarsetest serveritest: sellistes süsteemides on serveril alati õigus. Näiteks kui mängija arvab, et ta on punktis (10, 5) ja server ütleb talle, et ta on punktis (5, 3), peaks klient asendama oma asukoha sellega, millest server teatab, mitte vastupidi. Autoriteetsete serverite kasutamine muudab petturite äratundmise lihtsamaks.

Mänguvõrgusüsteemides on kolm põhikomponenti:

  • Transpordiprotokoll: kuidas andmeid klientide ja serveri vahel edastatakse.
  • Rakendusprotokoll: mida ja millises vormingus edastatakse klientidelt serverisse ja serverist klientidele.
  • Rakendusloogika: kuidas edastatud andmeid kasutatakse klientide ja serveri oleku värskendamiseks.

Väga oluline on mõista iga osa rolli ja nendega seotud raskusi.

Transpordiprotokoll

Esimene samm on valida protokoll andmete edastamiseks serveri ja klientide vahel. Selleks on kaks Interneti-protokolli: TCP и UDP. Kuid saate luua ühe neist põhineva oma transpordiprotokolli või kasutada neid kasutavat teeki.

TCP ja UDP võrdlus

Nii TCP kui ka UDP põhinevad IP. IP võimaldab paketi edastamist allikast vastuvõtjale, kuid see ei garanteeri, et saadetud pakett jõuab varem või hiljem vastuvõtjani, et see jõuab selleni vähemalt korra ja pakettide jada saabub õige järjekord. Lisaks võib pakett sisaldada ainult piiratud andmemahtu, mis on määratud väärtusega MTU.

UDP on vaid õhuke kiht IP peal. Seetõttu on sellel samad piirangud. Seevastu TCP-l on palju funktsioone. See tagab veakontrolliga usaldusväärse järjestatud ühenduse kahe sõlme vahel. Seetõttu on TCP väga mugav ja seda kasutatakse paljudes teistes protokollides, näiteks in HTTP, FTP и SMTP. Kuid kõigil neil funktsioonidel on oma hind: viivitus.

Et mõista, miks need funktsioonid võivad latentsust põhjustada, peame mõistma, kuidas TCP töötab. Kui saatev host saadab paketi vastuvõtvale hostile, eeldab ta saada kinnitust (ACK). Kui teatud aja möödudes ta seda ei saa (sest pakett või kinnitus läks kaduma või muul põhjusel), saadab ta paketi uuesti. Lisaks garanteerib TCP, et paketid võetakse vastu õiges järjekorras, nii et kuni kadunud paketi vastuvõtmiseni ei saa kõiki teisi pakette töödelda, isegi kui vastuvõttev sõlm on need juba vastu võtnud.

Kuid nagu te ilmselt mõistate, on latentsus mitme mängijaga mängudes väga oluline, eriti sellistes aktiivsetes žanrites nagu FPS. Seetõttu kasutavad paljud mängud UDP-d oma protokolliga.

UDP-l põhinev natiivne protokoll võib erinevatel põhjustel olla tõhusam kui TCP. Näiteks võib see märkida mõned paketid usaldusväärseks ja teised ebausaldusväärseks. Seetõttu on tal ükskõik, kas ebausaldusväärne pakett on adressaadini jõudnud. Või võib see töödelda mitut andmevoogu nii, et ühes voos kaotatud pakett ei aeglustaks teisi vooge. Näiteks võib mängija sisestamiseks olla lõim ja vestlussõnumite jaoks teine ​​lõim. Kui vestlussõnum, mis ei ole kiireloomuline andmeside, läheb kaotsi, ei aeglusta see kiireloomulise teabe sisestamist. Või võib patenteeritud protokoll rakendada töökindlust teisiti kui TCP, et olla videomängukeskkonnas tõhusam.

Niisiis, kui TCP on nõme, siis ehitame UDP-l põhineva oma transpordiprotokolli?

Kõik on veidi keerulisem. Kuigi TCP on mängude võrgusüsteemide jaoks peaaegu ebaoptimaalne, võib see teie konkreetse mängu jaoks üsna hästi töötada ja säästa teie väärtuslikku aega. Näiteks ei pruugi latentsus olla probleem käigupõhises mängus või mängus, mida saab mängida ainult LAN-võrkudes, kus latentsus ja pakettide kadu on palju väiksemad kui Internetis.

Paljud edukad mängud, sealhulgas World of Warcraft, Minecraft ja Terraria, kasutavad TCP-d. Enamik FPS-e kasutab siiski oma UDP-põhiseid protokolle, seega räägime neist allpool.

Kui otsustate kasutada TCP-d, veenduge, et see oleks keelatud Nagle algoritm, kuna see puhverdab paketid enne saatmist, mis tähendab, et see suurendab viivitust.

Lisateavet UDP ja TCP erinevuste kohta mitme mängijaga mängude kontekstis leiate Glenn Fiedleri artiklist UDP vs. TCP.

Patenditud protokoll

Nii et soovite luua oma transpordiprotokolli, kuid ei tea, kust alustada? Teil on vedanud, sest Glenn Fiedler kirjutas selle kohta kaks hämmastavat artiklit. Neist leiab palju nutikaid ideid.

Esimene artikkel Võrgustiku loomine mänguprogrammeerijatele 2008, lihtsam kui teine Mängu võrguprotokolli loomine 2016. aasta. Soovitan alustada vanemast.

Pidage meeles, et Glenn Fiedler toetab UDP-l põhinevat oma protokolli. Ja pärast tema artiklite lugemist võtate tõenäoliselt vastu tema arvamuse, et TCP-l on videomängudes tõsiseid puudusi, ja soovite rakendada oma protokolli.

Kuid kui olete võrgunduses uus, tehke endale teene ja kasutage TCP-d või raamatukogu. Oma transpordiprotokolli edukaks rakendamiseks peate eelnevalt palju õppima.

Võrguteegid

Kui vajate midagi tõhusamat kui TCP, kuid ei taha oma protokolli juurutada ja paljudesse üksikasjadesse laskuda, võite kasutada võrguteeki. Neid on palju:

Ma pole neid kõiki proovinud, aga eelistan ENetit, sest seda on lihtne kasutada ja töökindel. Lisaks on sellel selge dokumentatsioon ja õpetus algajatele.

Transpordiprotokolli järeldus

Kokkuvõtteks on kaks peamist transpordiprotokolli: TCP ja UDP. TCP-l on palju kasulikke funktsioone: töökindlus, pakettide järjestuse säilitamine, vigade tuvastamine. UDP-l pole seda kõike, kuid TCP-l on oma olemuselt kõrge latentsusaeg, mis on mõne mängu puhul vastuvõetamatu. See tähendab, et madala latentsusaja tagamiseks saate luua oma UDP-põhise protokolli või kasutada teeki, mis rakendab UDP-s transpordiprotokolli ja on kohandatud mitme mängijaga videomängude jaoks.

Valik TCP, UDP ja raamatukogu vahel sõltub mitmest tegurist. Esiteks mängu vajadustest: kas see vajab madalat latentsust? Teiseks rakendusprotokolli nõuetest: kas see vajab usaldusväärset protokolli? Nagu järgmises osas näeme, on võimalik luua rakendusprotokoll, mille jaoks ebausaldusväärne protokoll on üsna sobiv. Lõpuks peate arvestama ka võrgumootori arendaja kogemustega.

Mul on kaks nõuannet:

  • Abstraheerige transpordiprotokoll ülejäänud rakendusest nii palju kui võimalik, et seda saaks hõlpsasti asendada ilma kogu koodi ümber kirjutamata.
  • Ärge optimeerige üle. Kui te pole võrguekspert ega ole kindel, kas vajate oma UDP-põhist transpordiprotokolli, võite alustada TCP-st või teegist, mis pakub usaldusväärsust, ning seejärel testida ja mõõta jõudlust. Kui teil on probleeme ja olete kindel, et tegemist on transpordiprotokolliga, võib olla aeg luua oma transpordiprotokoll.

Selle osa lõpus soovitan lugeda Sissejuhatus mitme mängijaga mängude programmeerimisse Brian Hook, mis hõlmab paljusid siin käsitletud teemasid.

Rakendusprotokoll

Nüüd, kui saame klientide ja serveri vahel andmeid vahetada, peame otsustama, milliseid andmeid ja millises vormingus edastada.

Klassikaline skeem on see, et kliendid saadavad serverile sisendi või toimingud ja server saadab klientidele mängu hetkeseisu.

Server ei saada pleieri läheduses olevate üksustega mitte täis, vaid filtreeritud olekut. Ta teeb seda kolmel põhjusel. Esiteks võib kogu olek olla kõrgel sagedusel edastamiseks liiga suur. Teiseks huvitavad kliente peamiselt visuaalsed ja heliandmed, kuna suurem osa mänguloogikast on mänguserveris simuleeritud. Kolmandaks, mõnes mängus ei pea mängija teadma teatud andmeid, näiteks vaenlase asukohta teisel pool kaarti, sest vastasel juhul saab ta nuusutada pakette ja teab täpselt, kuhu liikuda, et teda tappa.

Serialiseerimine

Esimene samm on teisendada andmed, mida tahame saata (sisend või mängu olek) edastamiseks sobivasse vormingusse. Seda protsessi nimetatakse serialiseerimine.

Kohe tuleb pähe mõte kasutada inimloetavat vormingut, näiteks JSON või XML. Kuid see on täiesti ebaefektiivne ja võtab suurema osa kanalist tühjaks.

Selle asemel on soovitatav kasutada binaarvormingut, mis on palju kompaktsem. See tähendab, et paketid sisaldavad vaid mõnda baiti. Siin peame probleemiga arvestama baitide järjestus, mis võib erinevatel arvutitel erineda.

Andmete serialiseerimiseks võite kasutada teeki, näiteks:

Lihtsalt veenduge, et raamatukogu loob kaasaskantavaid arhiive ja hoolitseb lõplikkuse eest.

Alternatiivne lahendus oleks see ise juurutada, see pole nii raske, eriti kui kasutate koodis andmekeskset lähenemist. Lisaks võimaldab see teil teha optimeerimisi, mis pole teegi kasutamisel alati võimalikud.

Glenn Fiedler on kirjutanud serialiseerimise kohta kaks artiklit: Pakettide lugemine ja kirjutamine и Serialiseerimisstrateegiad.

kokkusurumine

Klientide ja serveri vahel edastatavate andmete hulk on piiratud kanali ribalaiusega. Andmete tihendamine võimaldab teil iga hetktõmmise korral edastada rohkem andmeid, suurendada värskendussagedust või lihtsalt vähendada ribalaiuse nõudeid.

Natuke pakkimine

Esimene tehnika on bittide pakkimine. See seisneb soovitud väärtuse kirjeldamiseks vajaliku arvu bittide kasutamises. Näiteks kui teil on enum, millel võib olla 16 erinevat väärtust, saate terve baidi (8 bitti) asemel kasutada vaid 4 bitti.

Glenn Fiedler selgitab artikli teises osas, kuidas seda rakendada. Pakettide lugemine ja kirjutamine.

Bittide pakkimine töötab eriti hästi diskretiseerimisega, mis on järgmise jaotise teema.

Proovide võtmine

Proovide võtmine on kadudeta tihendustehnika, mis kasutab väärtuse kodeerimiseks ainult võimalike väärtuste alamhulka. Lihtsaim viis diskretiseerimise rakendamiseks on ujukomaarvude ümardamine.

Glenn Fiedler (jälle!) näitab oma artiklis, kuidas diskretiseerimist praktikas rakendada Snapshot Compression.

Tihendusalgoritmid

Järgmine tehnika on kadudeta tihendusalgoritmid.

Siin on minu arvates kolm kõige huvitavamat algoritmi, mida peate teadma:

  • Huffmani kodeerimine eelarvutatud koodiga, mis on ülikiire ja võib anda häid tulemusi. Seda kasutati Quake3 võrgumootoris pakettide tihendamiseks.
  • zlib on üldotstarbeline tihendusalgoritm, mis ei suurenda kunagi andmemahtu. Kuidas sa näed siin, seda on kasutatud mitmesugustes rakendustes. Olekute värskendamisel võib see olla üleliigne. Kuid see võib olla kasulik, kui teil on vaja saata serverist klientidele varasid, pikki tekste või maastikku.
  • Jooksu pikkuste kopeerimine on ilmselt kõige lihtsam tihendusalgoritm, kuid see on teatud tüüpi andmete puhul väga tõhus ja seda saab kasutada eeltöötlusetapina enne zlib-i. See sobib eriti hästi plaatidest või vokslitest koosneva maastiku tihendamiseks, milles korduvad paljud naaberelemendid.

delta kompressioon

Viimane tihendustehnika on delta tihendus. See seisneb selles, et edastatakse ainult erinevused mängu hetkeseisu ja viimase kliendi poolt vastuvõetud oleku vahel.

Seda kasutati esmakordselt Quake3 võrgumootoris. Siin on kaks artiklit, mis selgitavad selle kasutamist:

Glenn Fiedler kasutas seda ka oma artikli teises osas. Snapshot Compression.

Krüpteerimine

Lisaks peate võib-olla krüpteerima teabe edastamise klientide ja serveri vahel. Sellel on mitu põhjust:

  • Privaatsus/konfidentsiaalsus: sõnumeid saab lugeda ainult adressaat ja ükski teine ​​võrgu nuusutaja ei saa neid lugeda.
  • autentimine: inimene, kes soovib mängida mängija rolli, peab teadma oma võtit.
  • pettuste vältimine: pahatahtlikel mängijatel on palju keerulisem oma petupakette luua, nad peavad krüpteerimisskeemi kopeerima ja leidma võtme (mis muutub iga ühenduse korral).

Soovitan tungivalt kasutada selleks raamatukogu. Soovitan kasutada libnaatrium, sest see on eriti lihtne ja sisaldab suurepäraseid õpetusi. Eriti huvitav on õpetus võtme vahetus, mis võimaldab teil iga uue ühenduse korral luua uusi võtmeid.

Rakendusprotokoll: järeldus

See lõpetab rakendusprotokolli. Usun, et pakkimine on täiesti vabatahtlik ja selle kasutamise otsus sõltub ainult mängust ja vajalikust ribalaiusest. Krüpteerimine on minu arvates kohustuslik, kuid esimeses prototüübis saate ilma selleta hakkama.

Rakenduse loogika

Nüüd saame värskendada kliendi olekut, kuid meil võib tekkida latentsusprobleeme. Mängija peab pärast sisendi tegemist ootama serverilt mängu oleku värskendust, et näha, millist mõju see maailmale on avaldanud.

Pealegi on maailm kahe olekuvärskenduse vahel täiesti staatiline. Kui oleku värskendamise määr on madal, on liigutused väga tõmblevad.

Selle probleemi mõju leevendamiseks on mitu tehnikat ja ma käsitlen neid järgmises jaotises.

Viivitusega silumistehnikad

Kõiki selles jaotises kirjeldatud tehnikaid käsitletakse seerias üksikasjalikult. Kiire tempoga mitmikmäng Gabriel Gambetta. Soovitan soojalt lugeda seda suurepärast artiklite sarja. See sisaldab ka interaktiivset demo, et näha, kuidas need tehnikad praktikas töötavad.

Esimene meetod on sisendtulemuse rakendamine otse, ootamata serverilt vastust. Seda nimetatakse kliendipoolne ennustus. Kui klient aga saab serverilt värskenduse, peab ta kontrollima, kas tema ennustus oli õige. Kui see nii ei ole, siis peab ta lihtsalt oma olekut muutma vastavalt serverilt saadud andmetele, kuna server on autoritaarne. Seda tehnikat kasutati esmakordselt Quake'is. Lisateavet selle kohta saate lugeda artiklist. Quake Engine koodi ülevaade Fabien Sanglars [tõlkimine Habré kohta].

Teist tehnikakomplekti kasutatakse teiste üksuste liikumise sujuvaks kahe olekuvärskenduse vahel. Selle probleemi lahendamiseks on kaks võimalust: interpoleerimine ja ekstrapoleerimine. Interpolatsiooni puhul võetakse kaks viimast olekut ja näidatakse üleminekut ühest teise. Selle puuduseks on see, et see põhjustab väikese osa viivitusest, kuna klient näeb alati seda, mis juhtus minevikus. Ekstrapoleerimise eesmärk on ennustada, kus üksused peaksid nüüd asuma, tuginedes kliendi viimasele saadud olekule. Selle puuduseks on see, et kui olem muudab täielikult liikumissuunda, tekib prognoosi ja tegeliku asukoha vahel suur viga.

Viimane, kõige arenenum tehnika, mis on kasulik ainult FPS-is, on viivituse kompenseerimine. Viivituse kompenseerimise kasutamisel arvestab server sihtmärgi käivitamisel kliendi viivitusi. Näiteks kui mängija sooritas oma ekraanil pealöögi, kuid tegelikkuses oli tema sihtmärk viivituse tõttu teises kohas, siis oleks ebaõiglane keelata mängijalt viivituse tõttu tapmisõigust. Seega kerib server aega tagasi hetkeni, mil mängija tulistas, et simuleerida mängija ekraanil nähtut ja kontrollida, kas tema lasu ja sihtmärgi vahel pole kokkupõrget.

Glenn Fiedler (nagu alati!) kirjutas artikli 2004. aastal Võrgufüüsika (2004), milles ta pani aluse füüsikasimulatsioonide sünkroonimisele serveri ja kliendi vahel. 2014. aastal kirjutas ta uue artiklite sarja võrgufüüsika, milles ta kirjeldas muid füüsikasimulatsioonide sünkroonimise tehnikaid.

Valve vikis on ka kaks artiklit, Allikas Multiplayer Networking и Latentsuse kompenseerimise meetodid kliendi/serveri mängusisese protokolli kujundamisel ja optimeerimisel viivituste hüvitamisega tegelemine.

Pettuste ennetamine

Pettuste ennetamiseks on kaks peamist tehnikat.

Esiteks muudab petturite jaoks pahatahtlike pakettide saatmise raskemaks. Nagu eespool mainitud, on hea viis selle rakendamiseks krüptimine.

Teiseks peaks autoriteetne server vastu võtma ainult käske/sisendeid/toiminguid. Klient ei peaks saama serveri olekut muuta ainult sisendi saatmise teel. Seejärel peab server iga kord, kui ta sisendi saab, enne selle rakendamist selle kehtivust kontrollima.

Rakendusloogika: järeldus

Soovitan teil rakendada viisi, kuidas simuleerida kõrget latentsust ja madalaid värskendussagedusi, et saaksite testida oma mängu käitumist halbades tingimustes isegi siis, kui klient ja server töötavad samas masinas. See lihtsustab oluliselt viivituse silumise tehnikate rakendamist.

Muud kasulikud ressursid

Kui soovite uurida muid võrgumudeli ressursse, leiate need siit:

Allikas: www.habr.com

Lisa kommentaar