Пятничные факты #302 – Многопользовательский мегапакет

Поделиться

опубликовали  Twinsen, Klonan

Многопользовательский мегапакет Twinsen

В прошлом месяце я присоединился к MMO-событию которое организовала KatherineOfSky в качестве игрока. Я заметил, что после того, как мы достигли определенного количества игроков, каждые несколько минут их выкидывало. К счастью для вас (но к несчастью для меня), я был одним из игроков, которые отключались каждый раз, хотя у меня была приличная связь. Таким образом, я взялся за вопрос лично и начал изучать проблему. После 3 недель отладки, тестирования и исправления проблема, наконец, была устранена, но путешествие туда оказалось не таким легким.

Многопользовательские проблемы очень трудно отследить. Обычно они происходят только в очень специфических сетевых условиях, в очень специфических игровых условиях (в этом случае более 200 игроков). Даже если вы можете воспроизвести проблему, ее невозможно правильно отладить, так как установка точки остановки останавливает игру, портит таймеры и обычно прерывает соединение. Но благодаря некоторой настойчивости и благодаря удивительному инструменту, названному “неуклюжим“, мне удалось выяснить, что происходит.

Краткая версия такова: из-за ошибки и неполной реализации имитации состояния задержки клиент иногда оказывался в ситуации, когда он отправлял бы сетевой пакет из примерно 400 входных действий выбора объекта за один тик (то, что мы назвали ” мегапакет ‘). Тогда сервер должен не только правильно получать эти входные действия, но и отправлять их всем остальным. Это быстро становится проблемой, когда у вас есть 200 клиентов. Это быстро увеличивает серверную загрузку, вызывает потерю пакетов и вызывает каскад повторно запрошенных пакетов. Отсроченные действия при вводе приводят к тому, что все больше клиентов отправляют мегапакеты, еще больше каскадируя. Счастливчики успевают выздороветь, остальные в итоге отбрасываются.

Проблема была довольно фундаментальной и заняла 2 недели. Это довольно технически, поэтому я объясню в сочных технических деталях ниже. Но вам нужно знать, что начиная с версии 0.17.54, выпущенной вчера, многопользовательская игра будет более стабильной, а скрытие задержек будет намного менее затруднительным (меньше лагов и телепортирования) при возникновении временных проблем с подключением. Я также изменил то, как скрытие задержки обрабатывается в бою, надеюсь будет выглядеть более плавным.

Многопользовательский мегапакет – Tехническая часть Twinsen

Основной способ работы нашего многопользовательского режима заключается в том, что все клиенты имитируют состояние игры, и они только получают и отправляют входные данные игрока (так называемые действия ввода). Основной обязанностью сервера является передача входных действий и обеспечение того, чтобы все клиенты выполняли одинаковые действия в одном тике. Подробнее в FFF-149

Поскольку серверу необходимо осуществлять арбитраж при выполнении действий, действие игрока перемещается примерно так: действие игрока -> игровой клиент -> сеть -> сервер -> сеть -> игровой клиент. Это означает, что каждое действие игрока выполняется только тогда, когда оно совершает круговое путешествие по сети. Это сделало бы игру действительно запаздывающей, поэтому скрытие задержки стало механизмом, добавленным в игру почти с момента появления многопользовательской игры. Скрытие задержки работает путем имитации ввода игрока, без учета действий других игроков и без учета арбитража сервера.


В Factorio у нас есть Game State, это полное состояние карты, игрок, права, все. Оно моделируется определенно на всех клиентах на основе действий, полученных от сервера. Если оно когда-либо отличается от сервера или любого другого клиента, происходит рассинхронизация.

Кроме Game State есть Latency State. Это содержит небольшое подмножество основного состояния. Состояние задержки не является сакральным, оно просто отражает то, как мы думаем, как будет выглядеть состояние игры в будущем на основе входных действий, выполненных игроком.

Для этого мы сохраняем копию входных действий, которые мы выполняем, в очереди с задержкой.

В итоге процесс на стороне клиента выглядит примерно так:

  1. Применить все входные действия всех игроков к игровому состоянию, полученные с сервера.
  2. Удалить все входные действия из очереди задержки, которые были применены к игровому состоянию, в соответствии с сервером.
  3. Удалить состояние задержки и сбросить его, чтобы оно выглядело так же, как и состояние игры.
  4. Применить все действия в очереди задержки к состоянию задержки.
  5. Визуализировать игру для игрока, основываясь на информации из состояния игры и состояния задержки.

Это повторяется каждый тик.

Сложно? Чтобы учесть ненадежный характер интернет-соединений, у нас есть два механизма:

  • Пропущенные тики: когда сервер решает, какие входные действия будут выполняться в каком тике игры, если у него нет входных действий определенного игрока (например, из-за скачка задержки), он не будет ждать, а вместо этого скажет этому клиенту ” Я не включил ваши входные действия, постараюсь включить их в следующий тик “. Это так, когда у клиента возникают проблемы с подключением (или с компьютером), они не будут замедлять обновление карты для всех. Обратите внимание, что входные действия никогда не игнорируются, они только задерживаются.
  • Задержка туда и обратно: сервер пытается угадать задержку туда и обратно между клиентом и сервером для каждого клиента. Каждые 5 секунд он будет согласовывать новую задержку с клиентом, если это необходимо, в зависимости от того, как соединение в прошлом вело себя, и задержка при передаче туда и обратно будет соответственно увеличиваться и уменьшаться.

Сами по себе они довольно просты, но когда они происходят вместе (что часто встречается при возникновении проблем с подключением), логика кода начинает становиться громоздкой, с большим количеством крайних случаев. Кроме того, сервер и очередь задержек должны правильно вводить специальное действие ввода, называемое StopMovementInTheNextTick, когда в действие вступают вышеуказанные механизмы. Это предотвращает бег вашего персонажа (например, перед поездом), когда возникают проблемы с подключением.

Теперь пришло время объяснить, как работает наш выбор сущностей. Один из типов входных действий, которые мы отправляем, – это изменение выбора сущности, которое сообщает всем, над какой сущностью наведен курсор  каждого игрока. Как вы можете себе представить, это наиболее распространенное входное действие, отправляемое клиентами, поэтому оно было оптимизировано таким образом, чтобы использовать как можно меньше места для экономии пропускной способности. То, как это было сделано, заключается в том, что каждый выбор объекта вместо сохранения абсолютных координат карты высокой точности сохраняет относительное смещение низкой точности к предыдущему выбору. Это хорошо работает, так как выбор обычно очень близок к предыдущему. Это создает 2 важных требования: действия ввода никогда не могут быть пропущены и должны выполняться в правильном порядке. Эти требования выполнены для Game State. Но, поскольку целью состояния задержки является «хорошо выглядеть» для игрока, эти требования не были выполнены. Состояние задержки не учитывало множество крайних случаев, связанных с пропуском тиков и изменением задержки в обоих направлениях.

Так что вы, вероятно, можете увидеть, куда это идет. Наконец слабость мегапакета начала проявляться. Последняя проблема заключалась в том, что логика выбора объекта опиралась на состояние задержки, чтобы решить, следует ли ему отправлять действие с измененным выбором, но иногда состояние задержки не содержало правильной информации. Итак, мегапакет сгенерирован примерно так:

  1. У игрока проблемы с подключением.
  2. Пропущенные тики и механизмы регулировки Roundtrip Latency начинают действовать.
  3. Очередь состояния задержки не учитывает эти механизмы. Это приводит к тому, что некоторые действия преждевременно удаляются или выполняются в неправильном порядке, что приводит к неверному состоянию задержки.
  4. Игрок восстанавливается после проблемы с подключением и имитирует до 400 тиков, чтобы снова подключиться к серверу.
  5. Для каждого тика создается новое действие по изменению выбора объекта, которое готовится к отправке на сервер.
  6. Клиент отправляет серверу мегапакет с 400+ изменениями выбора сущностей (и другими действиями. Состояние съемки, ходьба и т. Д. Также пострадали от этой проблемы).
  7. Сервер получает 400 входных действий. Так как не разрешено пропускать любое входное действие, он говорит всем клиентам выполнить эти действия и отправляет их по сети.

По иронии судьбы, механизм, который должен был сохранить некоторую пропускную способность сети, в конечном итоге создавал массивные сетевые пакеты.

С этой целью это было решено путем исправления всех крайних случаев обновления и обслуживания очереди задержки. Хотя это заняло довольно много времени, в конце концов, вероятно, стоило сделать правильное исправление вместо некоторых быстрых фиксов

Clusterio – The Gridlock Cluster Klonan

Clusterio – это система сценариев / серверов, которая добавляет обмен игровыми данными между различными серверами. Например, отправка предметов между различными серверами, так что вы можете иметь «сервер добычи», который будет добывать всю железную руду, которая вам нужна, и отправлять ее на «сервер переплавки», который будет плавить все это и передавать дальше.

В прошлом году было организовано мероприятие с использованием системы, которое объединило более 30 серверов, чтобы достичь общей цели 60 000 науки в минуту.. У MangledPork есть видео начала прошлогоднего события on YouTube.

Великие умы и сообщества, стоящие за последним событием, снова собрались вместе, чтобы провести еще одно событие Clusterio: The Gridlock Cluster. На этот раз цель состоит в том, чтобы еще больше расширить границы, исследовать и колонизировать новые узлы по мере их создания, а также создать мега-фабрику на нескольких серверах.

Если вы заинтересованы в участии в этом мероприятии сообщества, все детали перечислены в Reddit post, и вы можете присоединиться к Gridlock Cluster Discord server.

Как всегда, дайте нам знать, что вы думаете на нашем форуме.


Поделиться

Комментарии: