Megapack: како програмерите на Factorio успеаја да го решат проблемот со мултиплеер за 200 играчи

Megapack: како програмерите на Factorio успеаја да го решат проблемот со мултиплеер за 200 играчи
Во мај оваа година учествував како играч во MMO настани KatherineOfSky. Забележав дека кога бројот на играчи ќе достигне одреден број, на секои неколку минути некој од нив „паѓа“. За твоја среќа (но не и за мене), јас бев еден од оние играчи кои се исклучија секој пат, дури и со добра врска. Го сфатив ова како личен предизвик и почнав да ги барам причините за проблемот. По три недели дебагирање, тестирање и поправки, грешката конечно беше поправена, но патувањето не беше толку лесно.

Проблемите со игрите со повеќе играчи се многу тешко да се пронајдат. Тие обично се појавуваат под многу специфични мрежни параметри и многу специфични услови за игра (во овој случај, имајќи повеќе од 200 играчи). И дури и кога проблемот може да се репродуцира, тој не може правилно да се дебагира бидејќи вметнувањето точки на прекин ја запира играта, ги збунува тајмерите и обично предизвикува истекување на врската. Но, благодарение на упорноста и прекрасната алатка наречена несмасна Успеав да дознаам што се случува.

Накратко, поради грешка и нецелосна имплементација на симулацијата на латентната состојба, клиентот понекогаш би се нашол во ситуација кога морал да испрати мрежен пакет кој се состои од дејства за избор на влез на играчот од приближно 400 ентитети на играта во еден часовник ( ние ова го нарекуваме „мега-пакет“). Тогаш серверот не само што мора правилно да ги прими сите овие влезни дејства, туку и да ги испрати до сите други клиенти. Ако имате 200 клиенти, ова брзо станува проблем. Врската до серверот брзо се затнува, што доведува до губење на пакети и каскада од повторно побарани пакети. Одложувањето на акцијата за внесување потоа предизвикува уште повеќе клиенти да испраќаат мегапакети, предизвикувајќи лавината да стане уште поголема. Среќните клиенти успеваат да се опорават, сите други паѓаат.

Megapack: како програмерите на Factorio успеаја да го решат проблемот со мултиплеер за 200 играчи
Проблемот беше прилично суштински и ми требаа 2 недели да го поправам. Тоа е прилично технички, па ќе ги објаснам сочните технички детали подолу. Но, прво, треба да знаете дека од верзијата 0.17.54, објавена на 4 јуни, со оглед на привремените проблеми со поврзувањето, мултиплеер стана постабилен, а криењето на доцнењата стана многу помалку кабриолет (помалку забавување и телепортирање). Го сменив и начинот на кој се крие борбеното заостанување и се надевам дека ова ќе го направи малку помазно.

Мега пакет со повеќе играчи - Технички детали

Едноставно кажано, мултиплеер во играта функционира вака: сите клиенти ја симулираат состојбата на играта, примајќи и испраќајќи само влез од играчот (наречен „влезни дејства“, Влезни дејства). Главната задача на серверот е да пренесува Влезни дејства и контролирајте сите клиенти да ги извршуваат истите дејства во ист такт циклус. Можете да прочитате повеќе за ова во објавата ФФФ-149.

Бидејќи серверот мора да донесува одлуки за тоа кои дејства да ги изврши, дејствата на играчот се движат приближно по оваа патека: акција на играчот -> клиент за игра -> мрежа -> сервер -> мрежа -> клиент за игра. Ова значи дека акцијата на секој играч се изведува само откако ќе направи кружно патување низ мрежата. Поради ова, играта ќе изгледаше ужасно бавна, па речиси веднаш по воведувањето на мултиплеер во играта, беше воведен механизам за криење на доцнењата. Сокривањето на доцнењето го симулира внесувањето на играчот без да ги земе предвид дејствата на другите играчи и одлуките на серверот.

Megapack: како програмерите на Factorio успеаја да го решат проблемот со мултиплеер за 200 играчи
Факторио има состојба на игра Игра држава е целосната состојба на картичката, играчот, ентитетите и сè друго. Детерминистички е симулиран кај сите клиенти врз основа на дејствата добиени од серверот. Состојбата на играта е света, и ако некогаш почне да се разликува од серверот или кој било друг клиент, тогаш се случува десинхронизација.

Но Игра држава имаме состојба на доцнење Латентна состојба. Содржи мало подмножество од основната состојба. Латентна состојба не е свето и едноставно претставува слика за тоа како ќе изгледа состојбата на играта во иднина врз основа на влезовите на играчите Влезни дејства.

За таа цел, складираме копија од создаденото Влезни дејства во редот за одложување.

Megapack: како програмерите на Factorio успеаја да го решат проблемот со мултиплеер за 200 играчи
Односно, на крајот од процесот на страната на клиентот сликата изгледа вака:

  1. Пријавете се Влезни дејства сите играчи да Игра држава начинот на кој овие влезни дејства биле примени од серверот.
  2. Отстрануваме сè од редот за одложување Влезни дејства, на кои, според серверот, веќе се примени Игра држава.
  3. Избриши Латентна состојба и ресетирајте го за да изгледа сосема исто како Игра држава.
  4. Ги применуваме сите дејства од редот за одложување до Латентна состојба.
  5. Врз основа на податоци Игра држава и Латентна состојба Ја прикажуваме играта на играчот.

Сето тоа се повторува во секоја мерка.

Премногу тешко? Не опуштајте се, ова не е се. За да ги компензираме несигурните интернет конекции, создадовме два механизми:

  • Пропуштени крлежи: кога серверот ќе одлучи за тоа Влезни дејства ќе биде погубен во ритамот на играта, тогаш ако не примил Влезни дејства некој играч (на пример, поради зголемено доцнење), тој нема да чека, туку ќе го извести овој клиент „Не го земав предвид вашето Влезни дејства, ќе се обидам да ги додадам во следната лента“. Ова е направено така што поради проблеми со поврзувањето (или компјутерот) на еден играч, ажурирањето на картата не се забавува за сите други. Вреди да се напомене дека Влезни дејства не се игнорираат, туку едноставно се ставаат настрана.
  • Целосна латентност за повратен пат: Серверот се обидува да погоди колкава е латентноста за повратен пат помеѓу клиентот и серверот за секој клиент. На секои 5 секунди, тој преговара за нова латентност со клиентот доколку е потребно (врз основа на тоа како врската се однесувала во минатото) и соодветно ја зголемува или намалува доцнењето за повратен пат.

Самите, овие механизми се прилично едноставни, но кога се користат заедно (што често се случува со проблеми со поврзувањето), логиката на кодот станува тешка за управување и со многу случаи на рабови. Дополнително, кога овие механизми ќе влезат во игра, серверот и редот за одложување мора правилно да го имплементираат специјалното Влезно дејство наречен StopMovementInTheNextTick. Благодарение на ова, ако има проблеми со врската, ликот нема да трча сам (на пример, пред воз).

Сега треба да ви објасниме како функционира изборот на ентитет. Еден од пренесените типови Влезно дејство е промена во состојбата на избор на ентитет. На сите им кажува на кој ентитет лебди играчот. Како што можете да замислите, ова е едно од најчестите дејства за внесување испратени од клиентите, така што за да заштедиме пропусен опсег, го оптимизиравме да зафаќа што е можно помалку простор. Начинот на кој функционира е дека како што се избира секој ентитет, наместо да складира апсолутни, високопрецизни координати на картата, играта складира релативно офсет со мала прецизност од претходниот избор. Ова функционира добро затоа што селекциите на глувчето имаат тенденција да бидат многу блиску до претходниот избор. Ова покренува два важни барања: Влезни дејства Тие никогаш не треба да се прескокнуваат и мора да се пополнат по правилен редослед. Овие барања се исполнети за Игра држава. Но од задачата Латентна состојба во „изгледа доволно добро“ за играчот, тие не се задоволни во состојбата на доцнење. Латентна состојба не зема предвид многу рабови, поврзано со прескокнување на циклусите на часовникот и менување на доцнењата на преносот во повратен пат.

Веќе можете да погодите каде оди ова. Конечно почнуваме да ги гледаме причините за проблемот со мегапак. Коренот на проблемот е во тоа што се потпира логиката за избор на ентитети Латентна состојба, и оваа состојба не секогаш ги содржи точните информации. Затоа, мегапакет се генерира нешто вака:

  1. Плеерот има проблеми со поврзувањето.
  2. Механизмите за прескокнување на циклусите на часовникот и за регулирање на доцнењето на преносот во кружен пат влегуваат во игра.
  3. Редот за одложена состојба не ги зема предвид овие механизми. Ова предизвикува некои дејства да се отстранат предвреме или да се извршат по погрешен редослед, што резултира со неточни Латентна состојба.
  4. Плеерот има проблем со поврзувањето и, за да го достигне серверот, симулира до 400 циклуси.
  5. На секој штиклирање, се генерира ново дејство, менувајќи го изборот на ентитет, и се подготвува за испраќање до серверот.
  6. Клиентот испраќа мега-серија од 400+ промени во изборот на ентитети на серверот (и со други дејства: состојби на снимање, состојби на одење итн. исто така страдаат од овој проблем).
  7. Серверот прима 400 влезни дејства. Бидејќи не е дозволено да се прескокнуваат никакви влезни дејства, им наредува на сите клиенти да ги извршат тие дејства и ги испраќа низ мрежата.

Иронијата е во тоа што механизмот дизајниран да заштеди пропусен опсег заврши со создавање огромни мрежни пакети.

Го решивме овој проблем со поправање на сите рабови на ажурирања и поддршка за заостанати редици. Иако беше потребно доста време, на крајот вредеше да се поправи, наместо да се потпираме на брзи хакери.

Извор: www.habr.com

Додадете коментар