Path of Exile: клиент-серверная игровая синхронизация
![path of exile](https://i.imgur.com/FnYA36e.png)
В предыдущей версии “Манифеста” уже была статья про рассинхронизацию, мы переопубликовали ее с актуальными исправлениями и дополнениями.
Начиная с закрытого бета-теста, одной из самых горячих тем для обсуждения были так называемые «десинки». В лучшем случае, они вызывают легкие неудобства, в худших персонаж может погибнуть в ситуации, когда игрок считает, что монстров поблизости вообще нет. У нас на подходе много изменений, которые существенно изменят ситуацию. Мы бы хотели вам объяснить, как работают наши системы синхронизации, и почему синхронизация состояния игры — это проблема, с которой сталкиваются все без исключения онлайн-игры.
В этой статье я собираюсь объяснить:
- как различные типы онлайн-игр справляются с сетевыми задержками
- как работает наша система предсказания
- почему с ней возникают проблемы синхронизации и в чем они проявляются
- почему рассинхрона нельзя избежать, и чем же хорош rubber-banding (эффект, когда персонажа “откидывает назад во времени”)
- почему у некоторых других ARPG рассинхрон незаметен
- что мы планируем предпринять для улучшения ситуации
Как различные типы онлайн-игр справляются с сетевыми задержками
Чтобы определить результат того или иного действия, любая игра производит расчеты. В ARPG они применяются в ситуациях от боевых вычислений (кто, кому, и сколько нанес урона) до важных с точки зрения экономики транзакций предметов и ценностей между игроками. Чтобы предотвратить мошенничество, эти расчеты не должны происходить на компьютере игрока, поскольку это позволяет подделать результат любых действий.
По этим причинам, все важные (влияющие на прогресс персонажа) расчеты должны производиться на подконтрольных нам серверах. Сервера у нас есть в Техасе, Амстердаме, Сингапуре и Австралии, но из-за физических ограничений (вроде скорости света) мгновенный обмен данными с ними невозможен. Обычно мы наблюдаем у наших клиентов время отклика в диапазоне от 50 до 250 мс.
Все онлайн-игры находятся в этой ситуации. Сервер должен указывать, какие действия произошли, а какие нет, но существует задержка в ~250 мс, пока данные попадут на сервер и обратно. Обычно в играх используют для решения этой проблемы 3 способа:
- Доверять клиенту. Это означает, что игроки могут смошенничать, но отклик игры на действия игрока будет мгновенный.
- Ждать подтверждения каждого действия от сервера. Этот способ часто используют жанры MOBA и RTS. Когда ты отдаешь команду юниту, она начинает выполняться только после подтверждения от сервера — то есть с 50-250 мс задержкой. Если ты физически недалеко от сервера — то быстро приспосабливаешься к постоянному лагу, переставая его замечать. Если же сервер далеко — возникает ощущение “игры пьяным”, когда каждое твое действие начинает выполняться через четверть секунды. Данный способ для ARPG не работает — темп событий и боя требует мгновенного отклика.
- Игра предсказывает результат действий, как будто сервер уже прислал их подтверждение. А когда приходит ответ от сервера — принять их во внимание, подправив расчеты. Именно так поступают ARPG, включая и PoE. Когда ты отдаешь команду двигаться или атаковать, это происходит мгновенно, и игра ощущается отзывчивой. Проблема возникает, когда сервер решает, что действие не могло произойти — это значит, что состояние игры на клиенте и сервере ушло от состояния синхронизации.
ARPG вынуждены использовать третий способ (с предсказанием действий), чтобы давать ощущение отзывчивости. Проблема начинается с того, что как только ты начинаешь двигаться — ты по определению рассинхронизируешься с сервером. Клиент уж отрисовал первые кадры анимации, а сервер не знает о том, что ты куда-то нажал мышкой, пока не дойдут данные. Предсказание действий для такого типа игр является обязательным, и клиент практически всегда находится в состоянии легкой рассинхронизации. Обычно это не проблема, но когда слишком много предсказаний происходит на основе неверных данных, начинает происходить страшное. Трудность заключается в том, что эту ситуацию надо обнаруживать и исправлять до того, как она произойдет.
Как работает наша система предсказания
Предположим, вы играете с задержкой 200 мс и нажали атаковать монстра, до которого идти 2 секунды. Также предположим, что в анимации атаки до «точки контакта» (это сам момент нанесения повреждений) проходит 300 мс.
Хронология событий:
- 0мс: ты нажал на монстра. персонаж начал движение в игре
- 100мс: факт нажатия получен сервером. персонаж начал движение на сервере. В этот момент персонаж на клиенте уже преодолел 5% пути.
- 2000мс: Персонаж достиг монстра в игре, но не на сервере. Ты даже не можешь быть уверен, что он дошел — его могла остановить атака и оглушение. Игра начинает отображение анимации удара.
- 2100мс: персонаж достиг монстра на сервере. Сервер немедленно просчитывает начало боя, у него еще есть время до «точки контакта», чтобы успеть переслать предварительный результат клиенту.
- 2200мс: игра получает от сервера уведомление, сколько примерно урона будет нанесено, и какого он типа. Данные пришли до точки контакта! Но так происходит не всегда.
- 2300мс: в игре происходит «момент контакта». Поскольку у клиента уже есть вся информация о повреждениях, игра может смело отрисовывать приятные глазу разлетающиеся внутренности и элементальные эффекты вроде огня/льда/молнии. На сервере удар еще не произошел.
- 2400мс: «Точка контакта» достигнута на сервере. Повреждения фиксируются и применяются к монстру. Он умирает. Опыт и выпавшие предметы рассчитываются и отсылаются клиенту.
- 2500мс: клиент получает информацию про полученный опыт и выпавший лут (в это время на нем скорее всего происходит анимация смерти монстра — прим. переводчика)
Несмотря на сетевую задержку в 100мс, информации о результатах атаки получена клиентом до «момента контакта». Единственный признак того, что есть задержки — это промежуток в 1/10 секунды между смертью монстра и выпадением вещей. И что самое главное — подобная система не позволяет смошенничать, подделав исход боя.
Почему возникают проблемы синхронизации, и в чем они проявляются
Пример выше предполагает, что все прошло гладко. Но постоянно случаются ситуации, когда 2 секунды бега на клиенте и сервере приводят к совершенно разным результатам, или из-за внезапного сетевого лага напрочь сбивается тайминг. Если атака прерывается на сервере до того, как она началась (еще в процессе перемещения), а на клиенте нет — ты видишь долгую анимацию без возможности ее прервать, поскольку время обмена данными составляет значительную долю от времени анимации.
Даже без учета внезапных проблем с сетью, клиент игры просчитывает маршрут движения каждого монстра на основании своей информации о местоположении персонажа — которое по определению отличается от такового на сервере из-за задержек. При этих расчетах учитывается положение остальных монстров — которые тоже находятся «слегка не на своем месте» относительно сервера. Различие в рассчитанных маршрутах вносит свою лепту в дальнейшую рассинхронизацию.
Следует подчеркнуть, что в 99% боях все выглядит вполне хорошо. Хотя симуляция игры на клиенте и сервере в рассинхроне из-за задержек передачи данных, расчет времени в общем работает неплохо — монстры, которые какими-то хитрыми путями двигаются к персонажу, достигают его примерно в нужное время и примерно там, где ожидается. Тяжело понять, что что-то пошло не так — пока все не становится очень плохо.
К сожалению, при тяжелом рассинхроне игрок испытывает большие проблемы: “из ниоткуда” по персонажу проходит урон, или персонаж раз за разом “телепортируется” в толпу монстров, от которой пытается убежать. У нас есть код для определения подобных ситуаций — он принудительно синхронизирует все объекты на клиенте с сервером (rubber-band), но часто это не слишком хорошо выглядит.
Почему рассинхрона нельзя избежать, и чем же хорош rubber-banding?
Необходимо понимать, что все ARPG вынуждены использовать такую же (или похожую) систему предсказаний. Без нее игре придется ждать подтверждения каждого действия игрока, что приведет к очень медленному отклику на все действия.
Даже если код ресинхронизации идеален, неизбежны ситуации, когда малейшие различия во времени между событиями на клиенте и сервере будут вызывать рассинхрон. Представьте ситуацию — вы находитесь рядом с камнем, пытаетесь обежать его и отправляете персонажа в указанную вами точку. И клиент, и сервер просчитывают кратчайший путь в эту точку. Из-за того, что клиент по-определению слегка опережает сервер (поскольку для отзывчивости перемещения обрабатываются на 50-250мс раньше), случаются моменты, когда клиент и сервер выбирают разный маршрут движения в обход камня. Если по пути тебя ударяет монстр — твое движение будет прервано в различных точках в обоих симуляциях. Вот и рассинхрон! Код, отвечающий за синхронизацию, определит этот момент и переместит вас на другую сторону камня — туда, где вы должны находиться на сервере.
Самый важный вывод из этого — улучшение нашего кода для обнаружения рассинхронов приведет к более частой принудительной синхронизации, чем сейчас. Если мы будем работать в этом направлении, положение монстров и игроков будет корректироваться чаще, с целью снизить “разбег” рассинхронизации. Игроки часто называют “десинком” сам этот эффект синхронизации, хотя на самом деле это исправление проблемы в тот момент, когда она обнаружена. Будет довольно сложно доказать игрокам, что более частая ресинхронизация это не только хорошо, но и вообще лучшее решение.
Почему у “некоторых других” ARPG рассинхрон незаметен
Игры, ждущие подтверждения действий от сервера (MOBA и RTS), отличаются гораздо большими задержками ввода, но не имеют таких проблем с синхронизацией, как у нас. Зато у них есть свой набор проблем, связанных с синхронизацией игровых состояний, с которым мы (слава богу!) дела не имеем.
Игры, использующие систему предсказаний, подобную нашей, испытывают совершенно аналогичные проблемы, если они не хитрят с различными аспектами игровой симуляции. Например, для ARPG характерны следующие «хитрости», применяющиеся в различных сочетаниях:
- объекты могут наносить урон с очень далекой дистанции
- отсутствует понятие “шанс на попадание”, если удар начал наноситься — он обязательно попадет
- различные трюки со скоростью/коллизиями, делающими возможным мошенничество со скоростью бега на модифицированном клиенте и/или прохождение сквозь монстров
- анимация атаки не может быть прервана (то, как у нас работает «оглушение»)
- (Сознательное избегание узких проемов при дизайне подземелий. Этого не было в манифесте, но об этом упоминал один из разработчиков PoE — прим. переводчика)
К сожалению, мы не собираемся использовать все эти хитрости. Каждая из них по отдельности уничтожает какой-то важный аспект хардкорности нашей игры: позволяет клиенту мошенничать с атаками или перемещением, полностью убирает механику меткости и уворота, убирает станлок, убирает возможность заблокировать персонажа монстрами и т.д.
Мы хотим, чтобы в игре были все эти хардкорные механики (те, при которых положение персонажа важно, а нельзя мошенничать в PvP) — и нам остается только работать над улучшением кода симуляции и синхронизации.
Что мы планируем предпринять для улучшения ситуации
У нас в планах множество изменений, каждое из которых призвано улучшить синхронизацию симуляции на клиенте и сервере (нередко — с побочными эффектами):
- Заставить монстров на клиенте атаковать персонажа по его серверному положению, чтобы уменьшить энтропию. Возможен компромиссный вариант — атаковать среднюю точку между положениями игрока на клиенте и сервере. Возможная проблема в том, что визуально монстры могут впустую бить оружием воздух, хотя технически они больше синхронизированы.
- Рисовать кровь-кишки и элементальные эффекты в момент «точки контакта» на клиенте, вместо того чтобы использовать их в качестве подтверждения урона. Визуально бой будет более реалистичен, но мы потеряем канал обратной связи о том, было ли на самом деле нанесено повреждение. Проще всего это применить к эффектам от заклинаний, поскольку они не имеют механики промахов/попаданий.
- Ресинхронизировать объекты, которые успешно ударили персонажа, когда на клиенте рядом с ним никого нет. Это может потенциально увеличить рассинхронизацию, если положение персонажа на клиенте неверное.
- Ресинхронизировать всё вокруг объекта, если игра определила, что он вышел из синхронизации. Очень сильно снизит общую энтропию, но скорее всего сделает игровой процесс менее гладким.
- Добавить задержку действий, когда персонаж на клиенте опережает себя на сервере при движении. Это исправит ситуацию, когда монстры умирают до того, как персонаж к ним подходит (в случае рассинхрона). Но боевая эффективность игроков в подобных случаях снизится.
- Улучшить «дистанционную» ресинхронизацию, когда объекты находятся далеко от своего предполагаемого местонахождения. На данный момент она не совсем корректно учитывает скорость перемещения. Рассинхрон при ударе Роа с разбегу — из этой категории.
- Измерять общий уровень энтропии вокруг игрока, и ресинхронизировать при превышении им определенного порога.
- Исправить ошибки в работе некоторых умений, из-за которых их симуляция отличается на клиенте и сервере. Наример, Whirling blades иногда не срабатывает.
- Есть и другие изменения, но они либо малозначительны, либо их слишком сложно объяснить в рамках данной статьи.
Судя по всему, максимальный эффект, который мы можем получить от улучшения кода — более быструю и своевременную ресинхроцизацию. Это повысит частоту визуальных эффектов синхронизации, но значительно снизит число смертей от того, что игрок заблуждался относительно настоящего местоположения объектов на экране.
Я указал возможные побочные эффекты того или иного решения с целью показать, что проблема, с которой мы боремся, очень сложна по своей природе. С одной стороны мы ограничены законами физики (скоростью света), с другой — необходимостью сохранить (и защитить от мошенничества) все игровые механики. Но я абсолютно уверен, что мы последовательно будем внедрять изменения, которые заметно улучшат ситуацию.
Обновление от 20 апреля 2014г
Когда я публиковал статью, я не видел возможности как-то ее улучшить. Она подробно и точно объясняет ситуацию, и я хотел бы оставить ее “как есть”, не считая возможных незначительных изменений. Тем не менее, хотелось бы прояснить некоторые моменты отдельно.
- Рассинхрон никак не связан с нагрузкой на сервера. Покупка новых серверов не решит проблему. На данный момент их количество нас устраивает. Когда сервера перегружены — это проявляется в невозможности создавать новые инстансы, а не в проблемах с системой предсказаний.
- Некоторые игроки называют “десинками” проблемы совсем иного рода, не связанные с синхронизацией действий. К примеру, если у интернет-провайдера возникли проблемы, или игре понадобилось срочно подгрузить какой-то графический элемент, что привело к просадке FPS. Все эти проблемы требуют совсем иных способов решения. Расстраивает раз за разом читать фразы вроде “сегодня особенно много десинков на серверах”, когда на самом деле надо бы сказать “Сегодня интернет лагает особенно сильно”.
- В гонках рассинхрон действительно ощущаются сильнее — поскольку ради скорости игроки буквально протискивают своих персонажей через плотные толпы монстров и сквозь узкие проходы. Гонки проходят на тех же серверах, которые обслуживают обычные лиги, и в тех же игровых условиях (не считая специфики разных типов гонок).
- Нам периодически предлагают переделать игру под синхронное клиент-серверное взаимодействие, по принципу RTS/MOBA-игр. У них на самом деле нет рассинхрона — но зато есть задержка после каждого нажатия. Если ты просто направляешь куда-то очередного юнита — лаг воспринимается естественно, но если этот юнит — твое собственное альтер-эго, игра ощущается ужасно тормозной. На текущий момент, наша система предсказаний позволяет игрокам не самых развитых стран нормально играть с 200+ мс задержкой. При альтернативной сетевой модели игра была бы для них куда неприятней. Кроме того, для этой модели надо переписать большую часть основного кода.
- Существуют однозначные проблемы с некоторыми умениями (Whirling Blades, Cyclone, крюк Брутуса и т.д.) — из-за чего эти умения гораздо чаще вызывают рассинхрон. Мы вплотную занимаемся этим вопросом, и в обозримом будущем планируем значительно улучшить ситуацию в этом плане.
- Помимо узко-специфичных (касательно конкретных умений) изменений, мы уже тестируем кое-что более глобальное в экспериментальном режиме, пытаясь добиться улучшения путем более “интеллектуальной” ресинхронизации. Речь идет об очень сложном компромиссе между частой ресинхронизацией “по мелочам” (игра становится более “рваной”, но без “десинков”) и менее частыми, но более существенными “телепортами”. Пользователи обычно ассоциируют каждую ресинхронизацию с десинком — увеличение их частоты вызовет ощущение, что вместо улучшения ситуации мы ее, наоборот, ухудшили.
Если обрисовать проблему в двух словах: из-за необходимости сохранить хардкорные механики (блокирование прохода телами, оглушение, шанс промахнуться), и одновременно предотвратить возможность мошенничества — небольшой рассинхрон будет всегда. Этого не избежать из-за физических ограничений в виде скорости света. Идеальное решение для нас — мгновенно обнаруживать расхождение и сразу исправлять его. Мы пока не довели этот процесс до уровня, когда результат бы нас устроил. Когда это случится — вы будете периодически замечать ресинхронизацию, что поначалу будет вызывать ощущение постоянного десинка. На самом же деле, система обнаружила проблему и исправила ее — вместо того, чтобы позволить вам думать, что все идет отлично…пока вы не обнаружите персонажа за две комнаты в стороне, прижатого монстрами к стенке.
Я буду обновлять статью, когда мы будем достигать определенных успехов в борьбе с рассинхронизацией. Спасибо за ваше внимание и затраченное время. Теперь вы знаете даже больше, чем бы вам хотелось, о проблемах синхронизации игровых состояний.
— Крис Уилсон.
Список других моих переводов из серии «Манифест разработки»:
-
breaks
-
novax
-
ShvedRUS