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

Megapack: Kako je Factorio riješio problem više igrača s 200 igrača
U svibnju ove godine sudjelovao sam kao igrač u KatherineOfSky MMO događaji. Primijetio sam da kada broj igrača dođe do određene brojke, svakih nekoliko minuta neki od njih "otpadne". Na tvoju sreću (ali ne i na mene), ja sam bio jedan od tih igrača svaki putčak i uz dobru vezu. Shvatio sam to kao osobni izazov i počeo tražiti uzroke problema. Nakon tri tjedna otklanjanja pogrešaka, testiranja i popravljanja, greška je konačno ispravljena, ali putovanje nije bilo nimalo lako.

Probleme u igrama za više igrača vrlo je teško otkriti. Obično se pojavljuju pod vrlo specifičnim mrežnim parametrima i pod vrlo specifičnim stanjima igre (u ovom slučaju preko 200 igrača). Čak i kada se problem može reproducirati, ne može se pravilno otkloniti pogreške jer umetanje prijelomnih točaka zaustavlja igru, kvari mjerače vremena i obično uzrokuje prekid veze zbog isteka vremena. Ali zahvaljujući upornosti i prekrasnom alatu tzv nespretan Uspjela sam shvatiti što se događa.

Ukratko, zbog greške i nepotpune implementacije simulacije stanja kašnjenja, klijent se ponekad našao u situaciji u kojoj je morao poslati mrežni paket u jednom ciklusu takta, koji se sastoji od radnji unosa igrača za odabir približno 400 entiteta igre ( zovemo ga "megapaket"). Nakon toga, poslužitelj ne samo da mora ispravno primiti sve ove ulazne akcije, već ih i poslati svim ostalim klijentima. Ako imate 200 klijenata, to brzo postane problem. Kanal do poslužitelja brzo postaje začepljen, što rezultira izgubljenim paketima i kaskadom ponovno traženih paketa. Odgađanje radnji unosa tada uzrokuje da više klijenata počne slati megapakete, a njihova lavina postaje još jača. Uspješni klijenti se uspiju oporaviti, svi ostali otpadaju.

Megapack: Kako je Factorio riješio problem više igrača s 200 igrača
Problem je bio temeljan i trebalo mi je 2 tjedna 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. lipnja, usprkos privremenim problemima s vezom, igra za više igrača postala stabilnija, a skrivanje kašnjenja puno je manje buggy (manje kočenja i teleportiranja). Također, promijenio sam način na koji su odgode borbe skrivene, i nadam se da će ih to učiniti malo glatkijim.

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

Pojednostavljeno rečeno, više igrača u igrici funkcionira ovako: svi klijenti simuliraju stanje igre primanjem i slanjem samo unosa igrača (koji se nazivaju "radnje unosa" Radnje unosa). Glavni zadatak poslužitelja je prijenos Radnje unosa i osiguravanje da svi klijenti izvode iste radnje u istom ciklusu. Više o tome možete pročitati u postu. FFF-149.

Budući da poslužitelj mora donositi odluke o tome koje će radnje poduzeti, radnje igrača kreću se sljedećim putem: radnja igrača -> klijent igre -> mreža -> poslužitelj -> mreža -> klijent igre. To znači da se svaka radnja igrača izvodi tek nakon što je napravio kružni put kroz mrežu. Zbog toga bi se igra činila užasno sporom, pa je gotovo odmah nakon pojave multiplayera u igri uveden mehanizam za skrivanje kašnjenja. Skrivanje latencije simulira unos igrača bez razmatranja radnji drugih igrača i donošenja odluka poslužitelja.

Megapack: Kako je Factorio riješio problem više igrača s 200 igrača
Factorio ima stanje igre stanje igre je potpuno stanje mape, igrača, entiteta i svega ostalog. To je deterministički simulirano u svim klijentima na temelju radnji primljenih od poslužitelja. Stanje igre je svetinja i ako se ikada počne razlikovati od poslužitelja ili bilo kojeg drugog klijenta, tada dolazi do desinkronizacije.

Osim stanje igre imamo stanje kašnjenja Stanje latencije. Sadrži mali podskup glavnog stanja. Stanje latencije nije sveto i samo predstavlja sliku kako će stanje igre izgledati u budućnosti na temelju unosa igrača Radnje unosa.

Da bismo to učinili, čuvamo kopiju generiranog Radnje unosa u redu za kašnjenje.

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

  1. primijeniti Radnje unosa svi igrači do stanje igre način na koji su te radnje unosa primljene od poslužitelja.
  2. Uklonite sve iz reda odgode Radnje unosa, koji su prema poslužitelju već primijenjeni stanje igre.
  3. ukloniti Stanje latencije i resetirajte ga tako da izgleda potpuno isto kao stanje igre.
  4. Primijeni sve radnje iz reda odgode na Stanje latencije.
  5. Na temelju podataka stanje igre и Stanje latencije prikazati igru ​​igraču.

Sve se to ponavlja u svakom taktu.

Preteško? Ne opuštajte se, to nije sve. Kako bismo kompenzirali nepouzdane internetske veze, stvorili smo dva mehanizma:

  • Preskočeni tikovi: kada poslužitelj to odluči Radnje unosa će se izvršiti u taktu igre, onda ako nije primio Radnje unosa nekog igrača (na primjer, zbog povećanog kašnjenja), on neće čekati, već će obavijestiti ovog klijenta „Nisam uzeo u obzir vaše Radnje unosa, pokušat ću ih dodati u sljedećoj traci. To je učinjeno tako da se zbog problema s vezom (ili s računalom) jednog igrača ažuriranje karte ne usporava za sve ostale. Vrijedno je napomenuti da Radnje unosa ne ignoriraju se, nego jednostavno odgađaju.
  • Potpuna povratna latencija: Poslužitelj pokušava pogoditi koja je povratna latencija između klijenta i poslužitelja za svakog klijenta. Svakih 5 sekundi dogovara novu odgodu s klijentom prema potrebi (ovisno o tome kako se veza ponašala u prošlosti) i u skladu s tim povećava ili smanjuje odgodu povratnog putovanja.

Sami po sebi, ti su mehanizmi prilično jednostavni, ali kada se koriste zajedno (što se često događa s problemima veze), logikom koda postaje teško upravljati i s puno rubnih slučajeva. Osim toga, kada ovi mehanizmi uđu u igru, poslužitelj i red čekanja moraju ispravno implementirati poseban Radnja unosa pravo StopMovementInTheNextTick. Zahvaljujući tome, u slučaju problema s vezom, lik neće trčati sam (na primjer, ispod vlaka).

Sada vam moram objasniti kako funkcionira odabir entiteta. Jedan od prošlih tipova Radnja unosa je promjena u odabirnom stanju entiteta. Svima govori preko kojeg je entiteta igrač prešao mišem. Kao što vidite, ovo je jedna od najčešćih radnji unosa koju šalju klijenti, pa smo je optimizirali kako bismo uštedjeli propusnost tako da zauzima što manje prostora. Ovo se implementira ovako: kada je svaki entitet odabran, umjesto pohranjivanja apsolutnih, visokopreciznih koordinata karte, igra pohranjuje relativni pomak niske preciznosti u odnosu na prethodni odabir. Ovo dobro funkcionira jer se odabir mišem obično događa vrlo blizu prethodnog odabira. To dovodi do dva važna zahtjeva: Radnje unosa nikada se ne smije preskočiti i mora se raditi ispravnim redoslijedom. Ovi zahtjevi su ispunjeni za stanje igre. Ali budući da je zadatak stanje latencije u "izgledaju dovoljno dobro" za igrača, nisu zadovoljni u stanju odgode. Stanje latencije ne uzima u obzir mnogo graničnih slučajevapovezan s preskakanjem satova i promjenom odgode povratnog prijenosa.

Već možete pretpostaviti kamo ovo vodi. Konačno počinjemo uviđati uzroke problema s megapaketima. Korijen problema je u tome što se logika odabira entiteta oslanja na Stanje latencije, a ovo stanje ne sadrži uvijek točne informacije. Dakle, megapaket se generira ovako:

  1. Igrač ima problema s vezom.
  2. Mehanizmi za preskakanje ciklusa i reguliranje kašnjenja povratnog prijenosa dolaze u obzir.
  3. Red čekanja stanja kašnjenja ne uzima u obzir ove mehanizme. To uzrokuje prerano uklanjanje nekih radnji ili izvođenje pogrešnim redoslijedom, što rezultira netočnim Stanje latencije.
  4. Igrač nema problema s vezom i simulira do 400 ciklusa da uhvati korak s poslužiteljem.
  5. U svakom ciklusu generira se nova akcija i priprema za slanje na poslužitelj, mijenjajući odabir entiteta.
  6. Klijent šalje megapaket s 400+ promjena odabira entiteta na poslužitelj (i s drugim radnjama: stanje aktiviranja, stanje hodanja itd. također je patilo od ovog problema).
  7. Poslužitelj prima 400 ulaznih akcija. Budući da nije dopušteno preskočiti niti jednu radnju unosa, upućuje sve klijente da izvrše te radnje i šalje ih preko mreže.

Ironija je da je mehanizam dizajniran za očuvanje propusnosti rezultirao ogromnim mrežnim paketima.

Riješili smo ovaj problem ispravljanjem svih rubnih slučajeva ažuriranja i podrške za čekanje čekanja. Iako je trebalo dosta vremena, vrijedilo je na kraju sve ispraviti umjesto oslanjanja na brze hakove.

Izvor: www.habr.com

Dodajte komentar