Megapack: hur Factorio-utvecklarna lyckades lösa problemet med multiplayer för 200 spelare

Megapack: hur Factorio-utvecklarna lyckades lösa problemet med multiplayer för 200 spelare
I maj i år deltog jag som spelare i MMO-evenemang KatherineOfSky. Jag märkte att när antalet spelare når ett visst antal så "faller några av dem med några minuter". Lyckligtvis för dig (men inte för mig) var jag en av de spelarna som kopplade bort varje gång, även med en bra anslutning. Jag tog detta som en personlig utmaning och började leta efter orsakerna till problemet. Efter tre veckors felsökning, testning och korrigeringar fixades buggen äntligen, men resan var inte så lätt.

Problem med multiplayer-spel är mycket svåra att spåra. De inträffar vanligtvis under mycket specifika nätverksparametrar och mycket specifika spelförhållanden (i det här fallet med fler än 200 spelare). Och även när problemet kan reproduceras, kan det inte felsökas ordentligt eftersom att infoga brytpunkter stoppar spelet, förvirrar timers och vanligtvis gör att anslutningen timeout. Men tack vare uthållighet och ett underbart verktyg som heter klumpig Jag lyckades ta reda på vad som pågick.

Kort sagt, på grund av en bugg och ofullständig implementering av latenstillståndssimuleringen, skulle klienten ibland hamna i en situation där den var tvungen att skicka ett nätverkspaket bestående av spelarens ingångsvalsåtgärder på cirka 400 spelenheter i en klockcykel ( vi kallar detta ett "megapaket"). Servern måste då inte bara ta emot alla dessa inmatningsåtgärder korrekt, utan även skicka dem till alla andra klienter. Om du har 200 kunder blir detta snabbt ett problem. Länken till servern blir snabbt igensatt, vilket leder till paketförlust och en kaskad av återbegärda paket. Att försena inmatningsåtgärden får sedan ännu fler klienter att skicka megapaket, vilket gör att lavinen blir ännu större. Lyckliga kunder lyckas återhämta sig, alla andra faller av.

Megapack: hur Factorio-utvecklarna lyckades lösa problemet med multiplayer för 200 spelare
Problemet var ganska grundläggande och det tog mig 2 veckor att fixa det. Det är ganska tekniskt, så jag ska förklara de saftiga tekniska detaljerna nedan. Men först måste du veta att sedan version 0.17.54, som släpptes den 4 juni, inför tillfälliga anslutningsproblem, har multiplayer blivit stabilare, och gömma förseningar har blivit mycket mindre buggiga (mindre avmattning och teleportering). Jag har också ändrat hur stridsfördröjning döljs och jag hoppas att detta kommer att göra det lite smidigare.

Multiplayer Mega Pack - Tekniska detaljer

För att uttrycka det enkelt, multiplayer i ett spel fungerar så här: alla klienter simulerar spelets tillstånd, tar bara emot och skickar spelarinput (kallas "input actions", Inmatningsåtgärder). Serverns huvuduppgift är att överföra Inmatningsåtgärder och kontrollera att alla klienter utför samma åtgärder i samma klockcykel. Du kan läsa mer om detta i inlägget FFF-149.

Eftersom servern måste fatta beslut om vilka åtgärder som ska utföras, rör sig spelarens åtgärder ungefär längs denna väg: spelaråtgärd -> spelklient -> nätverk -> server -> nätverk -> spelklient. Detta innebär att varje spelares åtgärd endast utförs efter att ha gjort en rundresa över nätverket. På grund av detta skulle spelet verka fruktansvärt långsamt, så nästan direkt efter introduktionen av multiplayer i spelet, introducerades en mekanism för att dölja förseningar. Hiding delay simulerar spelarinmatning utan att ta hänsyn till andra spelares handlingar och serverns beslut.

Megapack: hur Factorio-utvecklarna lyckades lösa problemet med multiplayer för 200 spelare
Factorio har ett speltillstånd Game State är det fullständiga tillståndet för kortet, spelaren, enheterna och allt annat. Det simuleras deterministiskt i alla klienter baserat på de åtgärder som tas emot från servern. Speltillståndet är heligt, och om det någon gång börjar skilja sig från servern eller någon annan klient, sker avsynkronisering.

Utom Game State vi har ett tillstånd av förseningar Latensstatus. Den innehåller en liten delmängd av grundtillståndet. Latensstatus inte heligt och representerar helt enkelt en bild av hur speltillståndet kommer att se ut i framtiden baserat på spelarens indata Inmatningsåtgärder.

För detta ändamål lagrar vi en kopia av den skapade Inmatningsåtgärder i förseningskön.

Megapack: hur Factorio-utvecklarna lyckades lösa problemet med multiplayer för 200 spelare
Det vill säga, i slutet av processen på klientsidan ser bilden ut ungefär så här:

  1. Tillämpa Inmatningsåtgärder alla spelare till Game State hur dessa inmatningsåtgärder togs emot från servern.
  2. Vi tar bort allt från förseningskön Inmatningsåtgärder, som enligt servern redan har tillämpats på Game State.
  3. Radera Latensstatus och återställ den så att den ser exakt likadan ut som Game State.
  4. Vi tillämpar alla åtgärder från fördröjningskön till Latensstatus.
  5. Baserat på data Game State и Latensstatus Vi återger spelet till spelaren.

Allt detta upprepas i varje takt.

För svårt? Slappna inte av, det här är inte allt. För att kompensera för opålitliga internetanslutningar har vi skapat två mekanismer:

  • Missade bockar: när servern bestämmer det Inmatningsåtgärder kommer att utföras på takten i spelet, sedan om han inte fick Inmatningsåtgärder någon spelare (till exempel på grund av ökad fördröjning) kommer han inte att vänta, utan kommer att informera denna klient "Jag tog inte hänsyn till din Inmatningsåtgärder, jag ska försöka lägga till dem i nästa fält." Detta görs så att kartuppdateringen inte saktar ner för alla andra på grund av problem med anslutningen (eller datorn) för en spelare. Det är värt att notera Inmatningsåtgärder ignoreras inte, utan läggs helt enkelt åt sidan.
  • Full tur och retur latens: Servern försöker gissa vilken tur och retur latens mellan klienten och servern är för varje klient. Var 5:e sekund förhandlar den om en ny latens med klienten vid behov (baserat på hur anslutningen har betett sig tidigare), och ökar eller minskar tur och retur latensen i enlighet därmed.

På egen hand är dessa mekanismer ganska enkla, men när de används tillsammans (vilket ofta händer vid anslutningsproblem) blir kodens logik svår att hantera och med många kantfall. Dessutom, när dessa mekanismer kommer till spel måste servern och fördröjningskön implementera specialen korrekt Ingångsåtgärd med titeln StopMovementInTheNextTick. Tack vare detta, om det finns problem med anslutningen, kommer karaktären inte att springa på egen hand (till exempel framför ett tåg).

Nu måste vi förklara för dig hur val av enhet fungerar. En av de överförda typerna Ingångsåtgärd är en ändring i tillståndet för val av enhet. Den talar om för alla vilken enhet spelaren svävar över. Som du kan föreställa dig är detta en av de vanligaste inmatningsåtgärderna som skickas av klienter, så för att spara bandbredd har vi optimerat den för att ta upp så lite utrymme som möjligt. Sättet det fungerar på är att när varje enhet väljs, istället för att lagra absoluta, högprecisions kartkoordinater, lagrar spelet en låg precision relativ offset från det tidigare valet. Detta fungerar bra eftersom musval tenderar att vara mycket nära det tidigare valet. Detta ställer två viktiga krav: Inmatningsåtgärder De får aldrig hoppa över och måste fyllas i i rätt ordning. Dessa krav är uppfyllda för Game State. Men sedan uppgiften Latensläge i "ser bra ut" för spelaren är de inte nöjda i fördröjningsläget. Latensstatus tar inte hänsyn till många kantfall, förknippad med att hoppa över klockcykler och ändra överföringsfördröjningar tur och retur.

Du kan redan gissa vart detta tar vägen. Vi börjar äntligen se orsakerna till megapackproblemet. Roten till problemet är att entitetsvalslogiken förlitar sig på Latensstatus, och detta tillstånd innehåller inte alltid den korrekta informationen. Därför genereras ett megapaket ungefär så här:

  1. Spelaren har anslutningsproblem.
  2. Mekanismer för att hoppa över klockcykler och reglera fördröjningen av sändning tur och retur kommer in i bilden.
  3. Fördröjningstillståndskön tar inte hänsyn till dessa mekanismer. Detta gör att vissa åtgärder tas bort i förtid eller utförs i fel ordning, vilket resulterar i felaktiga Latensstatus.
  4. Spelaren har ett anslutningsproblem och simulerar upp till 400 cykler för att komma ikapp servern.
  5. Vid varje bock genereras en ny åtgärd, som ändrar entitetsvalet, och förbereds för att skickas till servern.
  6. Klienten skickar en mega-batch av 400+ ändringar av entitetsval till servern (och med andra åtgärder: skjuttillstånd, gångtillstånd, etc. led också av detta problem).
  7. Servern tar emot 400 inmatningsåtgärder. Eftersom det inte är tillåtet att hoppa över några inmatningsåtgärder, beordrar det alla klienter att utföra dessa åtgärder och skickar dem över nätverket.

Det ironiska är att en mekanism utformad för att spara bandbredd slutade med att skapa enorma nätverkspaket.

Vi åtgärdade det här problemet genom att åtgärda alla edge-fall av uppdateringar och backlog-köstöd. Även om det tog ganska lång tid, var det i slutändan värt att få det rätt i stället för att förlita sig på snabba hacks.

Källa: will.com

Lägg en kommentar