
Under de senaste tvÄ veckorna har jag arbetat med ett nÀtverksmotor för mitt spel. Innan dess visste jag överhuvudtaget ingenting om nÀtverkstekniker i spel, sÄ jag lÀste mÄnga artiklar och genomförde mÄnga experiment för att förstÄ alla koncept och kunna skriva min egen nÀtverksmotor.
I den hÀr guiden vill jag dela med mig av olika koncept som du behöver studera innan du skriver din egen spelmotor, samt de bÀsta resurserna och artiklarna för att lÀra dig om dem.
Det finns i allmÀnhet tvÄ huvudsakliga typer av nÀtverksarkitekturer: peer-to-peer och klient-server. I peer-to-peer-arkitekturen (p2p) överförs data mellan vilken som helst av de anslutna spelarna medan i klient-server-arkitekturen överförs data endast mellan spelarna och servern.
Ăven om peer-to-peer-arkitektur fortfarande anvĂ€nds i vissa spel, Ă€r klient-server-arkitekturen standard: den Ă€r enklare att genomföra, krĂ€ver mindre bandbredd och underlĂ€ttar skydd mot fusk. DĂ€rför kommer vi i den hĂ€r guiden att fokusera pĂ„ klient-server-arkitekturen.
Specifikt Àr vi mest intresserade av auktoritativa servrar: i sÄdana system har servern alltid rÀtt. Till exempel, om en spelare tror att de befinner sig pÄ koordinaterna (10, 5), men servern sÀger att de Àr pÄ (5, 3), sÄ mÄste klienten ersÀtta sin position med den som servern ger, och inte vice versa. Att anvÀnda auktoritativa servrar förenklar upptÀckten av fusk.
I spelens nÀtverksystem finns det tre huvudsakliga komponenter:
- Transportprotokollet: hur data överförs mellan kunder och server.
- Applikationsprotokollet: vad som överförs frÄn kunder till server och frÄn server till kunder och i vilket format.
- Applikationens logik: hur de överförda uppgifterna anvÀnds för att uppdatera statusen för kunder och server.
Det Àr vÀldigt viktigt att förstÄ rollen för varje del och de relaterade svÄrigheterna.
Transportprotokollet
Det första steget Àr att vÀlja ett protokoll för överföring av data mellan servern och kunderna. Det finns tvÄ internetprotokoll för detta: och . Men du kan ocksÄ skapa ditt eget transportprotokoll baserat pÄ ett av dem eller anvÀnda ett bibliotek dÀr de anvÀnds.
JÀmförelse av TCP och UDP
BÄde TCP och UDP baseras pÄ . IP möjliggör överföring av paket frÄn avsÀndare till mottagare, men ger inga garantier för att det skickade paketet sÄ smÄningom nÄr mottagaren, att det kommer fram Ätminstone en gÄng, eller att paketens sekvens anlÀnder i rÀtt ordning. Dessutom kan paketet innehÄlla bara en begrÀnsad mÀngd data, faststÀlld av storleken .
UDP Àr bara ett tunt lager ovanpÄ IP. DÀrför har det samma begrÀnsningar. Till skillnad frÄn det har TCP mÄnga funktioner. Det erbjuder en pÄlitlig ordnad anslutning mellan tvÄ noder med felkontroll. DÀrför Àr TCP mycket praktiskt och anvÀnds i mÄnga andra protokoll, till exempel i , och . Men alla dessa funktioner har ett pris: .
För att förstÄ varför dessa funktioner kan orsaka fördröjning mÄste man förstÄ hur TCP fungerar. NÀr avsÀndarnoden skickar ett paket till mottagarnoden vÀntar den pÄ att fÄ en bekrÀftelse (ACK). Om den inte fÄr den inom en viss tid (eftersom paketet eller bekrÀftelsen har gÄtt förlorad, eller av andra orsaker) sÄ sÀnder den paketet igen. Dessutom garanterar TCP att paketen mottas i rÀtt ordning, vilket innebÀr att tills det förlorade paketet har mottagits kan alla andra paket inte behandlas, Àven om de redan har mottagits av mottagarnoden.
Men som du förmodligen förstÄr Àr fördröjning i flerspelarspel mycket viktigt, sÀrskilt i aktiva genrer som FPS. Just dÀrför anvÀnder mÄnga spel UDP med ett eget protokoll.
Ett eget protokoll baserat pÄ UDP kan vara mer effektivt Àn TCP av olika anledningar. Till exempel kan det mÀrka vissa paket som pÄlitliga och andra som opÄlitliga. DÀrför bryr det sig inte om det opÄlitliga paketet nÄdde mottagaren. Eller det kan hantera flera datastreamar sÄ att ett paket förlorat i en ström inte bromsar de andra strömmarna. Till exempel kan det finnas en ström för spelarens input och en annan ström för chattmeddelanden. Om chattmeddelandet, som inte Àr en brÄdskande data, gÄr förlorat, sÄ fördröjer det inte inmatningen, som Àr akuta. Eller sÄ kan det egna protokollet implementera pÄlitlighet pÄ ett annat sÀtt Àn TCP för att vara mer effektivt i videospelsförhÄllanden.
SÄ, om TCP Àr sÄ dÄligt, ska vi skapa vÄrt eget transportprotokoll baserat pÄ UDP?
Det Ă€r lite mer komplicerat. Ăven om TCP nĂ€stan Ă€r suboptimalt för spel-nĂ€tverksystem, kan det fungera ganska bra specifikt i ditt spel och spara din vĂ€rdefulla tid. Till exempel kanske fördröjningen inte Ă€r ett problem för ett turordningsbaserat spel eller ett spel som endast kan spelas pĂ„ LAN-nĂ€tverk, dĂ€r fördröjningar och paketförluster Ă€r mycket mindre Ă€n pĂ„ internet.
MÄnga framgÄngsrika spel, inklusive World of Warcraft, Minecraft och Terraria, anvÀnder TCP. Men i de flesta FPS-spel anvÀnds egna protokoll baserade pÄ UDP, sÄ nedan pratar vi mer om dem.
Om du bestÀmmer dig för att anvÀnda TCP, se till att , Àr avstÀngd, eftersom den buffrar paket innan de skickas, vilket ökar fördröjningen.
För att fÄ mer information om skillnaderna mellan UDP och TCP i sammanhanget av flerspelarspel kan du lÀsa Glenn Fidlers artikel .
Eget protokoll
SÄ, du vill skapa ditt eget transportprotokoll, men vet inte vart du ska börja? Du Àr lycklig, för Glenn Fidler har skrivit tvÄ fantastiska artiklar om detta. I dem hittar du massor av kloka tankar.
Den första artikeln, frÄn 2008 Àr enklare Àn den andra, frÄn 2016. Jag rekommenderar att du börjar med den Àldre.
Observera att Glenn Fidler Àr en stor föresprÄkare för att anvÀnda ett eget protokoll baserat pÄ UDP. Och efter att ha lÀst hans artiklar kommer du förmodligen att anta hans Äsikt att TCP har allvarliga brister i videospel och vilja implementera ditt eget protokoll.
Men om du Àr ny pÄ nÀtverksarbete, gör dig sjÀlv en tjÀnst och anvÀnd TCP eller en bibliotek. För att framgÄngsrikt implementera ett eget transportprotokoll behöver du först lÀra dig mycket.
NĂ€tverksbibliotek
Om du behöver nÄgot mer effektivt Àn TCP, men inte vill krÄngla med att implementera ditt eget protokoll och gÄ in i mÄnga detaljer, kan du anvÀnda ett nÀtverksbibliotek. Det finns mÄnga av dem:
- Glenn Fidler
- , som inte lÀngre stöds, men dess fork verkar fortfarande vara aktiv.
- Àr ett bibliotek som skapades för flerspelar-FPS
- frÄn Valve
Jag har inte provat alla, men jag föredrar ENet eftersom det Àr enkelt att anvÀnda och pÄlitligt. Dessutom har det tydlig dokumentation och en handledning för nybörjare.
Transportprotokoll: slutsats
Sammanfattningsvis finns det tvÄ grundlÀggande transportprotokoll: TCP och UDP. TCP har flera anvÀndbara egenskaper: pÄlitlighet, paketordning och felupptÀckning. UDP saknar allt detta, men TCP har Ä sin sida högre latens, vilket Àr oacceptabelt för vissa spel. SÄ för att sÀkerstÀlla lÄg latens kan man skapa ett eget protokoll baserat pÄ UDP eller anvÀnda ett bibliotek som implementerar transportprotokollet över UDP och Àr anpassat för flerspelarvideospel.
Valet mellan TCP, UDP och biblioteket beror pÄ flera faktorer. För det första, pÄ spelets behov: krÀver det lÄg latens? För det andra, pÄ applikationens protokollkrav: behövs ett pÄlitligt protokoll? Som vi kommer att se i nÀsta del kan man skapa ett applikationsprotokoll som fungerar bra med ett opÄlitligt protokoll. Slutligen mÄste man Àven ta hÀnsyn till erfarenheten hos utvecklaren av nÀtverksmotorn.
Jag har tvÄ rÄd:
- Abstrahera transportprotokollet frÄn resten av applikationen sÄ mycket som möjligt sÄ att det enkelt kan bytas ut utan att behöva skriva om hela koden.
- Undvik prematur optimering. Om du inte Àr nÀtverksspecialist och inte Àr sÀker pÄ om du behöver ett eget transportprotokoll baserat pÄ UDP, kan du börja med TCP eller ett bibliotek som erbjuder pÄlitlighet och sedan testa och mÀta prestandan. Om problem uppstÄr och du Àr sÀker pÄ att det beror pÄ transportprotokollet, kan det vara dags att skapa ett eget transportprotokoll.
Avslutningsvis rekommenderar jag att du lÀser av Brian Hook, dÀr mÄnga av de Àmnen som diskuteras hÀr behandlas.
Applikationsprotokoll
Nu nÀr vi kan utbyta data mellan klienterna och servern behöver vi bestÀmma vilka data som ska skickas och i vilket format.
Den klassiska modellen Àr att klienterna skickar input eller handlingar till servern, och servern skickar det aktuella spelstÄndet till klienterna.
Servern skickar en filtrerad version av tillstÄndet med enheter som finns i nÀrheten av spelaren. Detta görs av tre anledningar. För det första kan det fullstÀndiga tillstÄndet vara för stort för att överföras med hög frekvens. För det andra Àr det mesta av intresset för visuella och ljuddata, eftersom större delen av spel-logiken simuleras pÄ spelservern. För det tredje fÄr spelaren inte veta vissa data i vissa spel, som positionen av fienden pÄ andra sidan kartan, eftersom detta skulle göra det möjligt för spelaren att sniffa paketen och exakt veta vart de ska röra sig för att döda fienden.
Serialisering
Det första steget Àr att omvandla de data vi vill skicka (inmatning eller spelstÄnd) till ett format som Àr lÀmpligt för överföring. Denna process kallas .
Man kommer genast pÄ att anvÀnda ett mÀnniskolÀsbart format som JSON eller XML. Men detta skulle vara helt ineffektivt och ta upp en stor del av kanalen.
IstÀllet rekommenderas att anvÀnda ett binÀrt format, som Àr mycket mer kompakt. Det vill sÀga att paketen bara innehÄller nÄgra fÄ byte. HÀr mÄste man ta hÀnsyn till problemet med , som kan vara olika pÄ olika datorer.
För att serialisera data kan man anvÀnda ett bibliotek, till exempel:
- frÄn Google
- frÄn Sandstorm
- av Shane Grant och Randolph Vurhis
Se bara till att biblioteken skapar portabla arkiv och hanterar byteordningen.
Ett alternativ kan vara att implementera det sjÀlv, vilket inte Àr sÀrskilt svÄrt, sÀrskilt om du anvÀnder ett datadrivet tillvÀgagÄngssÀtt i din kod. Dessutom ger det dig möjlighet att göra optimeringar som inte alltid Àr möjliga med ett bibliotek.
Glenn Fidler har skrivit tvÄ artiklar om serialisering: och .
Kompression
MÀngden data som överförs mellan klienterna och servern Àr begrÀnsad av kanalens bandbredd. Datakomprimering gör att man kan skicka mer data i varje snapshot, öka uppdateringsfrekvensen eller helt enkelt minska kraven pÄ kanalen.
Bitpackning
Den första tekniken Àr bitpackning. Det handlar om att anvÀnda exakt det antal bitar som behövs för att beskriva den nödvÀndiga storleken. Om du till exempel har en upprÀkning som kan ha 16 olika vÀrden, kan du anvÀnda bara 4 bitar istÀllet för en hel byte (8 bitar).
Glenn Fiedler förklarar hur man implementerar detta i andra delen av artikeln. .
Bitpackning fungerar sÀrskilt bra med diskretisering, vilket kommer att vara Àmnet för nÀsta avsnitt.
Diskretisering
Àr en teknik för komprimering med förlust, som gÄr ut pÄ att anvÀnda endast en delmÀngd av de möjliga vÀrdena för kodning av storleken. Det Àr lÀttast att genomföra diskretisering genom att avrunda flyttal.
Glenn Fiedler (igen!) visar hur man praktiskt tillÀmpar diskretisering i sin artikel. .
Komprimeringsalgoritmer
Den nÀsta tekniken kommer att vara förlustfria komprimeringsalgoritmer.
HÀr Àr, enligt min mening, tre av de mest intressanta algoritmerna som man bör kÀnna till:
- med förberÀknad kod som Àr extremt snabb och kan ge bra resultat. Den anvÀndes för att komprimera paket i nÀtverksmotorn Quake3.
- Àr en allmÀn komprimeringsalgoritm som aldrig ökar datamÀngden. Som man kan se , har den anvÀnts i mÄnga tillÀmpningar. För att uppdatera tillstÄnd kan den vara överflödig, men den kan vara anvÀndbar om du behöver skicka tillgÄngar, lÄnga texter eller terrÀng frÄn servern till klienterna.
- Àr kanske den enklaste komprimeringsalgoritmen, men den Àr vÀldigt effektiv för vissa typer av data och kan anvÀndas som ett förbearbetningssteg innan zlib. Den Àr sÀrskilt lÀmplig för att komprimera terrÀng bestÄende av tiles eller voxels dÀr mÄnga angrÀnsande element upprepas.
Delta-komprimering
Den sista komprimeringsmetoden Àr delta-komprimering. Det handlar om att endast överföra skillnaderna mellan det aktuella spelstatuset och det senaste statuset som mottagits av klienten.
Den anvÀndes första gÄngen i nÀtverksmotorn Quake3. HÀr Àr tvÄ artiklar som förklarar hur den anvÀnds:
- Brian Hook
- Fabien Sanglard [ artiklar pÄ Habré, se avsnittet "NÀtverksmodell"].
Glenn Fidler har ocksÄ anvÀnt den i den andra delen av sin artikel .
Kryptering
Dessutom kan det vara nödvÀndigt att kryptera dataöverföring mellan klienter och servrar. Det finns flera anledningar till detta:
- integritet: meddelandena kan endast lÀsas av mottagaren, och ingen annan person som övervakar nÀtet kan lÀsa dem.
- autentisering: en person som vill agera som spelare mÄste kÀnna till sin nyckel.
- förhindra fusk: illvilliga spelare kommer att ha mycket svÄrare att skapa egna paket för fusk, eftersom de mÄste Äterskapa krypteringsschemat och hitta nyckeln (som Àndras vid varje anslutning).
Jag rekommenderar starkt att anvÀnda ett bibliotek för detta. Jag föreslÄr att anvÀnda , eftersom det Àr sÀrskilt enkelt och har utmÀrkta handledningar. Det Àr sÀrskilt intressant med handledningen för , som gör det möjligt att generera nya nycklar vid varje ny anslutning.
Applikationsprotokoll: sammanfattning
HÀrmed avslutar vi med applikationsprotokollet. Jag anser att komprimering Àr helt onödig och att beslutet om att anvÀnda det endast beror pÄ spelet och den nödvÀndiga bandbredden. Kryptering, anser jag, Àr obligatorisk, men i den första prototypen kan vi klara oss utan det.
Applikationslogik
Nu kan vi uppdatera tillstÄndet i klienten, men vi kan möta problem med fördröjningar. NÀr spelaren gör en insats mÄste hen vÀnta pÄ att se spelets tillstÄnd uppdateras frÄn servern för att förstÄ vilken pÄverkan hen har haft pÄ vÀrlden.
Dessutom förblir vÀrlden helt statisk mellan tvÄ tillstÄndsuppdateringar. Om uppdateringsfrekvensen Àr lÄg kommer rörelserna att vara vÀldigt ryckiga.
Det finns flera tekniker som kan minska pÄverkan av detta problem, och i nÀsta avsnitt kommer jag att berÀtta om dem.
Tekniker för att jÀmna ut fördröjningar
Alla tekniker som beskrivs i det hÀr avsnittet behandlas ingÄende i serien av Gabriel Gambetta. Jag rekommenderar starkt att lÀsa denna utmÀrkta serie artiklar. Den inkluderar ocksÄ en interaktiv demo som visar hur dessa tekniker fungerar i praktiken.
Den första tekniken handlar om att tillÀmpa resultatet av insatsen direkt utan att vÀnta pÄ svar frÄn servern. Detta kallas klientbaserad förutsÀgelse. Men nÀr klienten fÄr en uppdatering frÄn servern mÄste den sÀkerstÀlla att dess förutsÀgelse var korrekt. Om sÄ inte Àr fallet, behöver den bara Àndra sitt tillstÄnd baserat pÄ det som mottagits frÄn servern, eftersom servern Àr auktoritÀr. Denna teknik anvÀndes först i Quake. Mer om detta kan lÀsas i artikeln Fabien Sanglard [ pÄ Habr].
Ett andra set av tekniker anvÀnds för att jÀmna ut rörelsen av andra entiteter mellan tvÄ uppdateringar av tillstÄnd. Det finns tvÄ sÀtt att lösa denna uppgift: interpolation och extrapolation. Vid interpolation tas de tvÄ senaste tillstÄnden och övergÄngen frÄn ett till ett annat visas. Dess nackdel Àr att det orsakar en liten fördröjning eftersom klienten alltid ser vad som hÀnde i det förflutna. Extrapolation handlar om att förutsÀga var entiteterna nu borde befinna sig baserat pÄ det senaste tillstÄndet som klienten mottagit. Dess nackdel Àr att om en entitet helt Àndrar rörelseriktning, sÄ uppstÄr det en stor felmarginal mellan förutsÀgelsen och den verkliga positionen.
Den senaste, mest avancerade tekniken, anvĂ€ndbar endast i FPS â Ă€r fördröjningskompensation. Vid anvĂ€ndning av fördröjningskompensation tar servern hĂ€nsyn till klientens latens nĂ€r denne skjuter mot ett mĂ„l. Till exempel, om en spelare gjorde ett headshot pĂ„ sin skĂ€rm, men i verkligheten befann sig mĂ„let pĂ„ grund av latensen pĂ„ ett annat stĂ€lle, skulle det vara orĂ€ttvist att neka spelaren rĂ€tten till ett dödligt skott pĂ„ grund av latens. DĂ€rför spolar servern tillbaka tiden till det ögonblick dĂ„ spelaren sköt, för att simulera vad spelaren sĂ„g pĂ„ sin skĂ€rm och kontrollera kollisionen mellan hans skott och mĂ„let.
Glenn Fiedler (som alltid!) skrev 2004 en artikel , dÀr han la grunden för synkronisering av fysiksimulering mellan servern och klienten. 2014 skrev han en ny serie artiklar , dÀr han beskrev andra tekniker för synkronisering av fysiksimulering.
Det finns ocksÄ tvÄ artiklar pÄ företagets wiki Valve, och dÀr fördröjningskompensation behandlas.
Förebyggande av fusk
Det finns tvÄ huvudsakliga tekniker för att förebygga fusk.
Första: försvÄrande av att cheaters skickar skadliga paket. Som nÀmnts ovan, ett bra sÀtt att implementera detta Àr kryptering.
Andra: den auktoritÀra servern ska endast ta emot kommandon/input/ÄtgÀrder. Klienten ska inte ha möjlighet att Àndra tillstÄndet pÄ servern, annat Àn att skicka input. DÄ bör servern kontrollera giltigheten hos input varje gÄng den mottar det innan det tillÀmpas.
Applikationens logik: slutsats
Jag rekommenderar att du implementerar en metod för att simulera stora fördröjningar och lÄga uppdateringsfrekvenser för att kunna testa spelets beteende under dÄliga förhÄllanden, Àven nÀr klienten och servern körs pÄ samma dator. Detta kommer att förenkla implementeringen av metoder för att smidiga fördröjningar.
Andra anvÀndbara resurser
Om du vill utforska andra resurser kring nÀtverksmodeller kan du hitta dem hÀr:
- â vĂ€rt att lĂ€sa hela hans blogg, den innehĂ„ller mĂ„nga utmĂ€rkta artiklar. alla artiklar om nĂ€tverksteknologier Ă€r samlade.
- av M. Fatih MAR â en omfattande lista över artiklar och videor om nĂ€tverksmotorer för videospel.
- I har ocksÄ mÄnga anvÀndbara lÀnkar.
KĂ€lla: habr.com
