Но погодите, ведь речь в докладе идёт именно о backend инфраструктуре (в значении сервисов — matchmaking, «валюта» игрока, рейтинг игрока, что угодно ещё), а не самой игры (если я ошибаюсь — поправьте меня, времени сейчас посмотреть доклад нет, но прочитал статью довольно внимательно). Да и сложно представить, чтобы на акторах кто-то разрабатывал шутер — это не имеет никакого смысла (load balancing в рамках игрового сервера не нужен, отказоустойчивость тоже; это игровой сессионный сервер и все к нему коннектятся и спокойно играют). Традиционные технологии прекрасно справляются — ещё со времён сетевой модели Quake 3 описанной Кармаком ничего нового не изобрели (всё те же механизмы client lag prediction, lag reconciliation, server state rewind, etc). К тому же, при разработке реальных игровых серверов для такого рода игр (fast-paced FPS/TPS), очень остро стоит вопрос о производительности (==цене) серверов. А оверхэд с акторами будет и ещё какой — нарушается cache locality, невозможно производить многие оптимизации, добавляются всевозможные абстракции…
А вот почему всё не хорошо ощущается в самой игре — хороший вопрос. Неужели действительно не используют сетевую часть idTech, или используют, но породили какие-то баги в ней?
Кстати, с удовольствием иногда смотрю этот канал www.youtube.com/user/xFPxAUTh0r1ty/videos автор только и занимается, что анализирует network performance ПК-игр, в том числе есть пара видео по Quake Champions.
Читы — имею ввиду разнообразные хаки, когда сервер доверяет клиенту часть authority. Как я понимаю, ECS позволяет симулировать независимо все системы и подразумевается что пересылается только инпут. Однако возможно, что сервер принимает и более сложные входные данные и даёт какой-то authority клиенту (чтобы сделать игру приятней с точки зрения клиента т.к. полная authority модель чрезвычайно сложная — в плане необходимости писать lag prediction и lag reconciliation механизмы которые всё равно не будут работать идеально просто потому что сеть не работает идеально). Именно это я и хотел бы уточнить.
В последние годы очень много «hack-fest» инцидентов в многопользовательских играх произошло и происходит, причём даже в случае ААА игр, а не только инди (которые во многом ограничены тем что им предлагает используемая высокоуровненовая сетевая библиотека большинство из которых заточены на P2P сетевую модель). Сервер слишком много доверяет. Примеры типичных хаков — клиент может попытаться поменять позицию и сервер с этим согласится, клиент может выстрелить когда не хватает патронов, клиент может прислать сообщение что по кому-то произвёл выстрел и успешно попал/убил и т.п…
Проблема настолько распространённая и дикая что разработчикам (особенно инди с небольшими бюджетами) игр с authoritative server model приходится отбиваться от заявлений игроков что игра легко ломается и т.д… У нас например один игрок сообщил, что хотел бы да боится хостить свой сервер игры ибо «есть слухи что ваша игра моментально ломается простейшими средствами». И доказательства никакие не требуются, уже верят наслово именно по той причине, что в последние годы ситуация печальная даже для десктопных игр — когда PUBG и Fortnite судятся с читерами и выигрывают, когда многие ААА игры (особенно отметились Ubisoft но там конечно спасибо нужно отдать P2P модели которую они с фейлом несколько лет пытались использовать с целью снижения расходов на серверную инфраструктуру, в итоге одумавшись и занявшись «гибридным подходом») имеют на ютубе удивительные видео с хаками, что ещё могут обычные игроки ожидать, тем более от инди? (если любопытно, вот мой официальный ответ игрокам на эту проблему forums.atomictorch.com/index.php?topic=1147.0 )
ECS это в принципе спорный подход, особенно для шутрера, особенно для мобильного
Тем не менее, на этом подходе успешно построен Overwatch и это не перестаёт меня удивлять (всё-таки там множество уникальных ability персонажей которые так просто не закодить с обычным ECS подходом). Возможно нужно просто уметь грамотно готовить ECS. Увы, когда я разбирался последний раз с этим, видео об ECS в Overwatch с GDC (2017?) было доступно только в GDC Vault за 550 USD/год (и вроде бы ничего с тех пор не изменилось).
Ни слова про читы? При таком подходе (в отсутствии серверного программиста или просто опытного специалиста) нередко вдруг оказывается так, что клиенту доверяется слишком многое, а потом удивительные вещи творятся… Как решает эту проблему подход с ECS? Происходит ли полностью независимая симуляция игрового мира на сервере только лишь с обработкой инпута игрока?
Я много лет разрабатываю онлайн игры и могу сказать, что подход с реюзаемостью кода между клиентом и сервером безусловно крайне полезен и позволяет сэкономить уйму времени. ECS же, в свою очередь, имеет свои минусы когда требуется нечто более сложное, чем сетевой шутер и логика обработки для определённых случаев должна затрагивать сразу множество компонент. Тут, однако, я не эксперт — мои эксперименты с ECS подходом не дали ожидаемых результатов и в ход пошло изобретение своих подходов. Собственно, ECS не обязателен для реюзания кода между клиентом и сервером, и подходы возможны разные. Если вам интересно, можете глянуть последний мой проект (ссылка на сайт студии в профиле) — там весь код (кроме движка) — с открытым кодом (пока без публичного Гитхаба, просто скачиваете и распаковывайте, игра идёт в комплекте с Roslyn'ом и имеет всё перекомпилировать и перезагружать на горячую). Реюзание кода позволяет реализовать независимую симуляцию игрового мира, включая перемещение персонажа (но тут в дело идёт lag prediction конечно же), использование предметов, менеджмент инвентаря, стрельба из оружия и т.п… Конечно, далеко не всё возможно таким способом симулировать — тогда в ход идёт маскировка лага — но в целом игра ощущается здорово если всё делать правильно. За счёт того, что всё основано на прозрачной репликации данных и автоматической сериализации RPC, приходится писать значительно меньше кода (так называемого водопроводного кода, особенно когда клиент и сервер на разных языках пишутся и требуется писать отдельные прослойки сетевых команд и т.п.).
Пока мы делаем это руками и когда появляется новая фича, а мы забываем вырезать эти данные, то что-то идет не так. На самом деле это можно генерировать, пометив каким-нибудь атрибутом.
Кстати, именно такой подход в своём проекте и использую: в описании класса-стейта данных необходимо пометить, какие поля (вернее C# свойства) реплицируются на клиент (опционально указать каналы передачи данных — reliable/unreliable, sequenced и т.п.; частота репликации тоже настраивается), дальше уже с помощью Roslyn движок сам генерирует нужный код уведомляющий сервер об изменении синхронизируемых свойств — все изменения передаются в систему репликации. Однако, конечно, тут есть ряд хитростей вроде того, что не все свойства должны реплицироваться — некоторые из них клиент-владелец персонажа может самостоятельно симулировать и лишь учитывать изменения от сервера (пример — стамина персонажа, её расход при беге и регенерация в покое; клиент может самостоятельно всё симулировать, а сервер в принципе может отправлять лишь изменения вроде «добавлено +30 стамины» (к примеру, был выпит energy drink)), в то время как другие клиенты всё-таки должны получать обновлённые значения таких свойств.
Из моего опыта разработки многопользовательских игр — дельта-компрессия серверного состояния мира (обычно только физического состояния мира — позиции/вектора объектов) хранит baseline (для каждого клиента) относительно которого и создаётся дельта-обновление. Клиент получает дельта-обновление и применяет его для получения нового baseline (=предыдущий baseline+delta) и в случае если этот baseline становится самым свежим — отправляет серверу ACK что теперь он имеет новый baseline (предыдущий baseline+delta). Сервер получив ACK задаёт данному клиенту новый baseline и далее уже относительно него умеет делать дельта-компрессию.
Клиент должен буферизировать baseline (т.к. входящие дельта-обновления будут приходить не для самого свежего baseline ввиду сетевой задержки).
Сервер должен буферизировать один baseline и последующие delta-обновления (чтобы в случае получения ACK на какое-либо дельта-обновление мог сделать baseline+delta и назначить его новым baseline).
Передавать полностью стейт мира (кроме initial при подключении) нужды нет вообще — у клиента будет гарантированно такой же стейт мира за счёт применения дельта-обновлений (для защиты от криворукости в дебаг-версию игры можно добавить baseline checksum в дельта-обновление — чтобы при построении нового baseline на клиенте сверить его с серверным).
Это довольно простой, но очень эффективный подход. Избыточность передаваемых данных крайне небольшая и компенсируется существенным выигрышем за счёт дельта-компрессии в целом.
> Кроме выше описанных, есть еще одно чудо-слово __arglist, которое позволяет сделать функцию с переменным числом параметров, да еще и любого типа.
> Странным является то, что нельзя из коробки организовать проход по элементам с помощью foreach, да и напрямую к элементу из списка не обратиться. Так что до С++ или JavaScript c его arguments не дотягивает.:)
Вообще-то и в этом плане дотягивает и ещё мощнее даже — используйте params ( url=https://msdn.microsoft.com/en-us/library/w5zay9db.aspx ). Cтрогая типизация и можно совмещать в одной сигнатуре метода постоянное и переменное число аргументов. Например, такой метод:
void Test(int a, params int[] extraArgs) { }
// пример вызова
Test(1, «extraArg», «extraArg2», «extraArgN»);
А вот почему всё не хорошо ощущается в самой игре — хороший вопрос. Неужели действительно не используют сетевую часть idTech, или используют, но породили какие-то баги в ней?
Кстати, с удовольствием иногда смотрю этот канал www.youtube.com/user/xFPxAUTh0r1ty/videos автор только и занимается, что анализирует network performance ПК-игр, в том числе есть пара видео по Quake Champions.
В последние годы очень много «hack-fest» инцидентов в многопользовательских играх произошло и происходит, причём даже в случае ААА игр, а не только инди (которые во многом ограничены тем что им предлагает используемая высокоуровненовая сетевая библиотека большинство из которых заточены на P2P сетевую модель). Сервер слишком много доверяет. Примеры типичных хаков — клиент может попытаться поменять позицию и сервер с этим согласится, клиент может выстрелить когда не хватает патронов, клиент может прислать сообщение что по кому-то произвёл выстрел и успешно попал/убил и т.п…
Проблема настолько распространённая и дикая что разработчикам (особенно инди с небольшими бюджетами) игр с authoritative server model приходится отбиваться от заявлений игроков что игра легко ломается и т.д… У нас например один игрок сообщил, что хотел бы да боится хостить свой сервер игры ибо «есть слухи что ваша игра моментально ломается простейшими средствами». И доказательства никакие не требуются, уже верят наслово именно по той причине, что в последние годы ситуация печальная даже для десктопных игр — когда PUBG и Fortnite судятся с читерами и выигрывают, когда многие ААА игры (особенно отметились Ubisoft но там конечно спасибо нужно отдать P2P модели которую они с фейлом несколько лет пытались использовать с целью снижения расходов на серверную инфраструктуру, в итоге одумавшись и занявшись «гибридным подходом») имеют на ютубе удивительные видео с хаками, что ещё могут обычные игроки ожидать, тем более от инди? (если любопытно, вот мой официальный ответ игрокам на эту проблему forums.atomictorch.com/index.php?topic=1147.0 )
Тем не менее, на этом подходе успешно построен Overwatch и это не перестаёт меня удивлять (всё-таки там множество уникальных ability персонажей которые так просто не закодить с обычным ECS подходом). Возможно нужно просто уметь грамотно готовить ECS. Увы, когда я разбирался последний раз с этим, видео об ECS в Overwatch с GDC (2017?) было доступно только в GDC Vault за 550 USD/год (и вроде бы ничего с тех пор не изменилось).
Я много лет разрабатываю онлайн игры и могу сказать, что подход с реюзаемостью кода между клиентом и сервером безусловно крайне полезен и позволяет сэкономить уйму времени. ECS же, в свою очередь, имеет свои минусы когда требуется нечто более сложное, чем сетевой шутер и логика обработки для определённых случаев должна затрагивать сразу множество компонент. Тут, однако, я не эксперт — мои эксперименты с ECS подходом не дали ожидаемых результатов и в ход пошло изобретение своих подходов. Собственно, ECS не обязателен для реюзания кода между клиентом и сервером, и подходы возможны разные. Если вам интересно, можете глянуть последний мой проект (ссылка на сайт студии в профиле) — там весь код (кроме движка) — с открытым кодом (пока без публичного Гитхаба, просто скачиваете и распаковывайте, игра идёт в комплекте с Roslyn'ом и имеет всё перекомпилировать и перезагружать на горячую). Реюзание кода позволяет реализовать независимую симуляцию игрового мира, включая перемещение персонажа (но тут в дело идёт lag prediction конечно же), использование предметов, менеджмент инвентаря, стрельба из оружия и т.п… Конечно, далеко не всё возможно таким способом симулировать — тогда в ход идёт маскировка лага — но в целом игра ощущается здорово если всё делать правильно. За счёт того, что всё основано на прозрачной репликации данных и автоматической сериализации RPC, приходится писать значительно меньше кода (так называемого водопроводного кода, особенно когда клиент и сервер на разных языках пишутся и требуется писать отдельные прослойки сетевых команд и т.п.).
Кстати, именно такой подход в своём проекте и использую: в описании класса-стейта данных необходимо пометить, какие поля (вернее C# свойства) реплицируются на клиент (опционально указать каналы передачи данных — reliable/unreliable, sequenced и т.п.; частота репликации тоже настраивается), дальше уже с помощью Roslyn движок сам генерирует нужный код уведомляющий сервер об изменении синхронизируемых свойств — все изменения передаются в систему репликации. Однако, конечно, тут есть ряд хитростей вроде того, что не все свойства должны реплицироваться — некоторые из них клиент-владелец персонажа может самостоятельно симулировать и лишь учитывать изменения от сервера (пример — стамина персонажа, её расход при беге и регенерация в покое; клиент может самостоятельно всё симулировать, а сервер в принципе может отправлять лишь изменения вроде «добавлено +30 стамины» (к примеру, был выпит energy drink)), в то время как другие клиенты всё-таки должны получать обновлённые значения таких свойств.
Клиент должен буферизировать baseline (т.к. входящие дельта-обновления будут приходить не для самого свежего baseline ввиду сетевой задержки).
Сервер должен буферизировать один baseline и последующие delta-обновления (чтобы в случае получения ACK на какое-либо дельта-обновление мог сделать baseline+delta и назначить его новым baseline).
Передавать полностью стейт мира (кроме initial при подключении) нужды нет вообще — у клиента будет гарантированно такой же стейт мира за счёт применения дельта-обновлений (для защиты от криворукости в дебаг-версию игры можно добавить baseline checksum в дельта-обновление — чтобы при построении нового baseline на клиенте сверить его с серверным).
Это довольно простой, но очень эффективный подход. Избыточность передаваемых данных крайне небольшая и компенсируется существенным выигрышем за счёт дельта-компрессии в целом.
> Странным является то, что нельзя из коробки организовать проход по элементам с помощью foreach, да и напрямую к элементу из списка не обратиться. Так что до С++ или JavaScript c его arguments не дотягивает.:)
Вообще-то и в этом плане дотягивает и ещё мощнее даже — используйте params ( url=https://msdn.microsoft.com/en-us/library/w5zay9db.aspx ). Cтрогая типизация и можно совмещать в одной сигнатуре метода постоянное и переменное число аргументов. Например, такой метод:
void Test(int a, params int[] extraArgs) { }
// пример вызова
Test(1, «extraArg», «extraArg2», «extraArgN»);