Cheb
> Я, лично, хочу сделать lockstep где все клиенты выполняют мир синхронно, с
> хитрой лагокомпенсацией, передаются только инпуты, а ИИ выполняется только на
> сервере.
Ой, что будет...
Малейший глюк или потеря пакетов, и вместо пяти шагов враг сделал три.
Если передавать не только ввод, но и координаты, то обязательно будут хакеры с телепортацией.
Можно пытаться отдавать клиенту чужой ввод, но всё равно нужно проверять результаты на сервере и отдавать потом реальные координаты/состояние, причём не только другим клиентам, но и инициатору, иначе будет ходьба сквозь стены и тот же рассинхрон. Опять же, если ввод передаётся всем, то это вообще неоптимально. Лишний трафик с сомнительной выгодой в скорости, и с огромным потенциалом к читерству.
Первый закон "враждебного окружения" - нельзя верить тому, что происходит на клиенте, ибо клиент может быть взломан или неисправен. Любые действия клиета сервер должен прогонять через фильтры текущих состояний, отрабатывать, и возвращать корректное состояние обратно. Ну и раздавать всем, для кого это актуально. А то будут сплошные ниндзя, прыгающие до облаков, проходящие сквозь стены, бегающие как флэш и ускользающие из-под паралича силой мысли.
Опять же, если передавать всем весь инпут, то можно запросто видеть, как твой враг крадётся к тебе тихой сапой в надежде воткнуть нож в твоё левое полупопие.
Сервер, и только сервер должен решать, видит ли клиент изменения состояния/положения объектов, или нет. И ни в коем случае не отдавать клиенту того, чего он знать не должен.
СерыйМыш
> Малейший глюк или потеря пакетов, и вместо пяти шагов враг сделал три.
> Если передавать не только ввод, но и координаты, то обязательно будут хакеры с
> телепортацией.
Он наверное имел ввиду физику на сервере считать. А клиентам отправлять уже посчитанные координаты.
Programmer777
> Он наверное имел ввиду физику на сервере считать. А клиентам отправлять уже
> посчитанные координаты.
Вроде бы ясно написано: "lockstep", "где все клиенты выполняют мир синхронно".
Цитаты с хабра:
Lockstep — очень эффективная модель сетевого взаимодействия, но сложна в реализации из-за проблем рассинхронизации или различий компиляторов/разных платформ. Lockstep использовался в прошлом потому, что пропускная способность сети была очень низкой.
Это очень легко в теории, до того, как задержки или отличия архитектур испортят все. На практике, это сложно сделать. Программист должен убедится, что все команды исполняются в одинаковом порядке на каждом клиенте. Что если кто-то не получил команду вовремя? Клиент должен ждать подтверждения получения команды от всех клиентов. Что случится, если кто-то переподключился? Первое, что приходит на ум — симулировать всю игру с самого начала, что может занять довольно много времени.
Если этого вам недостаточно, Lockstep использует p2p, наследуя все проблемы второго. Если вы используете TCP, вы не сможете соединить два любых компьютера через интернет из-за брандмауэров, NAT'ов, прокси, VPN… и т.д.
Клиент-Серверная модель более устойчивая к читерам. Используя Lockstep, каждый клиент знает все обо всей игре. Например туман войны — клиентская часть, клиент в реальности знает, что за туманом, но не показывает его. В Клиент-Серверной модели сервер просто не отсылает обновления за туманом войны.
Примеры:
StarCraft 1 использовал Lockstep, в то время как StarCraft 2 использует Клиент-Серверную модель.
Supreme Commander 1 использует Lockstep когда более новые игры, например Planetary Annihilation, использует Клиент-Серверную модель.
DotA запускалась на движке Warcraft 3 который использовал Lockstep, но Valve используют Клиент-Серверную модель для Dota 2.
DOOM использовал Lockstep но Quake использует Клиент-Серверную модель, Lockstep вообще не пользовался популярностью в FPS.
MMO никогда не использовали Lockstep потому, что он неприменим для такого большого количества игроков.
Короче, lockstep можно делать только в качестве разминки для мозгов. И только в определённом виде игр - RTS и им подобных. Такая архитектура не позволяет тащить больше десятка клиентов, слишком много перекрёстных связей, и слишком сложно устранять последствия лагов и рассинхрона.
>Он наверное имел ввиду физику на сервере считать. А клиентам отправлять уже посчитанные координаты.
Не-а. Многослойное многопоточное выполнение одного и того же на сервере и на клиентах.
До того, как меня унесло в дебри всяких скучных вещей, вроде поддержки шрифтов по нормальному, или создания костылей для RTL языка, где поддержка юникодных путей до сих пор сломана (радости миграции), я как раз докапывался до структуры классов, позволяющих всю эту свистопляску задёшево.
Вкратце: массивы в паскале - уже менеджед, со счётчиком ссылок. Чтобы сделать свой уникальным - достаточно SetLength(a, Length(a)). Причём, это провоцирует рекурсивный обход массива, если он содержит другие менеджет сущности. Если наследовать классы от IUnknown, определив свои хитровывернутые _AddRef и _Release, плюс методы рекурсивного клонирования (почти готовы тоже) - то я делаю и классы менеджед, и это позволяет иметь структуру, где не изменившиеся элементы дублируются, а изменившиеся - расслаиваются по потокам.
Зачем это нужно? Lockstep отстаёт на дельту (200 до 500мс, подобрать опытным путём), нужную, чтобы дать инпутам дойти с приемлемой гарантированностью. А впереди этого базового слоя на клиентах бегут опережающие частичные клоны, основанные на неполной информации - т.е. на тех инпутах, которые успели дойти до данного клиента на тот момент. Они гонятся вперёд несколько тиков за тик, и всё время замещают собой самый верхний слой, который и видит у себя локальный игрок и в котором любая реакция на его инпуты мгновенная и объективная.
Какой у этого результат? Игрок, на которого не вляют, прямо или опосредованно, другие игроки, не заметит абсолютно никаких различий в своих действиях - кроме случаев потери инпута, не успевшего дойти вовремя. Что должно быть очень маловероятно, т.к. каждый пакет от клиента к серверу включает инпуты этого тика и четыре-пять прошлых. Но если уж начали не успевать - это вызовет микро-реконнект.
Регулировка нагрузки может быть за счёт подбора радиуса зоны, для которой создаются все эти опережающие лагокомпенсирующие слои. Т.е. вдали всё будет лагать на 200..500 мс. Предпочтительно, чтобы этот радиус был больше или равен радиусу видимости.
Минусы:
- геймплей, основанный на тумане войны, невозможен в принципе. Ну и шут с ним. Планирую сделать "волхак" одним из повер-апов, наравне с озверином и прочими.
- вход в игру - это долгая закачка всего 100-мегабайтного снапшота, а потом доолгое ожидание пока физика промотает вперёд все события, произошедшие с того момента, пока снапшот качался.
Плюсы:
- возможен былинный масштаб разрушений, разносящих пол-карты одной камехамехой: ведь объём трафика абсолютно не зависит от того, насколько массированные изменения происходят в мире
- возможность потопов: честно текущие жидкости на весь мир, как в Террарии. Разломай вулкан в командном дезматче чтобы спавн команды противника залило лавой.
- возможность кооп-оходы на гигантские орды монстров, или дезматча с фишками типа "натрави на оппонента стадо бизонов в 500 голов".
- возможна полная запись *всех* матчей для разбора модератором (какая сволочь построила эту свастику).
>Короче, lockstep можно делать только в качестве разминки для мозгов.
Ну, тут либо вы все ещё услышите моё "Она живая! Живая!", либо я умру от старости, так и не доделав.
>Первый закон "враждебного окружения" - нельзя верить тому, что происходит на клиенте, ибо клиент может быть взломан или неисправен.
Изящно обходится тем, что клиент, выполняющий хоть на бит не так, как сервер, вылетит с Out of Sync. Ну, или будет играть в некую абстракцию, не имеющую ничего общего с реальностью.
Инпуты, приходящие от клиента, будут пронумерованы и содержать N прошлых инпутов, так что ситуация "поправить задним числом" будет невозможна. Плюс, фильтрация движений мышью, сломать которую - значит нарваться на Out of Sync. Но тут важна осторожность: я как-то смотрел свои действия в AoS записи и с удивлением обнаружил, что прицеливание из гранатомёта по вертикали (граната летит по дуге) занимает у меня меньше одного кадра (на 60 fps). Вот целюсь по горизонтали - потом !хлоп! следующий кадр прицел уже задран и граната уже летит. В последующих кадрах прицел опять неподвижен, и лишь невезучий фраг взрывается красными кубиками.
Cheb
> До того, как меня унесло в дебри всяких скучных вещей, вроде поддержки шрифтов
> по нормальному, или создания костылей для RTL языка, где поддержка юникодных
> путей до сих пор сломана (радости миграции), я как раз докапывался до структуры
> классов, позволяющих всю эту свистопляску задёшево.
Месье знает толк в извращениях )
Я тоже люблю дельфи/фрипаскаль, и считаю, что он гораздо удобнее, чем всеми любимый птичий Си и его вариации.
И я даже лет 10 назад писал на дельфи некое подобие мультиплеерного сервера со всяким извращениями типа динамической типизации и полуручным управлением временем жизни объектов.
Но то, что написано дальше - это ужас. Рекурсивные клоны больших объёмов данных потребуют овердохрена памяти, и приличных накладных расходов на синхронизацию. Работа с памятью в паскале тоже не эталон производительности, и лепить рекурсивные клоны (пусть даже частичные) игровых объектов, с тем, чтобы на следующем шаге продавливать их на предыдущий слой и снова клонировать - это изнасилование менеджера памяти в особо циничной форме.
Возможно, матчевые игры типа того же AoS с количеством игроков 4-6 с каждой стороны и временем одной игровой сессии минут в 10 ещё как-то можно крутить, но более длительные и масштабные игры других жанров - вряд ли.
Кроссплатформа к сожалению тоже пока что лучше получается на си/яве, и вряд ли паскаль их догонит (
Для реализации эпических масштабов можно запускать на всех клиентах синхронный код, производящий работу по уничтожению мира, но по окончании выполнения всё равно должна производиться проверка синхронности всех клиентов и сервера. Один из вариантов - при выполнении кода накапливать некоторое значение хеша/контрольной суммы, вычисленное на основе результатов изменения окружения, и по окончании сравнивать полученные значения. Если хеши совпали, то значит все клиенты и сервер получили на выходе одинаковый результат. Кто не справился - Out of
Sync.Cheb
>честно текущие жидкости на весь мир
> "натрави на оппонента стадо бизонов в 500 голов".
Тут аналогичная ситуация. Код, управляющий водой или стадом должен запускаться на всех клиентах и сервере синхронно, по команде сервера, и с определённой периодичностью должна происходить проверка результатов выполнения с целью контроля синхронизации. По сути, это уже не чистый lockstep, где все клиенты просто пытаются "шагать в ногу", ориентируясь друг на друга, а управляемый строй, в котором сервер выступает в качестве сержанта, орущего начало речёвки, и контролирующего, чтобы все хором орали продолжение.
Основная проблема, которая мне видится - скорость выполнения кода на всех клиентах должна быть на уровне самого медленного клиента. Иначе он не будет успевать и в какой-то момент потеряет синхронизацию.
Cheb
Кстати, а когда ты дашь нам пощупать твой революционный движок с процедурной генерацией?
заместо 100 метрового снапшота и промотки физики, можно время от времени снимать снимки с клиентов и держать на сервере почти актуальный снапшот, чтобы не проспуфили снапшот можно снять у нескольки клиентов один и тот же стейт снапшота и сравнить.
тогда к новому подключенному можно быстро слить почти актуальный снапшот и домотать последнии изменения.
это будет работать даже когда все игроки дропнутся с сервера а потом поновому будут заходить.
конечно это не чистый локстеп а клиент сервер, зато расчеты клиентские что в принципе и фишка локстепа.
>Рекурсивные клоны больших объёмов данных потребуют овердохрена памяти,
Вся прелесть в том, что нихрена они не потребуют сверх реально требуемого. Когда всё, что у тебя есть - это ссылочные типы со счётчиком ссылок... Всё извращение как раз в том и заключается, что реальное клонирование происходит при модификации объекта внутри тика физики, а все ссылки в нём продолжают указывать на объекты слоя-прародителя. Если в слое-прародителе один из этих объектов модифицируется - тот слой делает копию для себя, а у всех остальных слоёв остаётся старый неизменный инстанс.
То есть, перед каждой модификацией чего-либо я должен дёргать у объекта метод "склонируйся, если твой счётчик ссылок больше 1". Могло бы привести в ад отладки, но у меня на это есть Хитрый План (тм) - долго объяснять, покажу когда взлетит.
>Кроссплатформа к сожалению тоже пока что лучше получается на си/яве, и вряд ли паскаль их догонит (
Ну, виндовс будет, линукс будет, а в калашный ряд консолей меня с моим свиным рылом и так не пустят.
Поддержка Raspberry Pi 3, кстати, будет. Я в неё немало сил вложил, и она уже работает.
>Для реализации эпических масштабов можно запускать на всех клиентах синхронный код, производящий работу по уничтожению мира, но по окончании выполнения всё равно должна производиться проверка синхронности всех клиентов и сервера.
Вот то-то и оно, что надо что-то передавать. А в моей парадигме ничего передавать не надо, кроме потоков мышь/кнопки.
>Кстати, а когда ты дашь нам пощупать твой революционный движок с процедурной генерацией?
Текущими темпами, в 19 или 20 году можно будет уже поиграть :(
>можно время от времени снимать снимки с клиентов и держать на сервере почти актуальный снапшот,
А смысл?
Моя модель даёт абсолютную независимость объёма трафика от объёма изменений в мире.
И даже ещё интереснее: львиная доля трафика от сервера клиентам - это широковещательная рассылка идентичных данных. Возможны сетевые архитектуры на манер битторрента, субсерверы-ретрансляторы для локальной сети, и т.п.
>Возможно, матчевые игры типа того же AoS с количеством игроков 4-6 с каждой стороны и временем одной игровой сессии минут в 10 ещё как-то можно крутить, но более длительные и масштабные игры других жанров - вряд ли.
Так прокрутка вперёд - это решение проблемы со снапшотом, устаревшим пока закачивался. Которая иначе просто не решается.
>и временем одной игровой сессии минут в 10
в классическом режиме AoS время матча - 80 минут. И как то соединялся.
Но там *копание* на стороне сервера, и при подлагивании начинает подлагивать результат маха лопатой. Ты коп, коп - а оно не копается, и довольный кемпер тебя фрагнул, и ты такой FFFFffffffuuuu-----
>Код, управляющий водой или стадом должен запускаться на всех клиентах и сервере синхронно, по команде сервера
А у меня это даже будет лагокомпенсировано, и на клиенте, начавшем бучу, результат будет мгновенный.
тогда лучше начать пилить геймплей, если результат все таки игра, если нет то с удовольствием послежу за прогрессом, а все что под капотом интересует только самого автора и пару гиков в теме). конечному пользвотелю вообще пофигу, пока сейчас я вижу концепцию нечто техногенного.
>а управляемый строй, в котором сервер выступает в качестве сержанта, орущего начало речёвки, и контролирующего, чтобы все хором орали продолжение.
Виноват, проглядел. Да, у меня концепция клиент-серверного Lockstep, где сервер принимает инпуты от всех клиентов и широковещательно ретранслирует всем. Но при этом сервер может быть не один! Можно будет в локалке поднять свой связанный сервер, и тогда для всех игроковв этой локалки будет уменьшенный лаг, повышенная надёжность и прочие пряники.
А при обрыве связи между серверами эффект - как будто все игроки с не своего сервера вдруг откинулись.
>тогда лучше начать пилить геймплей,
Я столько лет шёл к реализации этой платформы - и должен всё бросить, не закончив, и начать строить бассейн на фундаменте Дворца Советов?..
Нет уж.
Cheb
> Я столько лет шёл к реализации этой платформы - и должен всё бросить, не
> закончив, и начать строить бассейн на фундаменте Дворца Советов?..
> Нет уж.
Как я тебя понимаю, брат )
У меня в заначках до сих пор лежат всякие поделки на дельфи 3/7, и я их изредка достаю, отряхиваю от пыли, слегка чего-нибудь подкручиваю, ностальгирую некоторое время, а потм прячу обратно )
Как например вот это (2012 год):
Скока-скока фейсов?.. 0.o
>и я их изредка достаю, [...] а потм прячу обратно
Вот я свой проект бросал два раза: в 2008-м и в 2012-м. Потому что все силы уходили в написание фанфиков. Но потом решил: хватит! И наконец увидел путь к цели во всей ясности и поплёлся им!
Методы оптимизации физики:
1. Медленные объекты движутся с пониженной частотой тиков. Любые проверки на столкновение с ними используют интерполяцию позиции, но объект реально передвигается в промежуточную позицию только если происходит воздействие на него.
2. Монотонно движущиеся объекты просчитывают своё движение впрок, сериями (ориентировочно, по 8 позиций). То есть, объект имеет 8 позиций коллайдера, распределённых по времени. Проверка на столкновение с ним осуществляется путём интерполяции реального положения коллайдера на тот момент. Серия прерывается, если происходит пересечение с другим динамическим объектом, и объект отправляет самому себе сообщение "проснуться в тик X, просчитать физику заново". Любое воздействие на такой размазанный по времени объект (например, статическую геометрию подрыли) локализует его во времени и пространстве и будит его.
Суммирую: движущиеся объекты бОльшую часть тиков спят, просчитав своё движение вперёд. Это радикально уменьшает загрязнение кеша, а соответственно - пропускную способность памяти, что критически важно, т.к. сильномогучих ядер в процессоре много, а шина памяти на всех одна.
То же самое с текущей водой: блоки воды просчитывают динамику втекания/вытекания и засыпают, до события или таймера.
Cheb
> Скока-скока фейсов?.. 0.o
Там реально 4 миллиона фейсов )
Разреженный куб 64*64*64, примерно 40% заполнение, по 6 граней (фейсов) на куб, за вычетом смежных невидимых.
Методику оптимизации бессовестно украду на будущее.
Реально основные тормоза происходят не при обсчёте, а из-за постоянных вызовов процедур для каждого объекта на каждый тик. Вызов процедур/переключение контекста одна из самых затратных статей для ИИ. Обсчёт 8-16-32 шагов за один вызов будет явно продуктивнее, чем 8-16-32 вызова. Затолкать результаты в массив, доставать оттуда по мере надобности. Даже интерполяция не нужна, можно пожертвовать памятью, и обсчитать/сохранить положения/состояния сразу с нужной дискретностью. При побудке и принудительном пересчёте учитывать, сколько прошло шагов до прерывания, и рассчитывать сначала на меньшее количество шагов, а если прерывания не происходит, то постепенно увеличивать дальность предрасчётов до максимума.
Я до такой схемы не додумался. Респект и уважуха, и спасибо за идею)
Cheb
> Скока-скока фейсов?.. 0.o
Вроде же не много. Вот тупо в блендере накидал высокодетализированных торов:
Все крутится на 60 FPS на моей GF960.
СерыйМыш
> Разреженный куб 64*64*64, примерно 40% заполнение, по 6 граней (фейсов) на куб
Как не перемножал - ну не выходит у меня 4М и все тут.
Тема в архиве.