Megapack: Hvordan Factorio løste 200-spillere multiplayer-problemet

Megapack: Hvordan Factorio løste 200-spillere multiplayer-problemet
I maj i år deltog jeg som spiller i MMO-begivenheder KatherineOfSky. Jeg bemærkede, at når antallet af spillere når et bestemt antal, "falder nogle af dem af" hvert par minutter. Heldigvis for dig (men ikke for mig), var jeg en af ​​de spillere, der afbrød forbindelsen hver gang, selv med en god forbindelse. Jeg tog dette som en personlig udfordring og begyndte at lede efter årsagerne til problemet. Efter tre ugers debugging, test og rettelser blev fejlen endelig rettet, men rejsen var ikke så let.

Problemer med multiplayer-spil er meget svære at spore. De forekommer normalt under meget specifikke netværksparametre og meget specifikke spilforhold (i dette tilfælde med mere end 200 spillere). Og selv når problemet kan reproduceres, kan det ikke fejlsøges korrekt, fordi indsættelse af breakpoints stopper spillet, forvirrer timere og normalt får forbindelsen til at time-out. Men takket være vedholdenhed og et vidunderligt værktøj kaldet klodset Det lykkedes mig at finde ud af, hvad der foregik.

Kort sagt, på grund af en fejl og ufuldstændig implementering af latenstilstandssimuleringen, ville klienten nogle gange befinde sig i en situation, hvor den skulle sende en netværkspakke bestående af spillerens inputvalgshandlinger på ca. 400 spilenheder i én clock-cyklus ( vi kalder dette en "mega-pakke"). Serveren skal så ikke kun modtage alle disse inputhandlinger korrekt, men også sende dem til alle andre klienter. Hvis du har 200 kunder, bliver dette hurtigt et problem. Linket til serveren bliver hurtigt tilstoppet, hvilket fører til pakketab og en kaskade af genanmodede pakker. En forsinkelse af inputhandlingen får så endnu flere klienter til at sende megapakker, hvilket får lavinen til at blive endnu større. Det lykkes heldige kunder at komme sig; alle de andre falder af.

Megapack: Hvordan Factorio løste 200-spillere multiplayer-problemet
Problemet var ret grundlæggende, og det tog mig 2 uger at løse det. Det er ret teknisk, så jeg vil forklare de saftige tekniske detaljer nedenfor. Men først skal du vide, at siden version 0.17.54, udgivet den 4. juni, i lyset af midlertidige forbindelsesproblemer, er multiplayer blevet mere stabil, og skjulte forsinkelser er blevet meget mindre buggy (mindre opbremsning og teleportering). Jeg har også ændret måden, hvorpå kampforsinkelse er skjult, og jeg håber, at dette vil gøre det lidt mere jævnt.

Multiplayer Mega Pack - Tekniske detaljer

For at sige det enkelt fungerer multiplayer i et spil sådan her: alle klienter simulerer spillets tilstand, modtager og sender kun spillerinput (kaldet "input handlinger", Indtast handlinger). Serverens hovedopgave er at overføre Indtast handlinger og kontrollere, at alle klienter udfører de samme handlinger i den samme clock-cyklus. Det kan du læse mere om i indlægget FFF-149.

Da serveren skal træffe beslutninger om, hvilke handlinger der skal udføres, bevæger spillerens handlinger sig omtrent ad denne vej: spillerhandling -> spilklient -> netværk -> server -> netværk -> spilklient. Det betyder, at hver spillers handling kun udføres efter at have foretaget en rundtur på tværs af netværket. På grund af dette ville spillet virke forfærdeligt langsomt, så næsten umiddelbart efter introduktionen af ​​multiplayer i spillet, blev der introduceret en mekanisme til at skjule forsinkelser. Skjul forsinkelse simulerer spillerens input uden at tage hensyn til andre spilleres handlinger og serverens beslutninger.

Megapack: Hvordan Factorio løste 200-spillere multiplayer-problemet
Factorio har en spiltilstand Spiltilstand er den komplette tilstand af kortet, spilleren, entiteter og alt andet. Det simuleres deterministisk i alle klienter baseret på handlingerne modtaget fra serveren. Spiltilstanden er hellig, og hvis den nogensinde begynder at adskille sig fra serveren eller enhver anden klient, sker desynkronisering.

Undtagen Spiltilstand vi har en tilstand af forsinkelser Latenstilstand. Den indeholder en lille delmængde af grundtilstanden. Latenstilstand ikke hellig og repræsenterer blot et billede af, hvordan spillets tilstand vil se ud i fremtiden baseret på spillerens input Indtast handlinger.

Til dette formål gemmer vi en kopi af den oprettede Indtast handlinger i forsinkelseskøen.

Megapack: Hvordan Factorio løste 200-spillere multiplayer-problemet
Det vil sige, i slutningen af ​​processen på klientsiden ser billedet sådan ud:

  1. ansøge Indtast handlinger alle spillere til Spiltilstand den måde, hvorpå disse inputhandlinger blev modtaget fra serveren.
  2. Vi fjerner alt fra forsinkelseskøen Indtast handlinger, som ifølge serveren allerede er blevet anvendt på Spiltilstand.
  3. Slet Latenstilstand og nulstil det, så det ser præcis det samme ud som Spiltilstand.
  4. Vi anvender alle handlinger fra forsinkelseskøen til Latenstilstand.
  5. Baseret på data Spiltilstand и Latenstilstand Vi gengiver spillet til spilleren.

Alt dette gentages i hver takt.

For svært? Slap ikke af, det er ikke alt. For at kompensere for upålidelige internetforbindelser har vi skabt to mekanismer:

  • Missede flueben: når serveren beslutter det Indtast handlinger vil blive udført på spillets beat, så hvis han ikke modtog Indtast handlinger en spiller (for eksempel på grund af øget forsinkelse), vil han ikke vente, men vil informere denne klient "Jeg tog ikke hensyn til din Indtast handlinger, vil jeg prøve at tilføje dem i den næste bjælke." Dette gøres for, at kortopdateringen på grund af problemer med forbindelsen (eller computeren) på én spiller ikke bliver langsommere for alle andre. Det er værd at bemærke Indtast handlinger bliver ikke ignoreret, men blot lagt til side.
  • Fuld round-trip latency: Serveren forsøger at gætte, hvad round-trip latency mellem klienten og serveren er for hver klient. Hvert 5. sekund forhandler den en ny latency med klienten, hvis det er nødvendigt (baseret på, hvordan forbindelsen har opført sig tidligere), og øger eller mindsker round-trip latency i overensstemmelse hermed.

I sig selv er disse mekanismer ret simple, men når de bruges sammen (hvilket ofte sker ved forbindelsesproblemer), bliver kodens logik svær at administrere og med mange kantsager. Når disse mekanismer kommer i spil, skal serveren og forsinkelseskøen desuden implementere det særlige korrekt Indtast handling berettiget StopMovementInTheNextTick. Takket være dette, hvis der er problemer med forbindelsen, vil karakteren ikke køre på egen hånd (for eksempel foran et tog).

Nu skal vi forklare dig, hvordan enhedsvalg fungerer. En af de overførte typer Indtast handling er en ændring i enhedsudvælgelsestilstanden. Den fortæller alle, hvilken enhed spilleren svæver over. Som du kan forestille dig, er dette en af ​​de mest almindelige inputhandlinger sendt af klienter, så for at spare båndbredde har vi optimeret det til at fylde så lidt som muligt. Den måde, det fungerer på, er, at efterhånden som hver enhed er valgt, i stedet for at gemme absolutte, højpræcise kortkoordinater, gemmer spillet en relativ lavpræcisionsforskydning fra det forrige valg. Dette fungerer godt, fordi musevalg har en tendens til at være meget tæt på det tidligere valg. Dette rejser to vigtige krav: Indtast handlinger De må aldrig springes over og skal udfyldes i den rigtige rækkefølge. Disse krav er opfyldt vedr Spiltilstand. Men siden opgaven Latenstilstand i "ser godt nok ud" for spilleren, er de ikke tilfredse i forsinkelsestilstanden. Latenstilstand tager ikke hensyn til mange kantsager, forbundet med at springe urcyklusser over og ændre transmissionsforsinkelser rundtur.

Du kan allerede gætte, hvor det går hen. Vi begynder endelig at se årsagerne til megapack-problemet. Roden til problemet er, at enhedsudvælgelseslogikken er afhængig af Latenstilstand, og denne tilstand indeholder ikke altid de korrekte oplysninger. Derfor genereres en megapakke noget som dette:

  1. Afspilleren har forbindelsesproblemer.
  2. Mekanismer til at springe urcyklusser over og regulere forsinkelsen af ​​returtransmission kommer i spil.
  3. Forsinkelsestilstandskøen tager ikke højde for disse mekanismer. Dette medfører, at nogle handlinger fjernes for tidligt eller udføres i den forkerte rækkefølge, hvilket resulterer i forkerte Latenstilstand.
  4. Spilleren har et forbindelsesproblem og simulerer op til 400 cyklusser for at indhente serveren.
  5. Ved hvert flueben genereres en ny handling, der ændrer entitetsvalget, og forberedes til afsendelse til serveren.
  6. Klienten sender en mega-batch af 400+ enhedsvalgsændringer til serveren (og med andre handlinger: skydetilstande, gangtilstande osv. led også af dette problem).
  7. Serveren modtager 400 inputhandlinger. Da det ikke er tilladt at springe nogen inputhandlinger over, beordrer det alle klienter til at udføre disse handlinger og sender dem på tværs af netværket.

Det ironiske er, at en mekanisme designet til at spare båndbredde endte med at skabe enorme netværkspakker.

Vi løste dette problem ved at rette alle edge-tilfælde af opdatering og backlog-køsupport. Selvom det tog en del tid, var det i sidste ende værd at få det rigtigt frem for at stole på hurtige hacks.

Kilde: www.habr.com

Tilføj en kommentar