Oor die netwerkmodel in speletjies vir beginners

Oor die netwerkmodel in speletjies vir beginners
Die afgelope twee weke werk ek aan die netwerkenjin vir my speletjie. Voor dit het ek glad niks geweet van netwerkwerk in speletjies nie, so ek het baie artikels gelees en baie eksperimente gedoen om al die konsepte te verstaan ​​en my eie netwerkenjin te kan skryf.

In hierdie gids deel ek graag die verskillende konsepte wat jy moet leer voordat jy jou eie speletjie-enjin skryf, asook die beste hulpbronne en artikels om dit te leer, met jou te deel.

Oor die algemeen is daar twee hooftipes netwerkargitekture: eweknie-tot-eweknie en kliënt-bediener. In 'n eweknie-argitektuur (p2p) word data tussen enige paar gekoppelde spelers oorgedra, terwyl in 'n kliënt-bediener-argitektuur data slegs tussen die spelers en die bediener oorgedra word.

Alhoewel die eweknie-argitektuur steeds in sommige speletjies gebruik word, is kliënt-bediener die standaard: dit is makliker om te implementeer, vereis 'n kleiner kanaalwydte en maak dit makliker om teen bedrog te beskerm. Daarom sal ons in hierdie gids fokus op die kliënt-bediener-argitektuur.

In die besonder stel ons die meeste belang in outoritêre bedieners: in sulke stelsels is die bediener altyd reg. Byvoorbeeld, as die speler dink hy is by (10, 5) en die bediener sê vir hom hy is by (5, 3), dan moet die kliënt sy posisie vervang met die een wat die bediener rapporteer, nie andersom nie. Die gebruik van gesaghebbende bedieners maak dit makliker om bedrieërs te herken.

Daar is drie hoofkomponente in speletjienetwerkstelsels:

  • Vervoerprotokol: hoe data tussen kliënte en die bediener oorgedra word.
  • Toepassingsprotokol: wat word van kliënte na die bediener en van die bediener na kliënte oorgedra, en in watter formaat.
  • Toepassingslogika: hoe die oorgedrade data gebruik word om die toestand van kliënte en die bediener op te dateer.

Dit is baie belangrik om die rol van elke deel en die probleme wat daarmee gepaardgaan, te verstaan.

Vervoerprotokol

Die eerste stap is om 'n protokol te kies vir die vervoer van data tussen die bediener en kliënte. Daar is twee internetprotokolle hiervoor: TCP и UDP. Maar jy kan jou eie vervoerprotokol op grond van een daarvan skep of 'n biblioteek gebruik wat dit gebruik.

Vergelyking van TCP en UDP

Beide TCP en UDP is gebaseer op IP. IP laat toe dat 'n pakkie van 'n bron na 'n ontvanger oorgedra word, maar dit waarborg nie dat die gestuurde pakkie vroeër of later die ontvanger sal bereik nie, dat dit ten minste een keer daarby sal uitkom en dat die volgorde van pakkies in sal aankom. die regte volgorde. Boonop kan 'n pakkie slegs 'n beperkte datagrootte bevat, gegee deur die waarde MTU.

UDP is net 'n dun laag bo-op IP. Dit het dus dieselfde beperkings. Daarteenoor het TCP baie kenmerke. Dit bied 'n betroubare geordende verbinding tussen twee nodusse met foutkontrole. Daarom is TCP baie gerieflik en word dit in baie ander protokolle gebruik, byvoorbeeld in HTTP, FTP и SMTP. Maar al hierdie kenmerke kom teen 'n prys: vertraging.

Om te verstaan ​​waarom hierdie funksies latensie kan veroorsaak, moet ons verstaan ​​hoe TCP werk. Wanneer die sendende gasheer 'n pakkie na die ontvangende gasheer stuur, verwag dit om 'n erkenning (ACK) te ontvang. As dit na 'n sekere tyd dit nie ontvang nie (omdat die pakkie of bevestiging verlore is, of om een ​​of ander rede), dan stuur dit die pakkie weer. Boonop waarborg TCP dat pakkies in die korrekte volgorde ontvang word, dus totdat 'n verlore pakkie ontvang is, kan alle ander pakkies nie verwerk word nie, selfs al is dit reeds deur die ontvangende nodus ontvang.

Maar soos u waarskynlik verstaan, is latency in multiplayer-speletjies baie belangrik, veral in sulke aktiewe genres soos FPS. Daarom gebruik baie speletjies UDP met sy eie protokol.

'n Inheemse protokol gebaseer op UDP kan om verskeie redes doeltreffender wees as TCP. Dit kan byvoorbeeld sommige pakkette as vertrou merk en ander as onbetroubaar. Daarom gee hy nie om of die onbetroubare pakkie die ontvanger bereik het nie. Of dit kan veelvuldige datastrome verwerk sodat 'n pakkie wat in een stroom verloor word nie ander strome vertraag nie. Daar kan byvoorbeeld 'n draad wees vir spelerinvoer en 'n ander draad vir kletsboodskappe. As 'n kletsboodskap wat nie dringende data is nie, verlore gaan, sal dit nie die invoer wat dringend is vertraag nie. Of 'n eie protokol kan betroubaarheid anders as TCP implementeer om meer doeltreffend in 'n videospeletjie-omgewing te wees.

Dus, as TCP suig, sal ons ons eie vervoerprotokol op grond van UDP skep?

Alles is 'n bietjie meer ingewikkeld. Alhoewel TCP amper sub-optimaal is vir speletjienetwerkstelsels, kan dit redelik goed werk vir jou spesifieke speletjie en jou waardevolle tyd bespaar. Byvoorbeeld, latency is dalk nie 'n probleem vir 'n beurt-gebaseerde speletjie of 'n speletjie wat net op LAN-netwerke gespeel kan word, waar latency en pakkieverlies baie minder is as op die internet.

Baie suksesvolle speletjies, insluitend World of Warcraft, Minecraft en Terraria, gebruik TCP. Die meeste FPS'e gebruik egter hul eie UDP-gebaseerde protokolle, so ons sal hieronder meer daaroor praat.

As jy kies om TCP te gebruik, maak seker dat dit gedeaktiveer is Nagle se algoritme, want dit buffer pakkies voordat dit gestuur word, wat beteken dat dit die vertraging verhoog.

Om meer te wete te kom oor die verskille tussen UDP en TCP in die konteks van multiplayer-speletjies, sien Glenn Fiedler se artikel UDP vs. TCP.

Eie protokol

So jy wil jou eie vervoerprotokol skep, maar weet nie waar om te begin nie? Jy is gelukkig, want Glenn Fiedler het twee wonderlike artikels daaroor geskryf. Jy sal baie slim idees daarin vind.

Eerste artikel Netwerk vir spelprogrammeerders 2008, makliker as die tweede Bou 'n speletjienetwerkprotokol 2016. Ek beveel aan dat jy met die ouer een begin.

Wees bewus daarvan dat Glenn Fiedler 'n groot voorstander is van die gebruik van u eie protokol gebaseer op UDP. En nadat jy sy artikels gelees het, sal jy waarskynlik sy mening aanvaar dat TCP ernstige nadele in videospeletjies het, en jy sal jou eie protokol wil implementeer.

Maar as jy nuut is met netwerke, doen jouself 'n guns en gebruik TCP of 'n biblioteek. Om jou eie vervoerprotokol suksesvol te implementeer, moet jy vooraf baie leer.

Netwerk biblioteke

As jy iets meer doeltreffend as TCP nodig het, maar nie die moeite wil doen om jou eie protokol te implementeer en in baie besonderhede in te gaan nie, kan jy die netbiblioteek gebruik. Daar is baie van hulle:

Ek het hulle nie almal probeer nie, maar ek verkies ENet omdat dit maklik is om te gebruik en betroubaar is. Daarbenewens het dit duidelike dokumentasie en 'n handleiding vir beginners.

Vervoerprotokol Gevolgtrekking

Om op te som, daar is twee hoofvervoerprotokolle: TCP en UDP. TCP het baie nuttige kenmerke: betroubaarheid, behoud van pakkievolgorde, foutopsporing. UDP het nie dit alles nie, maar TCP, uit die aard van die saak, het 'n hoë latensie wat vir sommige speletjies onaanvaarbaar is. Dit wil sê, om 'n lae vertraging te verseker, kan jy jou eie protokol op grond van UDP skep of 'n biblioteek gebruik wat die vervoerprotokol op UDP implementeer en aangepas is vir multispeler-videospeletjies.

Die keuse tussen TCP, UDP en die biblioteek hang van verskeie faktore af. Eerstens, uit die behoeftes van die spel: benodig dit lae latensie? Tweedens, uit die vereistes van die toepassingsprotokol: benodig dit 'n betroubare protokol? Soos ons in die volgende deel sal sien, is dit moontlik om 'n toepassingsprotokol te skep waarvoor 'n onbetroubare protokol baie geskik is. Ten slotte moet u ook die ervaring van die netwerkenjin-ontwikkelaar in ag neem.

Ek het twee wenke:

  • Onttrek die vervoerprotokol soveel as moontlik uit die res van die toepassing sodat dit maklik vervang kan word sonder om al die kode te herskryf.
  • Moenie ooroptimaliseer nie. As jy nie 'n netwerkkenner is nie en nie seker is of jy jou eie UDP-gebaseerde vervoerprotokol nodig het nie, kan jy begin met TCP of 'n biblioteek wat betroubaarheid verskaf, en dan prestasie toets en meet. As jy probleme ondervind en jy is seker dit is 'n vervoerprotokol, dan is dit dalk tyd om jou eie vervoerprotokol te skep.

Aan die einde van hierdie deel beveel ek aan dat jy lees Inleiding tot Multiplayer Game Programmering Brian Hook, wat baie van die onderwerpe dek wat hier bespreek word.

Toepassingsprotokol

Noudat ons data tussen kliënte en die bediener kan uitruil, moet ons besluit watter data om oor te dra en in watter formaat.

Die klassieke skema is dat kliënte insette of aksies na die bediener stuur, en die bediener stuur die huidige speltoestand na die kliënte.

Die bediener stuur nie die volle nie, maar die gefiltreerde toestand met entiteite wat naby die speler is. Hy doen dit om drie redes. Eerstens kan die totale toestand te groot wees om teen 'n hoë frekwensie uit te saai. Tweedens stel kliënte hoofsaaklik belang in visuele en oudiodata, omdat die meeste van die spellogika op die spelbediener gesimuleer word. Derdens, in sommige speletjies hoef die speler nie sekere data te ken nie, soos die posisie van die vyand aan die ander kant van die kaart, want anders kan hy pakkies snuif en presies weet waar om te beweeg om hom dood te maak.

Serialisering

Die eerste stap is om die data wat ons wil stuur (invoer of speltoestand) om te skakel na 'n formaat wat geskik is vir oordrag. Hierdie proses word genoem serialisering.

Die idee kom dadelik by my op om 'n mens-leesbare formaat te gebruik, soos JSON of XML. Maar dit sal heeltemal ondoeltreffend wees en sal die meeste van die kanaal verniet opneem.

In plaas daarvan word dit aanbeveel om die binêre formaat te gebruik, wat baie meer kompak is. Dit wil sê, die pakkies sal slegs 'n paar grepe bevat. Hier moet ons die probleem in ag neem byte volgorde, wat op verskillende rekenaars kan verskil.

Om data te serialiseer, kan jy 'n biblioteek gebruik, byvoorbeeld:

Maak net seker dat die biblioteek draagbare argiewe skep en sorg vir endianness.

'n Alternatiewe oplossing sou wees om dit self te implementeer, dit is nie so moeilik nie, veral as jy 'n datasentriese benadering in jou kode gebruik. Daarbenewens sal dit jou toelaat om optimaliserings uit te voer wat nie altyd moontlik is wanneer jy die biblioteek gebruik nie.

Glenn Fiedler het twee artikels oor serialisering geskryf: Lees en skryf pakkies и Serialiseringstrategieë.

Kompressie

Die hoeveelheid data wat tussen kliënte en die bediener oorgedra word, word beperk deur die bandwydte van die kanaal. Datakompressie sal jou toelaat om meer data in elke momentopname oor te dra, die verversingstempo te verhoog of bloot die bandwydtevereistes te verminder.

Bietjie inpak

Die eerste tegniek is bietjie verpakking. Dit bestaan ​​uit die gebruik van presies die aantal bisse wat nodig is om die verlangde waarde te beskryf. Byvoorbeeld, as jy 'n enum het wat 16 verskillende waardes kan hê, dan kan jy in plaas van 'n hele greep (8 bisse), net 4 bisse gebruik.

Glenn Fiedler verduidelik hoe om dit te implementeer in die tweede deel van die artikel. Lees en skryf pakkies.

Bietjieverpakking werk besonder goed met diskretisering, wat die onderwerp van die volgende afdeling sal wees.

Monsterneming

Monsterneming is 'n verliesryke kompressietegniek wat slegs 'n subset van moontlike waardes gebruik om 'n waarde te enkodeer. Die maklikste manier om diskretisering te implementeer is deur swaaipuntgetalle af te rond.

Glenn Fiedler wys (weereens!) hoe om diskretisering in die praktyk toe te pas in sy artikel Snapshot kompressie.

Kompressie-algoritmes

Die volgende tegniek sal verlieslose kompressie-algoritmes wees.

Hier is na my mening die drie interessantste algoritmes wat u moet ken:

  • Huffman-kodering met voorafberekende kode, wat uiters vinnig is en goeie resultate kan lewer. Dit is gebruik om pakkies in die Quake3-netwerkenjin saam te pers.
  • zlib is 'n algemene kompressie-algoritme wat nooit die hoeveelheid data verhoog nie. Hoe kan jy sien hier, is dit in 'n verskeidenheid toepassings gebruik. Vir die opdatering van state kan dit oorbodig wees. Maar dit kan handig te pas kom as jy bates, lang tekste of terrein vanaf die bediener na kliënte moet stuur.
  • Kopieer looplengtes is waarskynlik die eenvoudigste kompressie-algoritme, maar dit is baie doeltreffend vir sekere tipes data, en kan as 'n voorverwerkingstap voor zlib gebruik word. Dit is veral geskik vir die saampersing van terrein wat bestaan ​​uit teëls of voxels waarin baie naburige elemente herhaal word.

delta kompressie

Die finale kompressie tegniek is delta kompressie. Dit lê in die feit dat slegs die verskille tussen die huidige speltoestand en die laaste toestand wat deur die kliënt ontvang is, oorgedra word.

Dit is die eerste keer in die Quake3-netwerkenjin gebruik. Hier is twee artikels wat verduidelik hoe om dit te gebruik:

Glenn Fiedler het dit ook in die tweede deel van sy artikel gebruik. Snapshot kompressie.

Enkripsie

Daarbenewens sal jy dalk die oordrag van inligting tussen kliënte en die bediener moet enkripteer. Daar is verskeie redes hiervoor:

  • Privaatheid/Vertroulikheid: Boodskappe kan slegs deur die ontvanger gelees word en geen ander netwerksnuffelspeurder sal dit kan lees nie.
  • stawing: 'n persoon wat die rol van 'n speler wil speel, moet sy sleutel ken.
  • cheat-voorkoming: dit sal baie moeiliker wees vir kwaadwillige spelers om hul eie cheat-pakkette te skep, hulle sal die enkripsieskema moet herhaal en die sleutel vind (wat op elke verbinding verander).

Ek beveel sterk aan om 'n biblioteek hiervoor te gebruik. Ek stel voor om libsodium, want dit is veral eenvoudig en het wonderlike tutoriale. Veral interessant is die tutoriaal oor sleutel ruil, wat jou toelaat om nuwe sleutels op elke nuwe verbinding te genereer.

Toepassingsprotokol: Gevolgtrekking

Dit sluit die toepassingsprotokol af. Ek glo dat kompressie heeltemal opsioneel is en die besluit om dit te gebruik hang net af van die spel en die vereiste bandwydte. Enkripsie is na my mening verpligtend, maar in die eerste prototipe kan jy daarsonder klaarkom.

Toepassingslogika

Ons kan nou die toestand in die kliënt opdateer, maar ons kan dalk vertragingsprobleme ondervind. Die speler, nadat hy 'n inset gemaak het, moet wag vir 'n speletjiestatusopdatering vanaf die bediener om te sien watter effek dit op die wêreld gehad het.

Boonop, tussen twee staatsopdaterings, is die wêreld heeltemal staties. As die staatsopdateringskoers laag is, sal die bewegings baie rukkerig wees.

Daar is verskeie tegnieke om die impak van hierdie probleem te verminder, en ek sal dit in die volgende afdeling bespreek.

Vertraag gladmaak tegnieke

Al die tegnieke wat in hierdie afdeling beskryf word, word in detail in die reeks bespreek. Vinnige Multiplayer Gabriël Gambetta. Ek beveel sterk aan om hierdie uitstekende reeks artikels te lees. Dit sluit ook 'n interaktiewe demonstrasie in om te sien hoe hierdie tegnieke in die praktyk werk.

Die eerste tegniek is om die invoerresultaat direk toe te pas sonder om te wag vir 'n antwoord van die bediener. Dit word genoem kliënt-kant voorspelling. Wanneer die kliënt egter 'n opdatering van die bediener ontvang, moet dit verifieer dat sy voorspelling korrek was. As dit nie die geval is nie, moet hy net sy toestand verander volgens wat hy van die bediener ontvang het, want die bediener is outoritêr. Hierdie tegniek is die eerste keer in Quake gebruik. Jy kan meer daaroor lees in die artikel. Quake Engine kode hersiening Fabien Sanglars [vertaling op Habré].

Die tweede stel tegnieke word gebruik om die beweging van ander entiteite tussen twee staatopdaterings glad te maak. Daar is twee maniere om hierdie probleem op te los: interpolasie en ekstrapolasie. In die geval van interpolasie word die laaste twee toestande geneem en die oorgang van die een na die ander word getoon. Die nadeel daarvan is dat dit 'n klein fraksie van die vertraging veroorsaak, want die kliënt sien altyd wat in die verlede gebeur het. Ekstrapolasie gaan oor die voorspelling waar die entiteite nou behoort te wees gebaseer op die laaste toestand wat die kliënt ontvang het. Die nadeel daarvan is dat as die entiteit die bewegingsrigting heeltemal verander, daar 'n groot fout tussen die voorspelling en die werklike posisie sal wees.

Die laaste, mees gevorderde tegniek, slegs nuttig in FPS, is vertragingsvergoeding. Wanneer vertragingsvergoeding gebruik word, neem die bediener die vertragings van die kliënt in ag wanneer dit op die teiken skiet. Byvoorbeeld, as 'n speler 'n kopskoot op hul skerm uitgevoer het, maar in werklikheid was hul teiken op 'n ander plek as gevolg van die vertraging, dan sal dit onregverdig wees om die speler die reg te ontsê om dood te maak weens die vertraging. Die bediener spoel dus die tyd terug na wanneer die speler geskiet het om te simuleer wat die speler op hul skerm gesien het en kyk vir 'n botsing tussen hul skoot en die teiken.

Glenn Fiedler (soos altyd!) het in 2004 'n artikel geskryf Netwerkfisika (2004), waarin hy die grondslag gelê het vir die sinchronisasie van fisika-simulasies tussen die bediener en die kliënt. In 2014 het hy 'n nuwe reeks artikels geskryf netwerk fisika, waarin hy ander tegnieke beskryf het om fisika-simulasies te sinchroniseer.

Daar is ook twee artikels op Valve se wiki, Bron Multiplayer Networking и Vertragingskompensasiemetodes in kliënt/bediener in-speletjie-protokolontwerp en -optimalisering hantering van vertragingsvergoeding.

Cheat Voorkoming

Daar is twee hoofbedrogvoorkomingstegnieke.

Eerstens, maak dit moeiliker vir bedrieërs om kwaadwillige pakkies te stuur. Soos hierbo genoem, is enkripsie 'n goeie manier om dit te implementeer.

Tweedens moet die gesaghebbende bediener slegs opdragte/invoer/aksies ontvang. Die kliënt behoort nie in staat te wees om die toestand op die bediener te verander behalwe deur insette te stuur nie. Dan moet die bediener, elke keer as dit insette ontvang, dit nagaan vir geldigheid voordat dit toegepas word.

Toepassingslogika: Gevolgtrekking

Ek beveel aan dat jy 'n manier implementeer om hoë latensie en lae verversingstempo's te simuleer sodat jy die gedrag van jou speletjie in slegte toestande kan toets, selfs wanneer die kliënt en bediener op dieselfde masjien werk. Dit vereenvoudig die implementering van tegnieke vir vertragings gladmaak aansienlik.

Ander nuttige hulpbronne

As jy ander netwerkmodelbronne wil verken, kan jy dit hier vind:

Bron: will.com

Voeg 'n opmerking