Megapack: Kako je Factorio riješio problem za više igrača za 200 igrača

Megapack: Kako je Factorio riješio problem za više igrača za 200 igrača
U maju ove godine učestvovao sam kao igrač MMO događaji KatherineOfSky. Primijetio sam da kada broj igrača dostigne određeni broj, svakih nekoliko minuta neki od njih “otpadnu”. Na vašu sreću (ali ne i na mene), ja sam bio jedan od onih igrača koji su prekinuli vezu svaki put, čak i uz dobru vezu. Ovo sam shvatio kao lični izazov i počeo da tražim uzroke problema. Nakon tri sedmice otklanjanja grešaka, testiranja i popravki, greška je konačno ispravljena, ali putovanje nije bilo tako lako.

Probleme sa igrama za više igrača je veoma teško ući u trag. Obično se javljaju pod vrlo specifičnim mrežnim parametrima i vrlo specifičnim uvjetima igre (u ovom slučaju, s više od 200 igrača). Čak i kada se problem može reproducirati, on se ne može ispravno otkloniti jer umetanje tačaka prekida zaustavlja igru, zbunjuje tajmere i obično uzrokuje prekid veze. Ali zahvaljujući upornosti i divnom alatu tzv nespretan Uspeo sam da saznam šta se dešava.

Ukratko, zbog greške i nepotpune implementacije simulacije stanja kašnjenja, klijent bi se ponekad našao u situaciji da je morao poslati mrežni paket koji se sastoji od akcija odabira ulaza igrača od približno 400 entiteta igre u jednom ciklusu takta ( ovo zovemo "mega-paket"). Server tada ne samo da mora ispravno primiti sve ove ulazne radnje, već ih i poslati svim drugim klijentima. Ako imate 200 klijenata, to brzo postaje problem. Veza sa serverom brzo postaje začepljena, što dovodi do gubitka paketa i kaskade ponovno traženih paketa. Odgađanje ulazne akcije onda uzrokuje još više klijenata da pošalju megapakete, uzrokujući da lavina postane još veća. Srećni klijenti uspevaju da se oporave, svi ostali padaju.

Megapack: Kako je Factorio riješio problem za više igrača za 200 igrača
Problem je bio prilično fundamentalan i trebalo mi je 2 sedmice da ga riješim. Prilično je tehnički, pa ću u nastavku objasniti sočne tehničke detalje. Ali prvo, morate znati da je od verzije 0.17.54, objavljene 4. juna, suočen s privremenim problemima s vezom, multiplayer postao stabilniji, a kašnjenja u skrivanju su postala mnogo manje bugova (manje usporavanja i teleportiranja). Takođe sam promenio način na koji je skriveno kašnjenje u borbi i nadam se da će to učiniti malo glatkijim.

Mega paket za više igrača - Tehnički detalji

Pojednostavljeno rečeno, multiplayer u igri funkcionira ovako: svi klijenti simuliraju stanje igre, primajući i šaljući samo unos igrača (koji se nazivaju "akcije unosa", Input Actions). Glavni zadatak servera je prijenos Input Actions i kontroliraju da svi klijenti izvode iste radnje u istom ciklusu takta. Više o ovome možete pročitati u postu FFF-149.

Budući da server mora donositi odluke o tome koje radnje da izvede, radnje igrača se kreću otprilike ovim putem: radnja igrača -> klijent igre -> mreža -> server -> mreža -> klijent igre. To znači da se radnja svakog igrača izvodi tek nakon kružnog putovanja kroz mrežu. Zbog toga bi igra izgledala užasno spora, pa je skoro odmah nakon uvođenja multiplayera u igru ​​uveden mehanizam za skrivanje kašnjenja. Skrivanje kašnjenja simulira unos igrača bez uzimanja u obzir radnji drugih igrača i odluka servera.

Megapack: Kako je Factorio riješio problem za više igrača za 200 igrača
Factorio ima stanje igre Stanje igre je kompletno stanje kartice, igrača, entiteta i svega ostalog. U svim klijentima se deterministički simulira na osnovu akcija primljenih od servera. Stanje igre je sveto, i ako ikada počne da se razlikuje od servera ili bilo kog drugog klijenta, dolazi do desinhronizacije.

osim Stanje igre imamo stanje kašnjenja Latency State. Sadrži mali podskup osnovnog stanja. Latency State nije sveto i jednostavno predstavlja sliku kako će stanje igre izgledati u budućnosti na osnovu unosa igrača Input Actions.

U tu svrhu pohranjujemo kopiju kreiranog Input Actions u redu čekanja.

Megapack: Kako je Factorio riješio problem za više igrača za 200 igrača
Odnosno, na kraju procesa na strani klijenta slika izgleda otprilike ovako:

  1. Prijavljujemo se Input Actions svim igračima Stanje igre način na koji su ove ulazne akcije primljene od servera.
  2. Uklanjamo sve iz reda čekanja Input Actions, koji su, prema serveru, već primijenjeni Stanje igre.
  3. Izbriši Latency State i resetirajte ga tako da izgleda potpuno isto kao Stanje igre.
  4. Primjenjujemo sve radnje iz reda čekanja na Latency State.
  5. Na osnovu podataka Stanje igre и Latency State Predstavljamo igru ​​igraču.

Sve se to ponavlja u svakoj takti.

Previše teško? Ne opuštajte se, ovo nije sve. Kako bismo kompenzirali nepouzdane internetske veze, kreirali smo dva mehanizma:

  • Propušteni tikovi: kada server to odluči Input Actions će biti izvršen u taktu igre, onda ako nije primio Input Actions neki igrač (na primjer, zbog povećanog kašnjenja), neće čekati, već će obavijestiti ovog klijenta „Nisam uzeo u obzir vaše Input Actions, pokušat ću ih dodati u sljedećoj traci.” Ovo je učinjeno tako da zbog problema sa vezom (ili računarom) jednog igrača, ažuriranje mape ne usporava za sve ostale. Vrijedi to napomenuti Input Actions nisu zanemareni, već jednostavno ostavljeni po strani.
  • Potpuna latencija povratnog puta: Server pokušava pogoditi kolika je latencija povratnog putovanja između klijenta i servera za svakog klijenta. Svakih 5 sekundi pregovara o novom kašnjenju s klijentom ako je potrebno (zasnovano na tome kako se veza ponašala u prošlosti) i u skladu s tim povećava ili smanjuje povratno kašnjenje.

Sami po sebi, ovi mehanizmi su prilično jednostavni, ali kada se koriste zajedno (što se često dešava kod problema sa vezom), logikom koda postaje teško upravljati i sa mnogo rubnih slučajeva. Dodatno, kada ovi mehanizmi dođu u igru, server i red čekanja moraju pravilno implementirati specijal Input Action pod imenom StopMovementInTheNextTick. Zahvaljujući tome, ako postoje problemi s vezom, lik neće trčati sam (na primjer, ispred voza).

Sada vam moramo objasniti kako funkcionira odabir entiteta. Jedan od tipova koji se prenose Input Action je promjena u stanju odabira entiteta. Svima govori nad kojim entitetom igrač lebdi. Kao što možete zamisliti, ovo je jedna od najčešćih ulaznih radnji koje šalju klijenti, tako da smo je optimizirali da zauzme što manje prostora kako bismo uštedjeli propusni opseg. Način na koji radi je da dok je svaki entitet odabran, umjesto pohranjivanja apsolutnih, visoko preciznih koordinata karte, igra pohranjuje relativni pomak niske preciznosti u odnosu na prethodni odabir. Ovo dobro funkcionira jer odabiri miša obično budu vrlo bliski prethodnom odabiru. Ovo postavlja dva važna zahtjeva: Input Actions Nikada ih ne treba preskakati i moraju se popuniti ispravnim redoslijedom. Ovi zahtjevi su ispunjeni za Stanje igre. Ali pošto zadatak Stanje kašnjenja u "izgledaju dovoljno dobro" za igrača, oni nisu zadovoljni u stanju kašnjenja. Latency State ne uzima u obzir mnogo rubnih slučajeva, povezano sa preskakanjem ciklusa takta i promjenom kašnjenja povratnog prijenosa.

Već možete pretpostaviti kuda ovo vodi. Konačno počinjemo uviđati razloge za problem megapaketa. Koren problema je u tome što se oslanja logika odabira entiteta Latency State, a ovo stanje ne sadrži uvijek tačne informacije. Stoga se megapaket generiše otprilike ovako:

  1. Plejer ima problema sa vezom.
  2. Mehanizmi za preskakanje ciklusa takta i regulaciju kašnjenja povratnog prijenosa dolaze u igru.
  3. Red stanja kašnjenja ne uzima u obzir ove mehanizme. To uzrokuje da se neke radnje prerano uklone ili izvedu pogrešnim redoslijedom, što rezultira neispravnim Latency State.
  4. Igrač ima problem sa vezom i, da bi sustigao server, simulira do 400 ciklusa.
  5. Prilikom svakog tiketa, nova akcija, mijenjajući izbor entiteta, se generira i priprema za slanje na server.
  6. Klijent šalje serveru mega-seriju od 400+ promjena odabira entiteta (a sa drugim radnjama: stanja snimanja, stanja hodanja, itd. također pati od ovog problema).
  7. Server prima 400 ulaznih radnji. Pošto nije dozvoljeno preskočiti bilo koju radnju unosa, on naređuje svim klijentima da izvrše te radnje i šalje ih preko mreže.

Ironija je u tome što je mehanizam dizajniran da uštedi propusni opseg na kraju stvorio ogromne mrežne pakete.

Ovaj problem smo riješili tako što smo popravili sve rubne slučajeve podrške za ažuriranje i zaostali red čekanja. Iako je za to trebalo dosta vremena, na kraju je vrijedilo da se to ispravi, a ne da se oslanja na brze hakove.

izvor: www.habr.com

Dodajte komentar