Megapack: Как Factorio реши проблема с мултиплейър с 200 играчи

Megapack: Как Factorio реши проблема с мултиплейър с 200 играчи
През май тази година участвах като играч в KatherineOfSky MMO събития. Забелязах, че когато броят на играчите достигне определен брой, на всеки няколко минути някой от тях "отпада". За твое щастие (но не и за мен), аз бях един от тези играчи всеки пътдори и с добра връзка. Приех го като лично предизвикателство и започнах да търся причините за проблема. След три седмици отстраняване на грешки, тестване и коригиране, грешката най-накрая е коригирана, но пътуването не беше толкова лесно.

Проблемите в мултиплейър игрите са много трудни за проследяване. Те обикновено се случват при много специфични мрежови параметри и при много специфични състояния на играта (в този случай над 200 играча). И дори когато проблемът може да бъде възпроизведен, той не може да бъде правилно отстранен, защото вмъкването на точки на прекъсване спира играта, обърква таймерите и обикновено причинява изчакване на връзката поради изчакване. Но благодарение на постоянството и прекрасен инструмент, наречен тромав Успях да разбера какво става.

Накратко, поради грешка и непълно внедряване на симулацията на състоянието на закъснение, клиентът понякога се оказваше в ситуация, в която трябваше да изпрати мрежов пакет за един такт, състоящ се от въведени от играча действия за избиране на приблизително 400 обекта на играта ( ние го наричаме "мегапакет"). След това сървърът трябва не само да получи правилно всички тези входни действия, но и да ги изпрати на всички други клиенти. Ако имате 200 клиента, това бързо се превръща в проблем. Каналът към сървъра бързо се задръства, което води до изгубени пакети и каскада от повторно заявени пакети. След това отлагането на входните действия кара повече клиенти да започнат да изпращат мегапакети и тяхната лавина става още по-силна. Успешните клиенти успяват да се възстановят, всички останали падат.

Megapack: Как Factorio реши проблема с мултиплейър с 200 играчи
Проблемът беше доста фундаментален и ми отне 2 седмици да го поправя. Доста е технически, така че ще обясня пикантните технически подробности по-долу. Но първо трябва да знаете, че след версия 0.17.54, пусната на 4 юни, в лицето на временни проблеми с връзката, мултиплейърът стана по-стабилен и забавянето на скриването е много по-малко бъгово (по-малко спиране и телепортиране). Освен това промених начина, по който се скриват закъсненията на битката и се надявам, че това ще ги направи малко по-плавни.

Мега пакет за мултиплейър - технически подробности

Казано по-просто, мултиплейърът в една игра работи по следния начин: всички клиенти симулират състоянието на играта, като получават и изпращат само информация от играча (наречена „действия за въвеждане“ Действия за въвеждане). Основната задача на сървъра е да прехвърля Действия за въвеждане и гарантиране, че всички клиенти извършват едни и същи действия в един и същи цикъл. Можете да прочетете повече за това в публикацията. FFF-149.

Тъй като сървърът трябва да взема решения какви действия да предприеме, действията на играча се движат по следния път: действие на играча -> клиент на игра -> мрежа -> сървър -> мрежа -> клиент на игра. Това означава, че всяко действие на играча се извършва само след като е направил обиколен път през мрежата. Поради това играта би изглеждала ужасно бавна, така че почти веднага след появата на мултиплейър в играта беше въведен механизъм за скриване на забавяния. Скриването на латентността симулира въвеждането на играч, без да отчита действията на другите играчи и вземането на решения от сървъра.

Megapack: Как Factorio реши проблема с мултиплейър с 200 играчи
Factorio има състояние на играта състояние на играта е пълното състояние на картата, играча, обектите и всичко останало. Той се симулира детерминистично във всички клиенти въз основа на действия, получени от сървъра. Състоянието на играта е свещено и ако някога започне да се различава от сървъра или друг клиент, тогава настъпва десинхронизация.

с изключение на състояние на играта имаме състояние на забавяне Състояние на латентност. Той съдържа малко подмножество от основното състояние. Състояние на латентност не е свещено и просто представлява картина на това как ще изглежда състоянието на играта в бъдеще въз основа на входовете от играча Действия за въвеждане.

За целта запазваме копие на генерирания Действия за въвеждане в опашката за забавяне.

Megapack: Как Factorio реши проблема с мултиплейър с 200 играчи
Тоест в края на процеса от страна на клиента картината изглежда така:

  1. Приложи Действия за въвеждане всички играчи да състояние на играта начина, по който тези входни действия са получени от сървъра.
  2. Премахнете всичко от опашката за забавяне Действия за въвеждане, които според сървъра вече са приложени към състояние на играта.
  3. Изтрий Състояние на латентност и го нулирайте, така че да изглежда точно както състояние на играта.
  4. Прилагане на всички действия от опашката за забавяне към Състояние на латентност.
  5. Въз основа на данни състояние на играта и Състояние на латентност рендирайте играта на играча.

Всичко това се повтаря във всеки такт.

Твърде трудно? Не се отпускайте, това не е всичко. За да компенсираме ненадеждните интернет връзки, създадохме два механизма:

  • Пропуснати отметки: когато сървърът реши това Действия за въвеждане ще бъде изпълнен в такта на играта, тогава ако не е получил Действия за въвеждане някой играч (например поради увеличено забавяне), той няма да чака, а ще информира този клиент „Не взех предвид вашето Действия за въвеждане, ще се опитам да ги добавя в следващата лента. Това се прави, така че поради проблеми с връзката (или с компютъра) на един играч, актуализацията на картата да не се забави за всички останали. Заслужава да се отбележи, че Действия за въвеждане не се игнорират, а просто се отлагат.
  • Пълно двупосочно забавяне: Сървърът се опитва да отгатне какво е двупосочното забавяне между клиент и сървър за всеки клиент. На всеки 5 секунди той договаря ново забавяне с клиента, ако е необходимо (в зависимост от това как връзката се е държала в миналото) и съответно увеличава или намалява забавянето на двупосочното пътуване.

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

Сега трябва да ви обясня как работи изборът на обект. Един от преминалите видове Действие за въвеждане е промяна в състоянието на избор на обект. Той казва на всеки над кой обект играчът е задържал курсора на мишката. Както можете да видите, това е едно от най-честите действия за въвеждане, изпращани от клиенти, така че за да спестим честотна лента, ние го оптимизирахме така, че да заема възможно най-малко място. Това се реализира по следния начин: когато всеки обект е избран, вместо да съхранява абсолютни координати на картата с висока точност, играта съхранява относително отместване с ниска точност спрямо предишния избор. Това работи добре, защото изборът с мишката обикновено се случва много близо до предишния избор. Това поражда две важни изисквания: Действия за въвеждане никога не трябва да се пропуска и трябва да се прави в правилния ред. Тези изисквания са изпълнени за състояние на играта. Но тъй като задачата състояние на латентност в "изглежда достатъчно добре" за играча, те не са доволни в състоянието на забавяне. Състояние на латентност не взема предвид много гранични случаисвързани с прескачане на часовници и промяна на закъсненията на двупосочно предаване.

Вече можете да познаете накъде отива това. Най-накрая започваме да виждаме причините за проблема с мегапакетите. Коренът на проблема е, че логиката за избор на обект разчита на Състояние на латентности това състояние не винаги съдържа правилната информация. Така мегапакетът се генерира по следния начин:

  1. Плейърът има проблеми с връзката.
  2. Механизмите за пропускане на цикли и регулиране на забавянето на двупосочното предаване влизат в действие.
  3. Опашката със състояние на забавяне не отчита тези механизми. Това кара някои действия да бъдат премахнати преждевременно или да се изпълняват в грешен ред, което води до неправилен Състояние на латентност.
  4. Плейърът няма проблем с връзката и симулира до 400 цикъла, за да настигне сървъра.
  5. Във всеки цикъл се генерира ново действие и се подготвя за изпращане до сървъра, променяйки избора на обект.
  6. Клиентът изпраща мегапакет от 400+ промени в избора на обект към сървъра (и с други действия: състояние на задействане, състояние на ходене и т.н. също страдат от този проблем).
  7. Сървърът получава 400 входни действия. Тъй като не е разрешено да се пропусне нито едно действие за въвеждане, той инструктира всички клиенти да изпълнят тези действия и ги изпраща по мрежата.

Иронията е, че механизъм, предназначен да запазва честотната лента, доведе до огромни мрежови пакети.

Разрешихме този проблем, като коригирахме всички крайни случаи на актуализация и поддръжката на опашка за забавяне. Въпреки че отне доста време, струваше си да го оправим накрая, вместо да разчитаме на бързи хакове.

Източник: www.habr.com

Добавяне на нов коментар