Pull to refresh

Comments 38

В игре Teeworlds именно так все и сделано. Есть некое «ядро» игры, в котором ведутся все основные расчеты физики мира. Это ядро используется одновременно на сервере и на клиенте (для предсказаний). Вдобавок файлы с его кодом используются при расчете специального хеша. При подключении клиента к серверу эти хеши сверяются, и если они не совпадают, значит на клиенте и на сервере используются разные, скорее всего несовместимые версии игры.
Правда, последний момент не слишком удобен для игр без автообновления. В некоторых версиях разработчики сами костыль ставили, чтобы передавался старый хеш.
Лучше у Варгейминга статью на эту тему напишите.
Насколько помню серваков у них только для одних танков 10 штук.
Танки это скушно :)
Во-первых скорость по меркам FPS/MMORPG низкая, во-вторых количество игроков в сессии жестко лимитировано.
С технической точки зрения, было бы интересно узнать!
Только в росийском регионе одновременно онлайн у них там несколько сот тысяч игроков.
Это Вам не Рыбалка с 60-70 игроками онлайн)))

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

Ну есть ещё чисто «игромеханические» трюки, позволяющие сглаживать скачки. В первую очередь анимации действий, тех же скиллов, выстрелов, резких поворотов и т.д. Ну, вы же не можете в реальности бежать влево и внезапно, тут же, побежать вправо. У вас будет вполне себе задержка на торможение, разворот и разгон. Вот это время можно и нужно закладывать в анимации. У игроков с быстрым пингом анимации отработают как надо, у лагающих — будут дёргаться, но сами позиции объектов не будут слишком сильно скакать.
В качестве классического примера решения задачи предсказания действий игрока интересующимся можно посоветовать взглянуть на механику многопользовательской игры первого Quake. Вот обзор его исходного кода, смотреть часть «Прогнозирование». Насколько мне известно, это было одно из первых решений такого рода в игровой индустрии.
Перечитывал этот абзац много раз, что-то трудно воспринимается:
Это значит, что мы не можем применить необратимые последствия, например, убийство игроков. Такие необратимые последствия можно применять только когда они поступают из GameStateHistory, т.е. когда их больше нельзя перезаписывать.
This means we cannot apply irreversible outcomes, i.e, killing a players, such irreversible outcomes will only be applied when it goes out of the GameStateHistory, ie. when it cannot be rewriten anymore.
Кто в теме, объясните пожалуйста подробней, а то чувствую от меня ускользает полное понимание этой идеи.

Логично выглядит так, что если в серверном буфере GameStateHistory например в GameState N записано что определённого игрока уже замочили, а затем происходит согласование на сервере (появились данные в UnprocessedUserInput которые старше текущего кадра) и будут обновлены GameState N+1, N+2,… — то этот игрок уже никак не может «ожить», это необратимое последствие, перезаписывать в этой ситуации нельзя.

А если наоборот, например в GameState N записано что определённый игрок жив, но затем после согласования (появились данные в UnprocessedUserInput которые старше текущего кадра, скажем от клиента с медленным соединением) оказывается что в следующем GameState N+1 его замочили, то перезаписывается, нельзя же ему оставаться бессмертным :) Это будет выглядеть «как смерть из-за угла», у Valve доках это следствие применения Lag Compensation: For instance, if a highly lagged player shoots at a less lagged player and scores a hit, it can appear to the less lagged player that the lagged player has somehow «shot around a corner».

Правильно ли, что описанная тут методика согласования, является попыткой обобщить Lag Compensation, который специфичен именно для шутеров?
Отличная статья, давно было интересно как происходит работа игрового сервера.

Очень хочется видеть продолжение с более сложными алгоритмами, которые применяются в более быстрых играх (шутеры, ММОРПГ)
Это не я рекомендовал, а автор статьи. Спасибо за ссылку, попозже добавлю в перевод.
А все эти сложности точно нужны? Здесь есть одна компания, которая как раз продаёт тупые клиенты «передай клавиши по сети, получили видео обратно», и вроде бы у неё хватает клиентов. Значит это не принципиально?
Если лаги в вашей игре не портят игровой процесс, то можно не заморачиваться. Но в играх где нужно стрелять и быстро реагировать на действия других игроков, без этого будет просто ад.
Насколько я понял, это решение рекламируется для всех игр. Правда игроки к рекламе относятся скептически.

Я играл, мне не понравилось. Хотя я в Питере, а сервера в Москве, ощущение лага не проходило ни на секунду(пинг около 50).

Было дело, ради фпс резал графику. ММОшником был.

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


Как наиболее надежно сообщить серверу, в какой именно момент на клиенте произошли действия?

В IV части вы найдете все ответы( https://habrahabr.ru/post/302394/ ), но возможно стоит просмотреть так же предыдущие части чтобы убедиться что вы все правильно понимаете.

Текущий алгоритм работы мультиплеера
  • Сервер получает команды с клиентов и времена их отправления
  • Сервер обновляет состояние мира
  • ...

Да, читал всё части, но как раз-таки остаётся вопрос: как передать время отправления команд на клиенте таким образом, чтоб его невозможно было подделать?
То есть каким образом сервер может удостоверится, что команда на клиенте действительно была выполнена в заявленный момент.
Просто напрашивается батальный чит: получаем от сервера актуальное состояние S(t), а команды шлем как будто мы видели состояние S(t-1). И всё, мы всегда на один шаг впереди остальных.

Для надёжности можно читить вдвоём. Один читер живёт в будущем, другой эмулирует большую задержку и живёт в прошлом. Тогда читер из будущего может открывать данные для читера из прошлого.

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

Так никто не мешает получить эту информацию на другой клиент.

Если информация о выстреле дошла на какой-либо клиент, он уже произошел на сервере. Игрок уже считается мертвым, так что любой новый ввод от него игнорируется пока не произойдет респавн.

Вами описанный сценарий — это сервер без согласования ("наивная реализация" из данной статьи). То есть как только команда пришла от клиента, она сразу выполняется "окончательно".
И тогда игроки с меньшим пингом имеют преимущество над остальными, что не всегда приемлемо.
Согласование подразумевает, что сервер может "переиграть" историю если более медленный клиент выполнял команды в то время.
Например, это заметно в некоторых ММО: жизни персонажа падают до 0, но умирает персонаж несколько позже, видимо когда сервер дождался всех медленных клиентов.

Нет.
Согласование — это техника, применяемая на клиенте. Она никак не затрагивает состояние сервера. Она нужна чтобы создать у игрока ощущение того, что его команды применяются мгновенно. Для этого результат каждой команды пользователя отправляется на сервер и сразу же симулируется на клиенте. Когда приходит состояние от сервера, надо с ним синхронизироваться. Для этого клиент применяет у себя состояние сервера, а затем локально переприменяет все свои команды, которые сервер еще не успел обработать.
Как видите, серверу наплевать на то, применяется ли техника согласования.


А вот компенсация лага — другое дело (см. https://habrahabr.ru/post/303006/). Но в моей статье как раз и написано, что выстрел происходит так: сервер откатывает состояние мира назад, проверяет попал ли выстрел, возвращается обратно к текущему состоянию(не переприменяет команды, а просто возвращается), а затем применяет эффект от попадания или промаха, в зависимости от того, попал выстрел. Таким образом бессмертия не достичь, так как:
1) если игрок умер, такой методой его уже не воскресить.
2) применяется компенсация лага только для темпорально критичных действий(таких как выстрел).

Я собственно ссылался на текущую статью, где есть отдельная глава "Согласование на сервере". И на сколько я понимаю это несколько отличается от "компенсации лага".


Но это не меняет сути моего вопроса. Что при согласование на сервере, что при компенсации лага клиент должен как-то сообщить серверу "что он видел" в момент выполнения команды.
Так вот как это сделать наиболее надежно от подделок?

Я знаю что такое согласование. Нет, клиент не должен сообщать серверу что он видел, клиент только отправляет команды. Перечитайте еще раз, возможно посмотрите на мои статьи(https://habrahabr.ru/post/302834/)

> Если информация о выстреле дошла на какой-либо клиент, он уже произошел на сервере

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

Нет, не будут. Команды не сбрасываются из-за изменения мира(нигде такого не было написано).


Например, при выстреле произойдут следующие проверки:
1) Жив ли персонаж сейчас?
2.1) откатить время назад
2.2) может ли персонаж стрелять?
2.3) попал ли персонаж при выстреле во врага?
2.4) вернуть время назад
3) если все проверки прошли, наносим урон врагу


При передвижении произойдет следующее(предположим, что игрок контролирует персонажа только на земле)
1) На земле ли персонаж? (в настоящем времени)
2) Если на земле, изменить скорость в соответствии с пользовательским вводом


Итог: сервер никогда не меняет прошлое. Сервер иногда заглядывает в него, чтобы синхронизировать моменты, требующие особой точности.

1) Жив ли персонаж сейчас?

Ну вот, он оказался мёртв сейчас. Но ведь на момент своих действий он был жив. Его ввод был отказан

2) Игрок повернул влево. Но он уже по инерции вошёл в стену и поворачивать не может.

и т.д.

Хотелось бы математически строго описания переключения состояний онлайн игры. Как народ любит заморачиваться для распределённых систем.

1) Ну вот так вот обычно делают. Если хотите, можете проверку смерти сделать в прошлом, а значит применить выстрел. Все равно выстрел применится в настоящем. Да, враг умрет, но и вы тоже умрете.
А вот у игроков будет ощущение того, что что-то пошло не так, т.к. обоюдные убийства в игре с hitscan оружием поспринимаются как признак забагованности.


2) Вы не правы. Если клиентское согласование работает корректно, персонаж двигается по одинаковым траекториям что на сервере, что на клиенте. При этом персонаж мгновенно реагирует на ввод игрока.


Хотелось бы математически строго описания переключения состояний онлайн игры.
Если сделаете — будете первым, на сколько я знаю. Только учтите, что стоит формально описывать существующий механизм, а не тот, который вы придумали, ну либо сначала проверить, что ваша идея действительно лучше.
> Да, враг умрет, но и вы тоже умрете.

Ну да. В результате мы имеем невозможную ситуацию на всех трёх экземплярах игровой логики — на сервере, на первом клиенте, и на втором. Можно, конечно, понизить в правах лагающего, тогда страдать будет только он, но лучше так не делать. В доте, например, нередки ситуации смерти после успешного телепорта от опасности, иногда даже стан, срабатывая, не прерывает телепорт.

Тут важно понимать, что реальности разошлись, и что склеивать их надо согласно какому-то компромиссу. Например, убивая обоих игроков ради справедливости.

2) Это пока состояние клиента_1 не наблюдает клиент_2. А если, например, клиент_1 заморгал на радаре клиента_2, то обратно ему пути нет, если он хочет соблюсти непротиворечивость. Хотя, конечно, лучше оставить противоречие, как неизбежную цену за многопользовательский режим.

> Только учтите, что стоит формально описывать существующий механизм,

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

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


Dota в данном контексте лучше не вспоминать, там нет ни client side prediction, ни lag compensation. А смерть после телепорта видимо происходит если они прямо в одном кадре произошли.


Математика так не работает. Сначала делается понятная и удобная абстракция, а потом уже ищется как применить её к реальности.

Математика прекрасно так работает. Ценность математического аппарата, который не способен описать современные алгоритмы — нулевая. Так что придумывать десяток абстракций просто так — бесполезно. Должна быть непротиворечивая система, в которой можно доказывать теоремы в том числе для текущих игр.

> Dota в данном контексте лучше не вспоминать, там нет ни client side prediction, ни lag compensation.

Я удивлён. Прям в этой же статье дана ссылка на статью валве про client-side preditction.

На всякий случай продублирую:

https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
http://www.dota2.com/reborn/part3

> Математика прекрасно так работает.

Ни разу не встречал. Обычно математика описывает что-то не слишком рабочее. А потом уже программист натягивает это на реальность.

Кстати, я тут подумал ещё об одном виде игр — гоночках. Если в шутерах у тебя есть три подзадачи:
а) стратегическая — выбрать подходящий инструмент и огневой рубеж
б) мышкокликерство — попасть прицелом точно в противника. Может быть усложнена, если игра включает баллистику.
в) игра на реакцию, как в вестернах, где три ковбоя одновременно наставили друг на друга по пистолету.

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

Далеко не все конфликты временных линий неразрешимы. Смерть — как раз такой пример. А обычные виражи при движении считаются не такими важными и их допустимо менять в прошлом.

А вот как быть в гоночках? Там же каждый момент важен. Если вы стартуете одновременно с противником и видите себя в настоящем, противника — в прошлом, то он ещё стоит на месте. И у вас возникает искушение подрезать его. Он поступает зеркально. В результате — крушение.

У Valve конечно же есть технология клиентского согласования. Но в доте она почти не используется, только вызывается отработка анимации.
Например, передвижение персонажа не предсказывается(как минимум, не предсказывалось в 2015 году, после выхода reborn)
https://www.youtube.com/watch?v=fWaVcWNh3Ro&t=729s


Ни разу не встречал. Обычно математика описывает что-то не слишком рабочее.

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


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

Да, так и стараются делать.


А вот как быть в гоночках? Там же каждый момент важен.

Если в гонках машины не умеют сталкиваться, их часто экстраполируют на основании текущей позиции и скорости. А если машины умеют сталкиваться, увы, ничего не поделать и только с низким пингом получится нормально играть.

Как-то не очень конструктивна получается дискуссия. Попробую объяснить своё видение.


Возьмём абстрактную игру и события во времени:


  • t(0) — есть некий игрок (И), у которого 10 из 100 ед. жизней ("чуть живой");
  • t(1) — другой игрок лекарь (Л) жмет кнопку "вылечить И на 10 ед";
  • t(2) — третий игрок убийца (У) жмет кнопку "ударить И на 10 ед";
  • t(3) — у У быстрый интернет и его команда прилетает на сервер первой;
  • t(4) — у Л медленный интернет, его команда пришла второй;

Вопрос: что произойдёт с И?
По вашему получается, что И будет мертв, а Л получит отказ (пытался лечить мертвого).
В итоге игрок с быстрым интернетом получает преимущество.


Я же считаю, что И должен остаться при 10 ед. жизней (10 начальных + 10 вылеченных — 10 ударенных), так как лекарь отправил команду раньше.
Даже если в момент t(3) сервер разослал всем клиентам что у И 0 жизней, он должен иметь некий "grace period" и если позже пришла запоздалая команда, то переиграть историю.


От сервера, я соответственно ожидаю состояния:


  • t(0) — у И 10 ед. жизней;
  • t(1) — у И 10 ед. жизней;
  • t(2) — у И 10 ед. жизней;
  • t(3) — у И 0 ед. жизней, но он всё ещё жив (ждем немного, авось кто-то опаздывает с лечением);
  • t(4) — у И 10 ед. жизней;

А если лечение не прилетело, то где-нибудь в t(5) отправляем клиентам что И мертв окончательно.


Или я мыслю слишком утопично? По аналоги можно смоделировать ситуации с движением, выстрелами в дверной проём и прочим.


Так вот что, собственно, меня будоражит: как достоверно сообщить серверу, в какой именно момент произошла отправка команды с клиента?
Как серверу проверить, что Л действительно отправил команду в t(1), а не в t(3) и прикинулся медленным?

Да, игрок с более быстрым пингом получает преимущество.
В вашей схеме с ожиданием основные проблемы:
1) Стреляющий игрок гораздо хуже чувствует время, которое ему нужно потратить на убийство(это очень важная метрика, пруф: https://www.youtube.com/watch?v=EtLHLfNpu84). Что увеличивает чувство лага.
2) Игрок дольше чувствует себя живым, хотя на самом деле он мертв(и чаще он действительно умирает, а не оказывается чудесным образом спасен), что тоже увеличивает чувство лага.


Или я мыслю слишком утопично?
100% справедливости вы никогда не достигните, ведь игроки живут рассинхронизированных, немного отличающихся мирах.
Самое важное в мультиплеере — это иллюзия одновременности, а не сама одновременность. И поэтому выбор обычно делают в сторону уменьшения самых заметных проблем.

И да, сервер не может проверить что Л действительно отправил команду в t(1). Нет достоверного способа отличить пакет, который шел дольше обычного, от позже отправленного пакета. Так что стоит разрабатывать геймдизайн, который уменьшает профит от такого мухлежа.

Из текста не очевиден момент:

Как определить в какой момент пользователь сделал какое-либо действие, если у нас нет его времени (отправки сообщения), а есть только серверное, то есть время, когда его запрос пришел на сервер?
Sign up to leave a comment.

Articles