Apie tinklo modelį žaidimuose pradedantiesiems

Apie tinklo modelį žaidimuose pradedantiesiems
Paskutines dvi savaites dirbau su savo žaidimo internetiniu varikliu. Prieš tai aš visiškai nieko nežinojau apie tinklų kūrimą žaidimuose, todėl skaičiau daug straipsnių ir atlikau daugybę eksperimentų, kad suprasčiau visas sąvokas ir galėčiau sukurti savo tinklo variklį.

Šiame vadove norėčiau pasidalyti su jumis įvairiomis sąvokomis, kurias turite išmokti prieš rašydami savo žaidimo variklį, taip pat geriausiais ištekliais ir straipsniais, kaip jas išmokti.

Apskritai yra du pagrindiniai tinklo architektūros tipai: lygiavertis ir klientas-serveris. Peer-to-peer (p2p) architektūroje duomenys perduodami tarp bet kokių prijungtų grotuvų porų, o kliento-serverio architektūroje duomenys perduodami tik tarp grotuvų ir serverio.

Nors kai kuriuose žaidimuose vis dar naudojama peer-to-peer architektūra, klientas-serveris yra standartas: ją lengviau įgyvendinti, reikia mažesnio kanalo pločio ir lengviau apsisaugoti nuo sukčiavimo. Todėl šioje pamokoje daugiausia dėmesio skirsime kliento-serverio architektūrai.

Ypač mus domina autoritariniai serveriai: tokiose sistemose serveris visada teisus. Pavyzdžiui, jei žaidėjas mano, kad yra ties koordinatėmis (10, 5), o serveris jam pasako, kad jis yra ties (5, 3), tada klientas turėtų pakeisti savo poziciją ta, kurią pranešė serveris, o ne vice. atvirkščiai. Naudojant autoritetingus serverius lengviau atpažinti sukčius.

Tinklo žaidimų sistemas sudaro trys pagrindiniai komponentai:

  • Transporto protokolas: kaip duomenys perduodami tarp klientų ir serverio.
  • Taikymo protokolas: kas ir kokiu formatu perduodama iš klientų į serverį ir iš serverio į klientus.
  • Taikymo logika: kaip perduoti duomenys naudojami klientų ir serverio būsenai atnaujinti.

Labai svarbu suprasti kiekvienos dalies vaidmenį ir su jomis susijusius iššūkius.

Transporto protokolas

Pirmas žingsnis yra pasirinkti protokolą duomenų perdavimui tarp serverio ir klientų. Tam yra du interneto protokolai: TCP и UDP. Bet jūs galite sukurti savo transportavimo protokolą pagal vieną iš jų arba naudoti juos naudojančią biblioteką.

TCP ir UDP palyginimas

Tiek TCP, tiek UDP yra pagrįsti IP. IP leidžia perduoti paketą iš šaltinio gavėjui, tačiau negarantuoja, kad išsiųstas paketas anksčiau ar vėliau pasieks gavėją, kad jį pasieks bent vieną kartą ir kad paketų seka ateis teisinga įsakymas. Be to, pakete gali būti tik ribotas duomenų kiekis, kurį nurodo vertė MTU.

UDP yra tik plonas sluoksnis ant IP. Todėl jis turi tuos pačius apribojimus. Priešingai, TCP turi daug funkcijų. Tai užtikrina patikimą, tvarkingą ryšį tarp dviejų mazgų su klaidų tikrinimu. Vadinasi, TCP yra labai patogus ir naudojamas daugelyje kitų protokolų, pvz. HTTP, FTP и SMTP. Tačiau visos šios funkcijos turi savo kainą: delsimas.

Norėdami suprasti, kodėl šios funkcijos gali sukelti delsą, turime suprasti, kaip veikia TCP. Kai siunčiantis mazgas perduoda paketą priimančiam mazgui, jis tikisi gauti patvirtinimą (ACK). Jei po tam tikro laiko jo negauna (dėl to, kad paketas ar patvirtinimas buvo prarastas ar dėl kokios nors kitos priežasties), tada siunčia paketą iš naujo. Be to, TCP garantuoja, kad paketai bus gauti teisinga tvarka, taigi, kol negaunamas prarastas paketas, visi kiti paketai negali būti apdoroti, net jei juos jau gavo priimantis kompiuteris.

Tačiau, kaip tikriausiai galite įsivaizduoti, kelių žaidėjų žaidimų delsa yra labai svarbi, ypač veiksmo kupinuose žanruose, tokiuose kaip FPS. Štai kodėl daugelis žaidimų naudoja UDP su savo protokolu.

Vietinis UDP protokolas dėl įvairių priežasčių gali būti efektyvesnis nei TCP. Pavyzdžiui, kai kuriuos paketus jis gali pažymėti kaip patikimus, o kitus kaip nepatikimus. Todėl jai nesvarbu, ar nepatikimas paketas pasieks gavėją. Arba jis gali apdoroti kelis duomenų srautus, kad prarastas paketas viename sraute nesulėtėtų likusių srautų. Pavyzdžiui, gali būti grotuvo įvesties gija ir kita pokalbių pranešimų gija. Jei prarandamas neskubus pokalbio pranešimas, tai nesulėtins įvesties, kuri yra skubi. Arba patentuotas protokolas gali įgyvendinti patikimumą kitaip nei TCP, kad būtų efektyvesnis vaizdo žaidimų aplinkoje.

Taigi, jei TCP taip nusišneka, tada sukursime savo transportavimo protokolą, pagrįstą UDP?

Tai šiek tiek sudėtingiau. Nors TCP yra beveik neoptimalus žaidimų tinklo sistemoms, jis gali puikiai veikti jūsų konkrečiam žaidimui ir sutaupyti brangaus laiko. Pvz., delsa gali būti ne problema, kai žaidžiama eilės tvarka arba žaidimai, kuriuos galima žaisti tik LAN tinkluose, kur delsa ir paketų praradimas yra daug mažesni nei internete.

Daugelis sėkmingų žaidimų, įskaitant World of Warcraft, Minecraft ir Terraria, naudoja TCP. Tačiau dauguma FPS naudoja savo UDP pagrįstus protokolus, todėl toliau apie juos kalbėsime daugiau.

Jei nuspręsite naudoti TCP, įsitikinkite, kad jis išjungtas Naglės algoritmas, nes jis saugo paketus prieš siųsdamas, o tai reiškia, kad padidina delsą.

Norėdami sužinoti daugiau apie UDP ir TCP skirtumus kelių žaidėjų žaidimų kontekste, galite perskaityti Glenno Fiedlerio straipsnį UDP vs. TCP.

Savas protokolas

Taigi norite sukurti savo transporto protokolą, bet nežinote, nuo ko pradėti? Jums pasisekė, nes Glennas Fiedleris apie tai parašė du nuostabius straipsnius. Juose rasite daug protingų minčių.

Pirmas straipsnis Tinklų kūrimas žaidimų programuotojams 2008 m., lengvesnis nei antrasis, Žaidimo tinklo protokolo kūrimas 2016 m. Rekomenduoju pradėti nuo senesnio.

Atminkite, kad Glennas Fiedleris yra didelis tinkinto protokolo, pagrįsto UDP, naudojimo šalininkas. O perskaitę jo straipsnius tikriausiai priimsite jo nuomonę, kad TCP turi rimtų vaizdo žaidimų trūkumų, ir norėsite įdiegti savo protokolą.

Bet jei nesate naujokas, padarykite sau paslaugą ir naudokite TCP arba biblioteką. Norėdami sėkmingai įgyvendinti savo transporto protokolą, iš anksto turite daug išmokti.

Tinklo bibliotekos

Jei jums reikia kažko efektyvesnio nei TCP, bet nenorite diegti savo protokolo ir gilintis į daug detalių, galite naudoti tinklo biblioteką. Jų yra daug:

Išbandžiau ne visus, bet man labiau patinka ENet, nes juo paprasta naudotis ir jis patikimas. Be to, jame yra aiški dokumentacija ir pamoka pradedantiesiems.

Transporto protokolas: Išvada

Apibendrinant: yra du pagrindiniai transportavimo protokolai: TCP ir UDP. TCP turi daug naudingų funkcijų: patikimumą, paketų užsakymų išsaugojimą, klaidų aptikimą. UDP viso to neturi, tačiau TCP dėl savo prigimties padidino delsą, o tai kai kuriems žaidimams yra nepriimtina. Tai yra, norėdami užtikrinti mažą delsą, galite sukurti savo protokolą, pagrįstą UDP, arba naudoti biblioteką, kuri įgyvendina UDP perdavimo protokolą ir yra pritaikyta kelių žaidėjų vaizdo žaidimams.

Pasirinkimas tarp TCP, UDP ir bibliotekos priklauso nuo kelių veiksnių. Pirma, iš žaidimo poreikių: ar jam reikia mažo delsos? Antra, iš taikomųjų programų protokolo reikalavimų: ar reikia patikimo protokolo? Kaip pamatysime kitoje dalyje, galima sukurti aplikacijos protokolą, kuriam visai tinka nepatikimas protokolas. Galiausiai taip pat turite atsižvelgti į tinklo variklio kūrėjo patirtį.

Turiu du patarimus:

  • Kiek įmanoma atitraukite transportavimo protokolą iš likusios programos, kad jį būtų galima lengvai pakeisti neperrašant viso kodo.
  • Per daug neoptimizuokite. Jei nesate tinklo ekspertas ir nesate tikri, ar jums reikia tinkinto UDP pagrįsto perdavimo protokolo, galite pradėti nuo TCP arba bibliotekos, kuri užtikrina patikimumą, o tada išbandyti ir įvertinti našumą. Jei kyla problemų ir esate tikri, kad priežastis yra transportavimo protokolas, gali būti laikas sukurti savo transportavimo protokolą.

Šios dalies pabaigoje rekomenduoju perskaityti Įvadas į kelių žaidėjų žaidimų programavimą Brian Hook, kuris apima daugelį čia aptartų temų.

Taikymo protokolas

Dabar, kai galime keistis duomenimis tarp klientų ir serverio, turime nuspręsti, kokius duomenis ir kokiu formatu perduoti.

Klasikinė schema yra tokia, kad klientai siunčia įvestį arba veiksmus į serverį, o serveris siunčia klientams esamą žaidimo būseną.

Serveris siunčia ne visą būseną, o filtruotą būseną su objektais, esančiais šalia grotuvo. Jis tai daro dėl trijų priežasčių. Pirma, visa būsena gali būti per didelė, kad būtų galima perduoti aukštu dažniu. Antra, klientus daugiausia domina vaizdo ir garso duomenys, nes didžioji žaidimo logikos dalis yra imituojama žaidimų serveryje. Trečia, kai kuriuose žaidimuose žaidėjui nereikia žinoti tam tikrų duomenų, pavyzdžiui, priešo padėties kitoje žemėlapio pusėje, kitaip jis gali uostyti paketus ir tiksliai žinoti, kur judėti, kad jį nužudytų.

Serializavimas

Pirmiausia reikia konvertuoti norimus siųsti duomenis (įvesties arba žaidimo būseną) į perdavimui tinkamą formatą. Šis procesas vadinamas serializavimas.

Iš karto kyla mintis, kad reikia naudoti žmonėms skaitomą formatą, pvz., JSON arba XML. Tačiau tai bus visiškai neveiksminga ir iššvaistys didžiąją kanalo dalį.

Vietoj to rekomenduojama naudoti dvejetainį formatą, kuris yra daug kompaktiškesnis. Tai reiškia, kad paketuose bus tik keli baitai. Čia reikia apsvarstyti problemą baitų tvarka, kurie skirtinguose kompiuteriuose gali skirtis.

Norėdami suskirstyti duomenis, galite naudoti biblioteką, pavyzdžiui:

Tiesiog įsitikinkite, kad biblioteka kuria nešiojamus archyvus ir rūpinasi baigtumu.

Alternatyvus sprendimas yra jį įdiegti patiems; tai nėra ypač sunku, ypač jei naudojate į duomenis orientuotą kodą. Be to, tai leis atlikti optimizavimus, kurie ne visada įmanomi naudojant biblioteką.

Glennas Fiedleris parašė du straipsnius apie serializavimą: Skaitymo ir rašymo paketai и Serializacijos strategijos.

Suspaudimas

Duomenų, perduodamų tarp klientų ir serverio, kiekį riboja kanalo pralaidumas. Duomenų suspaudimas leis jums perkelti daugiau duomenų kiekvienoje momentinėje nuotraukoje, padidinti atnaujinimo dažnį arba tiesiog sumažinti kanalo reikalavimus.

Bitelių pakuotė

Pirmoji technika yra bitų pakavimas. Jį sudaro tiksliai tiek bitų, kiek reikia norint apibūdinti norimą reikšmę. Pavyzdžiui, jei turite enumą, kuris gali turėti 16 skirtingų reikšmių, tada vietoj viso baito (8 bitai) galite naudoti tik 4 bitus.

Glennas Fiedleris paaiškina, kaip tai įgyvendinti antroje straipsnio dalyje Skaitymo ir rašymo paketai.

Antgalių supakavimas ypač gerai veikia su mėginių ėmimu, kuris bus kito skyriaus tema.

Mėginių ėmimas

Mėginių ėmimas yra nuostolingas glaudinimo metodas, kuris naudoja tik galimų reikšmių poaibį reikšmei koduoti. Lengviausias būdas diskretizuoti yra apvalinti slankiojo kablelio skaičius.

Glennas Fiedleris (vėl!) savo straipsnyje parodo, kaip atranką pritaikyti praktiškai Momentinės nuotraukos suspaudimas.

Suspaudimo algoritmai

Kitas metodas bus be nuostolių suspaudimo algoritmai.

Čia, mano nuomone, yra trys įdomiausi algoritmai, kuriuos reikia žinoti:

  • Huffmano kodavimas su iš anksto apskaičiuotu kodu, kuris yra itin greitas ir gali duoti gerų rezultatų. Jis buvo naudojamas suspausti paketus Quake3 tinklo variklyje.
  • zlib yra bendros paskirties glaudinimo algoritmas, kuris niekada nepadidina duomenų kiekio. Kaip tu gali pamatyti čia, jis buvo naudojamas įvairiose srityse. Jis gali būti nereikalingas atnaujinant būsenas. Tačiau tai gali būti naudinga, jei klientams iš serverio reikia siųsti išteklius, ilgus tekstus ar vietovę.
  • Nukopijuojami bėgimo ilgiai - Tai turbūt paprasčiausias glaudinimo algoritmas, tačiau jis labai efektyvus tam tikro tipo duomenims ir gali būti naudojamas kaip išankstinio apdorojimo veiksmas prieš zlib. Jis ypač tinka suspausti reljefą, sudarytą iš plytelių arba vokselių, kuriuose kartojasi daug gretimų elementų.

Delta suspaudimas

Paskutinis suspaudimo būdas yra delta suspaudimas. Jis susideda iš to, kad perduodami tik skirtumai tarp dabartinės žaidimo būsenos ir paskutinės kliento gautos būsenos.

Pirmą kartą jis buvo panaudotas Quake3 tinklo variklyje. Štai du straipsniai, paaiškinantys, kaip jį naudoti:

Glennas Fiedleris taip pat naudojo jį antroje savo straipsnio dalyje Momentinės nuotraukos suspaudimas.

Šifravimas

Be to, gali tekti užšifruoti informacijos perdavimą tarp klientų ir serverio. Tam yra keletas priežasčių:

  • privatumas/konfidencialumas: žinutes gali skaityti tik gavėjas, ir joks kitas tinkle besijaučiantis asmuo negalės jų perskaityti.
  • autentifikavimas: žmogus, norintis atlikti žaidėjo vaidmenį, turi žinoti savo raktą.
  • Apgaulės prevencija: Piktybiškiems žaidėjams bus daug sunkiau susikurti savo cheat paketus, jie turės atkurti šifravimo schemą ir rasti raktą (kuris keičiasi su kiekvienu prisijungimu).

Tam primygtinai rekomenduoju naudoti biblioteką. Siūlau naudoti libsodiumas, nes jis ypač paprastas ir turi puikių vadovėlių. Ypač įdomi yra pamoka raktų keitimas, kuri leidžia generuoti naujus raktus su kiekvienu nauju ryšiu.

Taikymo protokolas: Išvada

Tai užbaigia mūsų taikymo protokolą. Manau, kad suspaudimas yra visiškai neprivalomas ir sprendimas jį naudoti priklauso tik nuo žaidimo ir reikiamo pralaidumo. Šifravimas, mano nuomone, yra privalomas, tačiau pirmame prototipe galite apsieiti ir be jo.

Taikymo logika

Dabar galime atnaujinti kliento būseną, tačiau gali kilti delsos problemų. Žaidėjas, atlikęs įvestį, turi palaukti, kol žaidimo būsena bus atnaujinta iš serverio, kad pamatytų, kokią įtaką ji padarė pasauliui.

Be to, tarp dviejų būsenų atnaujinimų pasaulis yra visiškai statiškas. Jei būsenos atnaujinimo dažnis mažas, judesiai bus labai trūkčiojantys.

Yra keletas būdų, kaip sumažinti šios problemos poveikį, ir aš juos aptarsiu kitame skyriuje.

Latencijos išlyginimo metodai

Visi šiame skyriuje aprašyti būdai išsamiai aptariami serijoje Greito tempo kelių žaidėjų žaidimas Gabrielis Gambetta. Labai rekomenduoju perskaityti šią puikią straipsnių seriją. Jame taip pat yra interaktyvi demonstracinė versija, leidžianti pamatyti, kaip šie metodai veikia praktiškai.

Pirmasis būdas yra tiesiogiai pritaikyti įvesties rezultatą, nelaukiant atsakymo iš serverio. Tai vadinama kliento pusės prognozavimas. Tačiau kai klientas gauna atnaujinimą iš serverio, jis turi patikrinti, ar jo prognozė buvo teisinga. Jei taip nėra, jam tereikia pakeisti būseną pagal tai, ką gavo iš serverio, nes serveris yra autoritarinis. Ši technika pirmą kartą buvo panaudota Quake. Daugiau apie tai galite perskaityti straipsnyje Quake variklio kodo peržiūra Fabienas Sanglarsas [vertimas apie Habré].

Antrasis metodų rinkinys naudojamas sklandžiai kitų objektų judėjimui tarp dviejų būsenų atnaujinimų. Yra du būdai išspręsti šią problemą: interpoliacija ir ekstrapoliacija. Interpoliacijos atveju imamos paskutinės dvi būsenos ir parodomas perėjimas iš vienos į kitą. Jo trūkumas yra tai, kad tai sukelia nedidelį vėlavimą, nes klientas visada mato, kas nutiko praeityje. Ekstrapoliacija yra numatymas, kur dabar turėtų būti objektai, remiantis paskutine kliento gauta būsena. Jo trūkumas yra tas, kad jei subjektas visiškai pakeičia judėjimo kryptį, tada tarp prognozės ir faktinės padėties bus didelė paklaida.

Naujausia, pažangiausia technika, naudinga tik FPS atsilikimo kompensacija. Naudodamas atsilikimo kompensavimą, serveris atsižvelgia į kliento vėlavimus, kai jis šaudo į taikinį. Pavyzdžiui, jei žaidėjas šovė į galvą savo ekrane, bet iš tikrųjų jo taikinys buvo kitoje vietoje dėl delsimo, būtų nesąžininga atimti žaidėjui teisę žudyti dėl delsimo. Todėl serveris atsuka laiką atgal iki momento, kai žaidėjas šovė, kad imituotų tai, ką žaidėjas matė savo ekrane, ir patikrintų, ar jo šūvis ir taikinys nesusidūrė.

Glennas Fiedleris (kaip visada!) parašė straipsnį 2004 m Tinklo fizika (2004 m.), kuriame jis padėjo pagrindą serverio ir kliento fizikos modeliavimų sinchronizavimui. 2014 m. jis parašė naują straipsnių ciklą Tinklo fizika, kuriame aprašyti kiti fizikos modeliavimo sinchronizavimo būdai.

Valve wiki taip pat yra du straipsniai, Šaltinis kelių žaidėjų tinklas и Delsos kompensavimo metodai kliento / serverio žaidimo protokolo kūrime ir optimizavime kurios laiko kompensaciją už vėlavimą.

Apgaulės prevencija

Yra du pagrindiniai būdai apsisaugoti nuo sukčiavimo.

Pirma: sukčiams apsunkinti kenkėjiškų paketų siuntimą. Kaip minėta aukščiau, geras būdas tai įgyvendinti yra šifravimas.

Antra: autoritarinis serveris turėtų gauti tik komandas / įvestį / veiksmus. Klientas neturėtų turėti galimybės pakeisti būsenos serveryje kitaip, nei siųsdamas įvestį. Tada kiekvieną kartą, kai serveris gauna įvestį, jis turi patikrinti, ar jis galioja, prieš jį naudodamas.

Taikymo logika: išvada

Rekomenduoju įdiegti būdą, kaip imituoti dideles delsas ir mažus atnaujinimo dažnius, kad galėtumėte išbandyti žaidimo veikimą prastomis sąlygomis, net kai klientas ir serveris veikia tame pačiame kompiuteryje. Tai labai supaprastins vėlavimo išlyginimo metodų įgyvendinimą.

Kiti naudingi ištekliai

Jei norite ištirti kitus tinklo modelių išteklius, juos rasite čia:

Šaltinis: www.habr.com

Добавить комментарий