Megapack: Hvordan Factorio løste 200-spillers flerspillerproblem

Megapack: Hvordan Factorio løste 200-spillers flerspillerproblem
I mai i år deltok jeg som spiller i KatherineOfSky MMO-arrangementer. Jeg la merke til at når antallet spillere når et visst antall, "faller noen av dem med noen få minutter". Heldigvis for deg (men ikke for meg), var jeg en av disse spillerne hver gangselv med god forbindelse. Jeg tok det som en personlig utfordring og begynte å se etter årsakene til problemet. Etter tre uker med feilsøking, testing og fiksing er feilen endelig fikset, men reisen har ikke vært så lett.

Problemer i flerspillerspill er svært vanskelig å spore opp. De forekommer vanligvis under veldig spesifikke nettverksparametere og under veldig spesifikke spilltilstander (i dette tilfellet over 200 spillere). Og selv når et problem kan reproduseres, kan det ikke feilsøkes riktig fordi innsetting av bruddpunkter stopper spillet, roter til tidtakere og fører vanligvis til at tilkoblingen blir tidsavbrutt på grunn av et tidsavbrudd. Men takket være utholdenhet og et fantastisk verktøy kalt klønete Jeg klarte å finne ut hva som foregikk.

Kort sagt, på grunn av en feil og ufullstendig implementering av forsinkelsestilstandssimuleringen, befant klienten seg noen ganger i en situasjon der den måtte sende en nettverkspakke i én klokkesyklus, bestående av spillerinndatahandlinger for å velge omtrent 400 spillenheter ( vi kaller det en "megapakke"). Etter det trenger serveren ikke bare å motta alle disse inndatahandlingene på riktig måte, men også sende dem til alle andre klienter. Har du 200 kunder, blir dette fort et problem. Kanalen til serveren blir raskt tilstoppet, noe som resulterer i tapte pakker og en kaskade av etterspurte pakker. Utsettelse av input-handlinger fører til at flere klienter begynner å sende megapakker, og skredet deres blir enda sterkere. Vellykkede klienter klarer å komme seg, resten faller av.

Megapack: Hvordan Factorio løste 200-spillers flerspillerproblem
Problemet var ganske grunnleggende, og det tok meg 2 uker å fikse det. Det er ganske teknisk, så jeg skal forklare de saftige tekniske detaljene nedenfor. Men først må du vite at siden versjon 0.17.54, utgitt 4. juni, i møte med midlertidige tilkoblingsproblemer, har multiplayer blitt mer stabil, og forsinkelsesskjul er mye mindre buggy (mindre bremsing og teleportering). Jeg har også endret måten kampforsinkelser skjules på, og forhåpentligvis vil dette gjøre dem litt jevnere.

Multiplayer Mega Pack - Tekniske detaljer

For å si det enkelt, fungerer flerspiller i et spill slik: alle klienter simulerer spillets tilstand ved å motta og sende kun spillerinndata (kalt "inngangshandlinger" Inndatahandlinger). Hovedoppgaven til serveren er å overføre Inndatahandlinger og sikre at alle klienter utfører de samme handlingene i samme syklus. Du kan lese mer om dette i innlegget. FFF-149.

Siden serveren må ta beslutninger om hvilke handlinger som skal utføres, beveger spillerens handlinger seg langs følgende vei: spillerhandling -> spillklient -> nettverk -> server -> nettverk -> spillklient. Dette betyr at hver handling av spilleren utføres først etter at den har laget en tur-retur-bane gjennom nettverket. På grunn av dette ville spillet virket fryktelig tregt, så nesten umiddelbart etter at flerspilleren dukket opp i spillet, ble det introdusert en mekanisme for å skjule forsinkelser. Latency-skjuling simulerer spillerinnspill uten å ta hensyn til andre spilleres handlinger og serverbeslutninger.

Megapack: Hvordan Factorio løste 200-spillers flerspillerproblem
Factorio har en spilltilstand spilltilstand er den fullstendige tilstanden til kartet, spilleren, enhetene og alt annet. Det simuleres deterministisk i alle klienter basert på handlinger mottatt fra serveren. Spilltilstanden er hellig, og hvis den noen gang begynner å skille seg fra serveren eller en annen klient, skjer desynkronisering.

Foruten spilltilstand vi har en tilstand av forsinkelser Latenstilstand. Den inneholder en liten undergruppe av hovedtilstanden. Latenstilstand er ikke hellig og representerer bare et bilde av hvordan spillets tilstand vil se ut i fremtiden basert på innspillene fra spilleren Inndatahandlinger.

For å gjøre dette beholder vi en kopi av den genererte Inndatahandlinger i forsinkelseskøen.

Megapack: Hvordan Factorio løste 200-spillers flerspillerproblem
Det vil si at på slutten av prosessen på klientsiden ser bildet omtrent slik ut:

  1. Søke om Inndatahandlinger alle spillere til spilltilstand måten disse inndatahandlingene ble mottatt fra serveren på.
  2. Fjern alt fra forsinkelseskøen Inndatahandlinger, som ifølge serveren allerede er brukt på spilltilstand.
  3. Slett Latenstilstand og tilbakestill den slik at den ser nøyaktig ut som spilltilstand.
  4. Bruk alle handlinger fra forsinkelseskøen til Latenstilstand.
  5. Basert på data spilltilstand и Latenstilstand gjengi spillet til spilleren.

Alt dette gjentas i hvert slag.

For vanskelig? Ikke slapp av, det er ikke alt. For å kompensere for upålitelige Internett-tilkoblinger har vi laget to mekanismer:

  • Hoppet avkryssing: når serveren bestemmer det Inndatahandlinger vil bli utført i takt av spillet, så hvis han ikke har mottatt Inndatahandlinger noen spiller (for eksempel på grunn av en økt forsinkelse), vil han ikke vente, men vil informere denne klienten "Jeg tok ikke hensyn til din Inndatahandlinger, skal jeg prøve å legge dem til i neste linje. Dette gjøres slik at på grunn av problemer med tilkoblingen (eller med datamaskinen) til én spiller, blir ikke kartoppdateringen tregere for alle andre. Det er verdt å merke seg det Inndatahandlinger blir ikke ignorert, men bare utsatt.
  • Full tur-retur-latens: Serveren prøver å gjette hva tur-retur-latensen mellom klient og server er for hver klient. Hvert 5. sekund forhandler den om en ny forsinkelse med klienten etter behov (avhengig av hvordan forbindelsen har oppført seg tidligere), og øker eller reduserer tur-retur-forsinkelsen tilsvarende.

I seg selv er disse mekanismene ganske enkle, men når de brukes sammen (noe som ofte skjer ved tilkoblingsproblemer), blir kodelogikken vanskelig å administrere og med mange kantsaker. I tillegg, når disse mekanismene kommer inn, må serveren og forsinkelseskøen implementere en spesiell korrekt Inndatahandling rett StopMovementInTheNextTick. Takket være dette, i tilfelle tilkoblingsproblemer, vil karakteren ikke løpe på egen hånd (for eksempel under et tog).

Nå må jeg forklare deg hvordan enhetsvalg fungerer. En av de beståtte typene Inndatahandling er en endring i utvalgstilstanden til en enhet. Den forteller alle hvilken enhet spilleren svever over med musen. Som du kan se er dette en av de hyppigste inndatahandlingene som sendes av klienter, så for å spare båndbredde har vi optimalisert den slik at den tar minst mulig plass. Dette implementeres slik: når hver enhet er valgt, i stedet for å lagre absolutte, høypresisjonskartkoordinater, lagrer spillet en lavpresisjons relativ offset fra forrige valg. Dette fungerer bra fordi musevalget vanligvis skjer veldig nært det forrige valget. Dette stiller to viktige krav: Inndatahandlinger bør aldri hoppes over og må gjøres i riktig rekkefølge. Disse kravene er oppfylt for spilltilstand. Men siden oppgaven latenstilstand i "ser bra nok ut" for spilleren, er de ikke fornøyde i forsinkelsestilstanden. Latenstilstand tar ikke hensyn til mange grensetilfellerforbundet med å hoppe over klokker og endre overføringsforsinkelser tur/retur.

Du kan allerede gjette hvor dette går. Endelig begynner vi å se årsakene til megapakkeproblemet. Roten til problemet er at enhetsvalglogikken er avhengig av Latenstilstand, og denne tilstanden inneholder ikke alltid riktig informasjon. Så megapakken genereres slik:

  1. Spilleren har tilkoblingsproblemer.
  2. Mekanismene for å hoppe over sykluser og regulere overføringsforsinkelsen tur-retur spiller inn.
  3. Forsinkelsestilstandskøen tar ikke hensyn til disse mekanismene. Dette fører til at noen handlinger fjernes for tidlig eller kjøres i feil rekkefølge, noe som resulterer i en feil Latenstilstand.
  4. Spilleren har ingen tilkoblingsproblemer og simulerer opptil 400 sykluser for å ta igjen serveren.
  5. I hver syklus genereres en ny handling og klargjøres for å sendes til serveren, noe som endrer enhetsvalget.
  6. Klienten sender en megapakke med 400+ endringer i enhetsvalg til serveren (og med andre handlinger: avfyringstilstand, gangtilstand osv. led også av dette problemet).
  7. Serveren mottar 400 inndatahandlinger. Siden det ikke er tillatt å hoppe over en enkelt inndatahandling, instruerer den alle klienter om å utføre disse handlingene og sender dem over nettverket.

Det ironiske er at en mekanisme designet for å spare båndbredde resulterte i enorme nettverkspakker.

Vi har løst dette problemet ved å fikse alle oppdateringskanttilfeller og støtte for forsinket kø. Selv om det tok ganske lang tid, var det verdt å få det riktig til slutt i stedet for å stole på raske hacks.

Kilde: www.habr.com

Legg til en kommentar