Megapack: Cum a rezolvat Factorio problema multiplayer cu 200 de jucători

Megapack: Cum a rezolvat Factorio problema multiplayer cu 200 de jucători
În luna mai a acestui an, am participat ca jucător la Evenimente MMO KatherineOfSky. Am observat că atunci când numărul de jucători ajunge la un anumit număr, la fiecare câteva minute unii dintre ei „cad”. Din fericire pentru tine (dar nu pentru mine), am fost unul dintre acei jucători de fiecare datăchiar și cu o conexiune bună. Am luat-o ca pe o provocare personală și am început să caut cauzele problemei. După trei săptămâni de depanare, testare și remediere, eroarea este în sfârșit remediată, dar călătoria nu a fost chiar atât de ușoară.

Problemele din jocurile multiplayer sunt foarte greu de găsit. Ele apar de obicei sub parametri de rețea foarte specifici și în stări de joc foarte specifice (în acest caz, peste 200 de jucători). Și chiar și atunci când o problemă poate fi reprodusă, aceasta nu poate fi depanată corespunzător, deoarece inserarea punctelor de întrerupere oprește jocul, încurcă temporizatoarele și, de obicei, face ca conexiunea să expire din cauza unui timeout. Dar datorită perseverenței și unui instrument minunat numit neîndemânatic Am putut să-mi dau seama ce se întâmplă.

Pe scurt, din cauza unui bug și a implementării incomplete a simulării stării de întârziere, clientul s-a trezit uneori într-o situație în care trebuia să trimită un pachet de rețea într-un singur ciclu de ceas, constând în acțiuni de intrare de jucător pentru selectarea a aproximativ 400 de entități de joc ( îl numim „megapachet”). După aceea, serverul nu numai că trebuie să primească corect toate aceste acțiuni de intrare, ci și să le trimită tuturor celorlalți clienți. Dacă aveți 200 de clienți, acest lucru devine rapid o problemă. Canalul către server devine rapid înfundat, rezultând pachete pierdute și o cascadă de pachete re-solicitate. Amânarea acțiunilor de intrare face ca mai mulți clienți să înceapă să trimită megapachete, iar avalanșa lor devine și mai puternică. Clienții de succes reușesc să-și revină, restul cad.

Megapack: Cum a rezolvat Factorio problema multiplayer cu 200 de jucători
Problema a fost destul de fundamentală și mi-a luat 2 săptămâni să o rezolv. Este destul de tehnic, așa că voi explica detaliile tehnice suculente mai jos. Dar mai întâi trebuie să știți că de la versiunea 0.17.54, lansată pe 4 iunie, în fața problemelor temporare de conexiune, multiplayer-ul a devenit mai stabil, iar ascunderea întârzierii este mult mai puțin greșită (mai puține frânări și teleportare). De asemenea, am schimbat modul în care sunt ascunse întârzierile de luptă și sperăm că acest lucru le va face puțin mai lin.

Multiplayer Mega Pack - Detalii tehnice

Pentru a spune simplu, multiplayer într-un joc funcționează astfel: toți clienții simulează starea jocului primind și trimițând doar input-ul jucătorului (numite „acțiuni de intrare” Acțiuni de intrare). Sarcina principală a serverului este transferul Acțiuni de intrare și asigurarea faptului că toți clienții efectuează aceleași acțiuni în același ciclu. Puteți citi mai multe despre asta în postare. FFF-149.

Deoarece serverul trebuie să ia decizii cu privire la acțiunile pe care trebuie să le întreprindă, acțiunile jucătorului se deplasează pe următoarea cale: acțiunea jucătorului -> client de joc -> rețea -> server -> rețea -> client de joc. Aceasta înseamnă că fiecare acțiune a jucătorului este efectuată numai după ce acesta a făcut o cale dus-întors prin rețea. Din această cauză, jocul ar fi părut teribil de lent, așa că aproape imediat după apariția multiplayer-ului în joc a fost introdus un mecanism de ascundere a întârzierilor. Ascunderea latenței simulează intrarea jucătorului fără a lua în considerare acțiunile altor jucători și luarea deciziilor pe server.

Megapack: Cum a rezolvat Factorio problema multiplayer cu 200 de jucători
Factorio are o stare de joc starea jocului este starea completă a hărții, jucătorului, entităților și a tuturor celorlalte. Este simulat determinist în toți clienții pe baza acțiunilor primite de la server. Starea jocului este sacră și, dacă începe vreodată să difere de server sau de orice alt client, atunci are loc desincronizarea.

Dar starea jocului avem o stare de întârzieri Stare de latență. Conține un mic subset al stării principale. Stare de latență nu este sacru și reprezintă doar o imagine a modului în care va arăta starea jocului în viitor, pe baza intrărilor de la jucător Acțiuni de intrare.

Pentru a face acest lucru, păstrăm o copie a celor generate Acțiuni de intrare în coada de întârziere.

Megapack: Cum a rezolvat Factorio problema multiplayer cu 200 de jucători
Adică, la sfârșitul procesului din partea clientului, imaginea arată cam așa:

  1. aplica Acțiuni de intrare toți jucătorii să starea jocului modul în care aceste acțiuni de intrare au fost primite de la server.
  2. Eliminați totul din coada de întârziere Acțiuni de intrare, care, conform serverului, au fost deja aplicate starea jocului.
  3. Șterge Stare de latență și resetați-l astfel încât să arate exact la fel ca starea jocului.
  4. Aplicați toate acțiunile din coada de întârziere la Stare de latență.
  5. Pe baza datelor starea jocului и Stare de latență reda jocul jucătorului.

Toate acestea se repetă în fiecare măsură.

Prea dificil? Nu te relaxa, asta nu e tot. Pentru a compensa conexiunile nesigure la internet, am creat două mecanisme:

  • Tick-uri ignorate: când serverul decide asta Acțiuni de intrare va fi executat în tact de joc, atunci dacă nu a primit Acțiuni de intrare un jucător (de exemplu, din cauza unei întârzieri crescute), nu va aștepta, dar va informa acest client „Nu am luat în considerare Acțiuni de intrare, voi încerca să le adaug în bara următoare. Acest lucru se face astfel încât, din cauza problemelor cu conexiunea (sau cu computerul) unui jucător, actualizarea hărții să nu încetinească pentru toți ceilalți. Este demn de remarcat faptul că Acțiuni de intrare nu sunt ignorate, ci pur și simplu amânate.
  • Latență completă dus-întors: serverul încearcă să ghicească care este latența dus-întors între client și server pentru fiecare client. La fiecare 5 secunde, negociază o nouă întârziere cu clientul după cum este necesar (în funcție de modul în care s-a comportat conexiunea în trecut) și crește sau scade întârzierea dus-întors în consecință.

Prin ele însele, aceste mecanisme sunt destul de simple, dar atunci când sunt utilizate împreună (ceea ce se întâmplă adesea cu probleme de conectare), logica codului devine dificil de gestionat și cu o mulțime de cazuri marginale. În plus, atunci când aceste mecanisme intră în joc, serverul și coada de întârziere trebuie să implementeze corect o specială Acțiune de intrare denumit StopMovementInTheNextTick. Datorită acestui fapt, în cazul unor probleme de conexiune, personajul nu va rula singur (de exemplu, sub tren).

Acum trebuie să vă explic cum funcționează selecția entității. Unul dintre tipurile trecute Acțiune de intrare este o modificare a stării de selecție a unei entități. Le spune tuturor pe ce entitate a trecut jucătorul cu mouse-ul. După cum puteți vedea, aceasta este una dintre cele mai frecvente acțiuni de intrare trimise de clienți, așa că pentru a economisi lățime de bandă, am optimizat-o astfel încât să ocupe cât mai puțin spațiu posibil. Acest lucru este implementat astfel: atunci când fiecare entitate este selectată, în loc să stocheze coordonatele hărții absolute, de înaltă precizie, jocul stochează un offset relativ de precizie scăzută față de selecția anterioară. Acest lucru funcționează bine deoarece selecția mouse-ului are loc de obicei foarte aproape de selecția anterioară. Acest lucru dă naștere la două cerințe importante: Acțiuni de intrare nu trebuie sărită niciodată și trebuie făcută în ordinea corectă. Aceste cerințe sunt îndeplinite pt starea jocului. Dar din moment ce sarcina stare de latență în „arata suficient de bine” pentru jucător, nu sunt mulțumiți în starea de întârziere. Stare de latență nu ia in calcul multe cazuri limităasociate cu trecerea peste ceasuri și modificarea întârzierilor transmisiei dus-întors.

Poți deja ghici unde se duce asta. În cele din urmă, începem să vedem cauzele problemei megapachetului. Rădăcina problemei este că se bazează logica de selecție a entității Stare de latență, iar această stare nu conține întotdeauna informațiile corecte. Deci megapachetul este generat astfel:

  1. Jucătorul se confruntă cu probleme de conectare.
  2. Intră în joc mecanismele de sărire a ciclurilor și de reglare a întârzierii transmisiei dus-întors.
  3. Coada de stare de întârziere nu ține cont de aceste mecanisme. Acest lucru face ca unele acțiuni să fie eliminate prematur sau să ruleze în ordine greșită, rezultând o eroare Stare de latență.
  4. Playerul nu are nicio problemă de conexiune și simulează până la 400 de cicluri pentru a ajunge din urmă cu serverul.
  5. În fiecare ciclu, o nouă acțiune este generată și pregătită pentru a fi trimisă către server, modificând selecția entității.
  6. Clientul trimite un megapachet de 400+ modificări de selecție de entități către server (și cu alte acțiuni: starea de declanșare, starea de mers etc. a suferit și ele de această problemă).
  7. Serverul primește 400 de acțiuni de intrare. Deoarece nu este permis să săriți peste o singură acțiune de intrare, acesta instruiește toți clienții să efectueze aceste acțiuni și le trimite prin rețea.

Ironia este că un mecanism conceput pentru a conserva lățimea de bandă a dus la pachete uriașe de rețea.

Am rezolvat această problemă prin remedierea tuturor cazurilor marginale de actualizare și asistență pentru coadă de întârziere. Deși a durat destul de mult timp, a meritat să o facem corect în cele din urmă, mai degrabă decât să te bazezi pe hack-uri rapide.

Sursa: www.habr.com

Adauga un comentariu