Компания
954,59
рейтинг
23 апреля 2014 в 11:36

Разработка → Архитектура сервера онлайн-игры на примере Skyforge

Привет, Хабр! Я Андрей Фролов, ведущий программист, работаю в Mail.Ru над Next-Gen MMORPG Skyforge. Вы могли читать мою статью про архитектуру баз данных в онлайн-играх. Сегодня я буду раскрывать секреты, касающиеся устройства сервера Skyforge. Постараюсь рассказать максимально подробно, с примерами, а также объясню, почему было принято то или иное архитектурное решение. По нашему серверу без преувеличения можно написать целую книгу, поэтому для того, чтобы уложиться в статью, мне придется пройтись только по основным моментам.

image


Обзор


  • Сервер — это почти два миллиона строк кода на Java. Для соединения с сервером и отображения красивой картинки используется клиент, написанный на C++.
  • Свой вклад в серверный код внесли полсотни программистов. Код писался в течение многих лет лучшими специалистами российского «православного» геймдева. В нем собраны все самые удачные идеи со всего мира.
  • На текущий момент у нас написано около 5200 автоматических тестов, налажен continuous integration и нагрузочное тестирование с помощью ботов.
  • Сервер умеет запускаться и работать на десятках и сотнях серверов, поддерживать игру сотен тысяч человек одновременно. Мы решили отказаться от традиционной для MMO техники шардирования и запустить всех игроков в один большой мир.


Первое и главное правило разработки сервера: клиент в руках врага. Клиент защищен, но чисто теоретически и его могут хакнуть, могут расшифровать клиент-серверный протокол. Взлом клиента может привести к обходу игровых правил, читам, ботоводству и т.п. Такие вещи разрушают игру для всех. Чтобы этого не произошло, мы должны эмулировать весь игровой мир со всеми игровыми правилами у себя на сервере, а клиент использовать только для отображения красивой картинки. Кроме того, клиент надо проверять на взлом, отслеживая подозрительное поведение и т.д.



Сервисная архитектура


Одна из основных особенностей разработки состоит в том, что мы не знаем, сколько у нас будет игроков. Может быть, всего один — сам разработчик, а может, 100000 одновременно. Поэтому сервер должен уметь запускаться в маленькой конфигурации, на ноутбуке, и растягиваться при необходимости на десятки и сотни мощных серверов.

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

Третья большая проблема — это многопоточность. Как известно, лучший способ сладить с многопоточностью — это избежать ее. Deadlock, livelock, lock contention и другие милые сердцу программиста проблемы можно обойти, если архитектура сервера будет избавлять вас от необходимости синхронизировать потоки вручную. В идеале программист вообще должен писать простой однопоточный код и не задумываться о такого рода вещах.

Отсюда родилась наша универсальная структура сервера, которая используется в Skyforge:
  • Существует пул физических серверов, на которых будет запускаться игра. Этот набор серверов и наше серверное приложение, которое на них запущено, называется Realm.
  • На каждом сервере запускается серверное приложение (JVM), называемое ролью. Роли бывают разные: аккаунт-сервер, игровая механика, чат и т.д. Каждая роль берет на себя большой кусок функционала. Некоторые роли существуют в единственном числе, некоторые запускаются в нескольких экземплярах.
  • Роль состоит из набора сервисов. Сервис — это обычный поток (thread), который занимается своей, специфичной для него задачей. Примером сервиса может служить сервис авторизации, сервис резервирования имен, балансировщик нагрузки и т.п. Каждый сервис ничего не знает о физическом расположении других сервисов. Они могут быть рядом, а могут быть на другой физической машине. Сервисы взаимодействуют через систему сообщений, которая скрывает от них такого рода подробности.
  • Каждый сервис состоит из набора модулей. Модуль — это «кусок функциональности», у которого есть один метод tick(). Примером модуля может быть модуль статистики, модуль исполнения транзакций, модуль синхронизации времени. Вся работа сервиса заключается в том, чтобы в бесконечном цикле поочередно вызывать метод tick() у своих модулей. Один такой цикл называется «кадр сервера». Мы считаем показатель хорошим, если кадр сервера колеблется в пределах от 3 до 20 мс.
  • Вся эта структура описывается в XML-файлах. Системе запуска надо просто «скормить» название роли. Она найдет соответствующий файл, запустит все нужные сервисы и отдаст им списки модулей. Сами модули создадутся с помощью java reflection.




Таким образом, мы можем запустить роль «локальный сервер», где будет все необходимое, а можем разбить сервер на несколько десятков ролей — аккаунт-сервер, итем-сервер, игровая механика и т.д. — и запускать его на десятках разных физических серверов. Структура оказалась чрезвычайно гибкой и удобной, советую серьезно к ней присмотреться.

Основные сервисы


Есть некоторый набор сервисов, который несет основную игровую нагрузку. Каждый из серверов должен уметь масштабироваться. В идеале — до бесконечности. К сожалению, писать серверы так, чтобы они масштабировались, это непростая задача. Поэтому мы начали с того, что сделали основные сервисы масштабируемыми, а всякую дополнительную мелочь, которая не несет основной нагрузки, оставили на потом. Если у нас будет очень много пользователей, то и их нам придется улучшать для обеспечения возможности масштабирования.
  • Аккаунт-сервис. Отвечает за авторизацию и подключение новых клиентов.
  • Сервер игровой механики. Тут происходит, собственно, сама игра. После прохождения авторизации клиент подключается сюда и тут играет. С другими сервисами клиент напрямую не взаимодействует. Таких сервисов можно и нужно запускать несколько десятков, а то и сотен. Именно эти сервисы несут основную нагрузку.
  • Сервисы баз данных. Эти сервисы выполняют операции над данными игровых персонажей, их предметами, деньгами, прогрессом развития. Их обычно запускается несколько штук. Подробнее об архитектуре баз данных можно прочитать в моем прошлом докладе. ( habrahabr.ru/company/mailru/blog/182088 )
  • Чат. Занимается роутингом сообщений чата между пользователями.
  • Все остальные сервисы. Их несколько десятков, и они обычно не создают сильной нагрузки, поэтому не требуют обособленных серверов.




Сеть


Под словом «сеть» я подразумеваю систему доставки сообщений от одного сервиса к другому или от одного объекта к другому. Исторически так сложилось, что у нас существует сразу две такие системы. Одна основана на сообщениях. Вторая система основана на удаленном вызове процедур (RPC). В Skyforge система сообщений применяется внутри сервиса игровой механики, чтобы послать какое-то сообщение от аватара к мобу, а также для общения клиента и сервера. RPC используется для общения между сервисами.



Сообщения

Все объекты, которые хотят посылать или принимать сообщения, называются абонентами. Каждый абонент регистрируется в общей директории и получает уникальный идентификатор — адрес. Любой, кто хочет послать сообщение какому-либо абоненту, должен указать адреса «откуда» и «куда». Сетевой движок знает, где находится абонент, и доставляет ему сообщение. Сообщение — это Java-объект, у которого есть метод run(). При отправке этот объект сериализуется, прилетает к целевому абоненту, там десериализуется, а затем вызывается метод run() над целевым абонентом.



Такой подход очень удобен тем, что позволяет реализовывать простые команды типа «нанести удар», «выдать анлок», «запустить фаербол». Вся эта логика оказывается внешней по отношению к объекту, над которым выполняется действие. Большой минус этого подхода в том, что если логика команды требует выполнения какого-либо кода на нескольких абонентах, то нам потребуется сделать несколько сообщений, которые будут посылать друг друга по цепочке. Логика оказывается фрагментирована на несколько классов, и цепочки сообщений часто довольно долго и сложно распутывать.

RPC

Удаленный вызов процедур или RPC появился, чтобы решить проблему цепочек сообщений.
Основная идея заключается в использовании кооперативной многозадачности (Coroutine, Fibers). Тому, кто не знаком с это концепцией, для понимания темы советую заглянуть в «Википедию». en.wikipedia.org/wiki/Coroutine.
Сервис, который хочет, чтобы его могли вызывать через удаленный вызов процедур, должен реализовывать специальный интерфейс и зарегистрировать в специальной директории. Тогда любой желающий может попросить директорию дать ему интерфейс этого сервиса, и директория вернет специальный враппер над сервисом. Вызывать сервисы по RPC можно только внутри файбера (coroutin), т.е. специального контекста исполнения, который можно прерывать и возобновлять в точках разрыва. При вызове методов враппера он будет посылать RPC вызовы на удаленный сервис, прерывать текущий файбер в ожидании ответа и возвращать результат, когда удаленный сервер ответит.



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

Подробнее о нашей имплементации файберов можно узнать из лекции Сергея Загурского ( www.youtube.com/watch?v=YWLHELcvNbE ).

Сериализация

Чтобы у нас работала система посылки сообщений и удаленный вызов процедур, нам нужен клиент-серверный протокол и способ сериализации/десериализации объектов. Напомню, что у нас есть необходимость пересылать команды и данные с клиента на сервер, т.е. из C++ в Java и обратно. Для этого мы по Java-классам генерируем их копии в C++, а также генерируем методы для сериализации и десериализации объектов в байтовый поток. Код для сериализации встраивается прямо внутрь классов и обращается к полям класса напрямую. Таким образом, сервер не тратит процессорное время на обход классов с помощью reflection. Все это мы генерируем с помощью самописного плагина для IntelliJ IDEA. Внутрисерверный протокол для общения между сервисами полностью аналогичен клиент-серверному протоколу.

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



Игровая механика


Основной сервис, который был бы вам интересен, это сервис игровой механики. Именно там выполняется весь код, непосредственно связанный с игрой, именно там моделируется весь игровой мир, летают фаерболы и «грабятся корованы».

Карты и балансировка нагрузки

На серверах игровой механики создаются карты, на которых, собственно, находятся игроки, мобы и происходит все веселье. У каждой карты есть лимит на количество игроков, которые могут на ней находиться. Например, лимит может быть равен единице для персональных приключений, 10–30 для групповых активностей и 250 для больших карт. Что происходит, если на карту захочет попасть еще один игрок, когда лимит исчерпан? Тогда будет создана еще одна копия той же самой карты. Игроки с этих карт не будут видеть друг друга, не будут друг другу мешать. Т.е. в каком-нибудь игровом городе могут быть тысячи человек, но там не будет тесно. Такой способ организации игроков называется «каналы».

За создание карт отвечает центральный сервис «балансировщик карт», который распределяет карты по сервисам игровой механики в зависимости от популяции, нагрузки и других магических причин, стараясь поддерживать равномерное распределение нагрузки и нормальную плотность играющих, чтобы им не было скучно.

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



Аватары и мобы

Аватар — это персонаж, которым управляет игрок, моб — это монстр, которого игрок убивает. Это весьма разные, но часто очень похожие сущности. И моб, и аватар умеют ходить по карте, у них есть здоровье, они могут использовать заклинания и т.п. Только аватаром управляет игрок, а у моба есть свой мозг. Кроме того, на картах есть множество всяких сундуков, растений и других интерактивных сущностей. Очень часто нужно делать некую функциональность и цеплять ее к разным сущностям. Для этих целей мы используем компонентный подход, собирая игровую сущность из набора функциональностей. Поясню на примере. Допустим, у игрока и моба есть показатель здоровья. В таком случае мы оформляем элемент «здоровье» как отдельный Java-класс, в котором описываем, как здоровье себя ведет: как оно может уменьшаться, как восстанавливаться, какие есть таймеры и т.п. Потом мы просто складываем все функциональности в специальную HashMap внутри сущности и берем ее оттуда по необходимости. Таких компонент у нас существуют сотни, на них собрана половина игровой механики.

Так как серверное приложение очень сложное, неизбежно возникновение ошибок. Нужно сделать так, чтобы возникновение ошибки, даже необработанного NullPointerException, не приводило к падению сервера. Можно ошибку просто залогировать и пойти дальше, но если ошибка возникнет посреди какого-то длинного действия над аватаром, то аватар может оказаться в сломанном и неконсистентном состоянии. Тут нам на помощь приходит концепция под названием «локаль». Локаль — это контекст, внутри которого объекты могут ссылаться друг на друга. Объекты из одной локали не могут ссылаться на объекты из другой. Если из локали вылетает необработанное исключение, то локаль удаляется целиком. Аватары, мобы и другие сущности являются локалями, удаляются целиком и не могут держать ссылок на других аватаров и мобов. Поэтому все взаимодействие между аватарами и мобами идет через систему сообщений, хотя они находятся вместе на одной машине и в теории могли бы держать друг на друга прямую ссылку.

Репликация

Моделировать игровой мир нужно не только на сервере, но и частично на клиенте. Например, клиенту нужно видеть других игроков и мобов, которые находятся рядом с ним. Для этого используется механизм клиент-серверной репликации, когда с сервера клиентам рассылаются обновления окружающего игрового мира. Делается это с помощью генератора кода, который встраивает отсылку обновлений в сеттеры серверных Java-объектов. Вокруг игрока создается круг определенного радиуса, и если кто-то, например другой аватар, попадает в этот круг, он начинает реплицироваться на клиент. С репликацией есть фундаментальная проблема. Если в одном месте столпится N аватаров, то на каждого из них нужно будет посылать N реплик. Таким образом возникает квадратичная зависимость, что ограничивает количество аватаров, которые могут собраться в одном месте. Именно из-за этой фундаментальной квадратичности клиенты всех ММО тормозят в столицах. Мы избегаем этой проблемы, ограничивая количество игроков на карте и распределяя их по каналам.

Ресурсная система

В игре существуют сотни и тысячи заклинаний, предметов, квестов и других подобных сущностей. Как вы, наверное, догадываетесь, программисты не пишут все сотни квестов, это делают геймдизайнеры. Программист разрабатывает один Java-класс квеста, а описания всех квестов с их логикой, задачами и текстами содержатся в XML-файлах, называемых ресурсами. При старте сервера мы загружаем эти ресурсы и на их основе собираем Java-классы с описанием мира. Этими классами уже может пользоваться сервер. Примерно такая же система существует и на стороне клиента, только там ресурсы не грузятся из XML-файлов, а просто загружается заранее созданный «кусок памяти», содержащий все нужные объекты и ссылки между ними. Ресурсных файлов у нас существует несколько сотен тысяч, но их загрузка на сервере занимает около двух минут. На клиенте же все грузится за секунды. Система очень навороченная, поддерживает такие фичи, как прототипы и наследование, вложенные описатели и т.п. Поверх ресурсной системы у нас созданы специализированные программы для редактирования карт и остальных игровых сущностей.



Сервер в действии


Давайте теперь на примерах рассмотрим несколько сценариев того, как работает вся эта система в действии.

Убить собачку

Классический тест, который мы всегда проводим, если сильно изменили инфраструктуру и хотим проверить, что все работает, называется «Убить собачку». Нужно зайти клиентом на сервер и убить там какого-либо моба. Этот тест покрывает практически все основные моменты сервера и служит прекрасным примером для того, чтобы сложить все вышесказанное вместе. Давайте по пунктам разберем, что и как происходит при убиении несчастной собачки. Конечно, некоторые шаги упрощены, но это не критично для понимания.
  • Клиент посылает на аккаунт-сервер сообщение: «Хочу войти в игру».
  • Аккаунт-сервер запрашивает базу данных, проводит авторизацию и запрашивает у балансировщика карту, на которой игрок был в последний раз.
  • Балансировщик выбирает карту из уже загруженных или создает новую на наименее загруженном сервере игровой механики.
  • Клиент подключается к той механике, где для него была создана карта. Пока он подключается, для него загружается его аватар.
  • Сервер начинает реплицировать все объекты вокруг аватара на клиент. Клиент рисует шикарную картинку и посылает на сервер команды, которые посылает игрок.
  • Игрок начинает бегать по карте, а сервер перемещает его по миру и реплицирует изменения окружающей действительности. Игрок находит какого-либо моба и нажимает кнопку «ударить».
  • Команда «удар» прилетает на сервер, на сервере выполняется проверка, что удар возможен, и мобу отправляется сообщение о нанесении повреждений.
  • Команда «нанести повреждения» отрабатывается на мобе, просчитывает все резисты и другие подобные вещи, потом берет функциональность «здоровье» и списывает какое-то количество.
  • Клиенту посылается ответ с подтверждением нанесения урона, клиент рисует удар.


Масштабирование

Давайте зайдем с другой стороны и посмотрим, как сервер ведет себя под нагрузкой.
  • 0 клиентов. Если на сервере никого нет, его можно запускать одним приложением с минимальными настройками и без карт. На сервере нет никакой активности, и большую часть времени он простаивает.
  • 1 клиент. Для одного клиента приходится создавать карту, мобов, серверные объекты, которые начинают потреблять память и процессорное время для своей жизни.
  • 500 клиентов. 500 клиентов обычно уже достаточно много, чтобы процессорного времени одной персоналки не хватало для работы сервера. Приходится запускать realm на нескольких машинах или на более мощных серверах.
  • 10000 клиентов. 10000 клиентов требуют уже нескольких серверов. Так как большая часть нагрузки приходится на игровые механики, нужно запускать realm с дополнительными сервисами игровой механики.
  • 100000 клиентов. При 100000 одновременных игроков больше половины серверов заняты игровой механикой.
  • Клиентов больше, чем железа. Если вдруг игроков станет еще больше, а железо у нас вдруг кончится, то придется ограничивать вход людей в игру, пока подвозят новые серверы. Для этого существует очередь на вход, которая заставляет игроков ждать, когда сервер будет готов их принять. Эта очередь гарантирует, что одновременно один realm не может содержать игроков больше, чем мы готовы принять. В очередь игроков могут начать ставить и в том случае, если из-за бага или еще по каким-либо причинам сервер вдруг стал работать медленнее определенного порога. Лучше сделать приемлемый сервис для ограниченного числа клиентов, чем упасть для всех.


Заключение


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


Ну и конечно же запись на закрытый бета тест. sf.mail.ru

Занятная статистика


Статистика по строкам кода. В статистику включен только сервер.



Авторы, собранные из комментариев к коду.



P.S.: Как обычно, на все вопросы отвечаем в комментариях.
Автор: @Randll

Комментарии (179)

  • +18
    Спасибо огромное, у вас отличные статьи по теме.

    1. Читая ваш текст про абонентов и сообщения, все время возникал вопрос «почему не попробовали Akka?» это же оно самое один в один.
    Просто сейчас пилю сервер на Scala+Akka и уже очень много раз порадовался, на сколлько работа с акторами (абоненты в вашей терминологии) через систему сообщений, облегчает разработку такой сложной системы как игровой сервер.
    2. Будет ли продолжение с подробностями? Например как устроена сетевая часть.
    3. В начале вы пишете «Мы решили отказаться от традиционной для MMO техники шардирования и запустить всех игроков в один большой мир.», а далее по тексту рассказываете про разделение по каналам и что игроки на самом деле не видят друг друга. В чем тогда заключается единый мир, если игроки не могут взаимодействовать друг с другом?
    • +3
      1) На старте проекта у нас была уже готовая реализация нашего серверного движка, которая нас устраивала, с которой мы умели работать, знали все её особенности и ограничения. Не было никакой мотивации переходить на любой другой, пусть даже самый модный, движок. Тем более тогда он был ещё в зачаточном состоянии.

      2). Это зависит от коллег :) Если они захотят, то напишут.

      3) Единый мир заключается в том, что гильдии, поиск приключений и прочие социальные взаимодействия возможны между всеми игроками реалма. Поиск приключений идёт быстрее, торговля лучше, флуд на форуме не надо разделять на подфорумы по шардам и т.п.
      • +2
        Мы избегаем этой проблемы, ограничивая количество игроков на карте и распределяя их по каналам.

        Тогда собственно вопрос такой касательно этой циаты: в ВОВ-е периодически кланом собирались в одном месте в городе — обменяться вещами, етс. Я так понимаю подобной ситуации у вас не получится создать? Или как-то можно запихать весь клан в один «кнал»?
        • 0
          Весь клан по умолчанию будет пытаться сливаться в один канал. Но нет гарантии, что это получится.

          Механизм для форсированного сбора всех в одной точке пока не допилен, но вроде как хотели сделать что-то вроде спела «собраться», который будет принудительно телепортить всю группу (не гильду) в одну кучу :) Если надо будет делать то же самое для гильдий, то запилим.
        • +2
          В GuildWars 2 реализовали приоритетами — больше шансов попасть в тот же «канал», что и человек, которого ты видел хоть один раз в игре/он с тобой в пати/из твоего клана etc.

          P.S. Интересно было бы у них узнать про эту реализацию. Кто-нибудь хочет провести интервью и сделать перевод? :)
      • +2
        Если я правильно понимаю, то разделение по каналам на картах обратимое. Как в Firefall?
        После авторизации игрок попадает в один инстанс карты. Связавшись с кем-то из друзей в общем чате он может быть добавлен во временную команду. После некоторых манипуляций (игровые команды или тп) игрок будет иметь возможность переместиться в инстанс карты, в которой присутствует другой игрок.
        Если так, то мир почти единый. Чат, экономика разделяются между каналами, а события карты нет.
        • +1
          Не знаю как в Firefall, но у нас всё примерно так как вы описали.
  • +2
    Можно выложить ссылку на статью на форуме sf.my.com? :)
    Там народ уже истосковался по ЗБТ и будет рад любой свежей информации, даже технической.
    • +1
      Мы не выкладываем туда, так как все-таки статья техническая. Недавно, кстати, открыли sf.mail.ru для русскоязычных игроков, и в ближайшее время планируем публиковать немало новостей о мире Skyforge.
      Так что есть чем порадовать игроков =)
      Но, если игроки захотят поделиться информацией о статье на форуме мы будем совсем не против.
  • +2
    У меня глаз зацепился за кусок кода и не отпускает:

    Override
    public void run(@NotNNull Abonent abonent) {
    if (! (abonent instanceof Avatar)) {
    Cast.logError(abonent, Avatar.class);
    return;
    }
    final Avatar avatar = (Avatar) abonent
    //…
    }

    По сути это нарушение LSP, в том моменте где вы начинаете проверять тип абонента. Что говорит о том, что есть проблема в архитектуре. Это вынужденная мера?

    В теории, если уж вам приходит Abonent, то вы должны внутри ориентироваться на его интерфейс. Иначе какой смысл указывать в контракте тип аргумента?
    • +1
      Есть разные мнения на этот счёт :)
      Если бы мы сделали типизированные сообщения, то внутри месседж системы нам пришлось бы cделать unchecked cast.
      В этом случае мы делаем checked cast и можем отреагировать как-нибудь осмысленно.

      Обычно делается мессажка MsgToAvatar extends Msg, в которой прячется этот каст, и все кому надо посылать сообщения аватару наследуются от него и не пишут этот каст 100500 раз.
      • +1
        | Если бы мы сделали типизированные сообщения, то внутри месседж системы нам пришлось бы cделать unchecked cast.
        Зачем? Если вам приходит точно Avatar через аргумент, зачем вам приводить типы?

        На сколько я понял из статьи(я мог ошибиться), то у вас идут Msg от Abonent к Abonent и они выполняются(метод run) на Abonent получателе.
        Проверку самого Abonent поручили самому Msg. А логика того кто кому может послать Msg прячется исключительно в интерфейсе клиента. Другими словами, это клиент решает, что при наведении курсора на табуретку, отключается возможность послать Msg с действием ударить. Но мы это сможем сделать взломав клиент и тогда у вас будет в логи сыпать недопустимые операции.

        Правильно я всё понял?
        Если так, то очень интересна архитектура проверка доступности сообщений на клиенте. Т.к. мне кажется. вы решали проблему согласованности ограничений игровой логики и ошибок сообщений, если действие было выполнено, но не не поддерживается самим Msg.
        • 0
          Да вы всё правильно поняли. Можно послать сообщение неверному адресату, и это печально. В идеале и адреса и абоненты должны быть типизированы. Но… так исторически сложилось :) Ввиду количества кода переделать это не так просто.

          Доступность сообщений на клиенте отдана на откуп интерфейсу. Если его сломать можно слать какие угодно сообщения кому угодно. Но везде, где это хоть сколько-нибудь опасно или должно быть ограниченно игрой есть серверные проверки. Тогда в логах сервера будут ошибки, мы их увидим и плохого игрока… кхм… накажем :)
          • +2
            Вспоминается история с ребятами из Archeage, которые убивали всех вокруг на Острове свободы каким-то боссовским или гмским скиллом.
            • 0
              На всё это есть серверные проверки :)
          • –1
            На самом деле это подперто и клиент никаких левых сообщений прислать не сможет.
            В аннотации @ReplicateCmd указывается уровень доступа клиентской команды. Когда команда приходит на frontend, её уровень сразу же сверяется с уровнем доступа аккаунта, и при несоответствии она не будет выполнена и еще в лог напишут, что была попытка взлома.

            Поэтому даже если игрок сумеет с клиента отправить какой-то чит, он не пройдет, так как у его аккаунта недостаточно прав на сервере.
            • +1
              Не ну с читами понятно, но можно же отправить каст спела в сундук в качестве таргета. И тут вот сработает чек, что сундук не аватар.
              • +1
                Доступность сообщений на клиенте отдана на откуп интерфейсу. Если его сломать можно слать какие угодно сообщения кому угодно. Но везде, где это хоть сколько-нибудь опасно или должно быть ограниченно игрой есть серверные проверки. Тогда в логах сервера будут ошибки, мы их увидим и плохого игрока… кхм… накажем :)


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

                Я поэтому и решил откомментить дополнительно, что все подперто, выполнить некорректную команду не получится.
            • 0
              Кстати, интересно было бы узнать про то как у вас написан RBAC.
              Я сейчас пишу систему на Pyrmid и права на действия тоже указываются в анотациях, но мне не нравится оверхед на запрос данных о пользователе, о его правах и о предмете доступа.
              • 0
                Ну мир у нас не stateless, поэтому из базы права доступа мы запрашиваем только один раз, когда игрок входит в игру. Дальше права кэшируются на легате и сверяется какие права указаны в аннотации на комманде и какие есть у пользователя. Оверхэд есть, но не большой.
      • +1
        Если бы соблюдался LSP то у вас там вообще бы не было кастов. А у вас еще и SRP нарушен и семантика.
        Имхо, этой логики вообще там быть не должно. Почему «Сообщение» внезапно кого-то убивает? Зачем вообще какая-то логика в типичном DTO?
        • +2
          Потому, что это походу не DTO, а типа Command Pattern.
        • +1
          Всё у нас не по учебнику :)

          Мессажка это действительно «типа Command Pattern».

          В некоторых местах мы посылаем мессажку-носитель, в которой есть классик с данными. На приёмнике делается обработчик, подписывающийся на приход опеределённого типа данных. Это наверное ближе к DTO. Но этот подход используется реже, не очень удобно.
          • +1
            Дело в не том, что не по учебнику, а в том, что таким кодом вы сами себе стреляете по ногам.

            Command Pattern хорошо работает до тех пор, пока у вас исполнение команды не зависит от того, кто её исполняет. Я представляю, какой длинны будет switch по типам для команды «использовать». Если это сундук (контейнер) — открыть, если это залежи минералов — копать, если это какой-нибудь квестовый колокольчик — поставить флаг в квесте, и это, похоже, только начало…

            А будь у вашего Аватара и условного камня разные обработчики на одни и те же команды (Visitor pattern), то отправленное неверному адресату сообщение ничего бы не могло сломать в принципе. А верные адресаты — знали бы внутри себя, что с таким сообщением делать.
            • +2
              Да вроде ноги у всех на месте, после многих лет разработки и тестов. Хоть ситуация с тем, что мессажку послали не туда, и возможна, возникаетс она довольно редко.

              Всё проще. В псевдокоде это выглядит примерно так
              run(Abonent) {
              !(Abonent instanceof Interractable)
                Fail();
              
               obj = (Interractable) Abonent;
              
              obj.checkPrecondition(actorData)
              
              obj.interract(actorData), 
              }
              


              Никаких свичей, обычная перегрузка методов. ООП все учили. Логика камушка — в камушке, логика колокольчика — в колокольчике.

              p.s. Хотя, конечно, это только пример. Интерракшны с реальными объектами гораздо сложнее.
            • 0
              switch по типам для команды «использовать»

              вот уж такое точно не нужно
              достаточно вычитывать эти команды по частям и рассовывать их по обработчикам из, например, мапы
    • 0
      Думаю на такие нарушения можно пойти ради производительности. В данном случае Msg обрабатывается только для объектов класса Аватар, но вообще, видимо, интерфейс Abonent может быть реализован разными классами.
      • +1
        Абонент, кстати, не интерфейс а класс. В нём есть адресс и несколько функций по менеджменту жизненного цикла.

        Да не, это не из-за перфоманса. Cast-ы в Java очень быстрые. Скорее ближе к «так исторически сложилось» :)
    • 0
      Да, кстати, @NotNNull — что за аннотация такая?
  • 0
    ---
  • +2
    Захожу на сайт sf.mail.ru/, а там alert(1);
    Никак XSS?
    <div class="title_forum"> <a href="http://portal.sf.mail.ru:80/redirect/comments/5357771eb1d03dc83ea42c78/FirstUnvisited">"><img src=x onerror=alert(1)></a> </div>
    • +3
      Да, спасибо, похоже кто-то правила конкурса(https://hackerone.com/mailru) не соблюдает :) Чиним.
  • +6
    Очень интересное описание, но при этом поверхностное, хотелось бы больше технических деталей!

    Если не секрет, какую JVM вы используете, много ли тюнили в ней? Можно ли узнать примерно конфигурацию одной ноды/сервера для роли (хотя бы в контексте «память/процессор/дисковая система»)? Насколько я понимаю, вы храните все данные централизованно? Если не секрет, где и как? Не могли бы вы подробнее описать механизм локалей, из статьи не сильно понятно, это концепция чисто техническая (для отсечения указателей на другие объекты для обеспечения консистентности) или у нее какие-то более глубокие смыслы (типа транзакционности)? Как обрабатываются откаты?

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

    Что будет если мой друг Вася пригласит меня встретится на карте А, на которой кроме него уже 250 человек? Мы будем безуспешно искать друг-друга на разных вариантах этой карты? Чем это отличается от реалмов в лучшую сторону? Скажем, в том же ВоВе есть возможность создания кросс-серверных групп, которые будут играть на одном сервере.

    Конечно, я понимаю, что по архитектуре таких приложений можно писать книги.
    • +4
      JVM — Oracle HotSpot. Сейчас сидим на Java 7.

      Тюнили, попрошу коллег ответить как.

      Конфигурацию ноды узнать нельзя. NDA.

      Данные храним централизованно, в базе данных, PostgreSQL, в статье есть ссылка на мою предыдущую статью на эту тему. Лучше её почитать, чем тут дублировать.

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

      Друг Вася не может пригласить вас на конкетный канал. Но если вы состоите в группе, то вы будете прозрачно для игрока попадать с ним вместе на один и тот же канал. От шардов это отличается тем, что вы можете взаимодействовать с любым игроком, и взаимодействовать со всем миром через общие сервисы, типа форума, чата, гильдий, поиска приключений и т.п. Кроссерверные группы в ВоВ-е появились именно как попытка обойти ограничения шардов.
      • 0
        Спасибо, к сожалению пропустил ссылку на прошлую статью, как и саму статью, видимо. анал-раны в чате на 100к игроков конечно приобретают некую пикантность :)
      • +3
        Т.е. выцепить друга Васю и жестоко ему отомстить за прошлые прегрешения — не получится.
        • 0
          Ну можно позвать его в группу и там поговорить по душам :) Или ходить по приключениям и ждать… Земля круглая, рано или поздно встретитесь :)
        • +2
          Прощай free-pvp и ганк врага на каче!
    • +1
      А что именно интересует Вас в тюнинге JVM? Инструменты мы используем стандартные — jvisualvm, Java Mission Control (Flight Recorder жжёт). Плюс парсим логи gc, чтобы смотреть постфактум, что и как.
      Настройки тоже общеизвестные — хип, янген, CMS с выключенным IncrementalMode. Еще LargePages используем.
      • +1
        Я просто на JVM больших вещей не писал с момента смерти Sun, было интересно узнать, может быть что-то новое появилось. Спасибо за ответ!
  • +1
    когдато я сам участвовал в разработке эмулятора сервера world of warcraft
    вижу все те-же решения что и в wow
    1) guid-ы, всё имеет свой гуид, даже небо, даже камень
    2) сервера максимально раздроблены, логин сервер, аккаунт сервер, рилм сервер, чат сервер, ворлд сервер
    когда я изучал netty, написал себе некое подобие такого сервера, github.com/TERRANZ/universal
    для простоты я сделал фронтенд сервер, который занимается роутингом потоков между серверами и игроками
    спасибо за идею с модулями, сам бы я до такого не догадался!
    • 0
      У нас, кстати, GUID-ы почти не используются. Слишком длинные :) Адреса у нас умещаются в int-ы.
      • 0
        Интересно, как быстро у вас int переполнится при игре 100 тысяч человек и вызовет коллизии в id?
        • 0
          Мне тут коллеги подсказали, что я наврал и у нас не int а long. В любом случае в каждой игровой механике своё пространство адресов.
    • +1
      2) сервера максимально раздроблены, логин сервер, аккаунт сервер, рилм сервер, чат сервер, ворлд сервер

      Так и на официальных так выглядит тоже. Хорошо видно при наплывах народа.
  • +3
    Эх. Когда же уже научатся делать ММО с большим открытым миром, а не эту разбивку по локам и каналам… Ева не всчёт.
    Сколько у вас хотябы лимит намечается на 1 локацию? Например осады, если они там вообще будут.
    • +2
      Большой открытый мир наши дизайнеры не заказывали. Если мнение, что в нём играть не так уж и интересно. Люди любят играть, а не бегать от замка к замку. Но это лучше не со мной обсуждать :)

      Ева как раз в счёт. Ева показывает, что происходит если сделать большой мир. Когда начинаются эпичные сражения, тормоза неизбежны просто алгоритмически. Приходится придумывать замедление времени и другие некрасивые хаки.
      • +1
        И про каждое такое событие активно трубят везде где только можно даже люди не имеющие к игре отношения.
        Да и это риторический вопрос был. А вот на вопрос про ориентировочный лимит карты, я так понимаю, ответ дать не можете? Тот же сервер аион вполне нормально тянул осады с участием более 1000 человек и ивенты когда почти весь онлайн сервера был в одной локе, а вот клиент на СЕ1 уже не очень)
        • +1
          некоторые эмуляторы wow сервера тянули до трёх тыщ человек в одном мире
          • 0
            В мире, но не в одной точке.
        • 0
          У нас есть массовое ПВП, но я не знаю могу ли я уже публично называть цифру сколько там будет людей.

          Сами же отвечаете на свой вопрос… ) На сервере сделать можно при очень большом желании. Но клиент всё равно не отрисует, если на срежет графику в ноль. Будет медленно и не красиво. А у нас игра про то, чтобы всё бегало быстро и красиво, и боёвка очень динамичная, как в мортал комбате. Не думаю, что можно просто сделать мортал комбат на 1000 человек :)
        • +2
          Вот так, кстати, выглядит «пвп на 1000 человек».
          • +1
            Как игрок скажу, что так даже удобнее играть. Куча мельтешащих тел только мешают оценивать обстановку, а разноцветные надписи весьма наглядны и позволяют ориентироваться. А тут и все меточки видно, и кто куда бежит и тд и тп.
            И тормозит именно из-за движка не предназначенного для такой массовости + не каждый интернет канал выдержит такой поток данных.
            • +1
              Я тоже в Quake играю с r_picmip 5 :) Но не все этого любят.
              • +2
                Дело не в качестве графики, а в количестве информации.
                В небольших боях визуальное отображения игроков играет большую роль: видно кто кого атакует, по анимации понятно какое умение использует и что лучше предпринять и тд. В больших боях это всё лишнее и только мешает — достаточно видеть куда перемещается «зерг», метки на важных игроках и команды своего лидера.
                С квейком особо не знаком, но так понимаю что различия будут только в виде «вместо красивых воинов в сверкающих доспехах серые болванки». Что, всё-таки, совершенно из другой области чем пример на видео выше и своего рода способ получения игрового преимущества.

                Что-то совсем от темы ушли.
                • +2
                  количество информации тоже можно регулировать, в том же вов есть возможность в один пакет уложить до 64 пакетов и сжать zlib-ом
                  можно в одном пакете движения задать весь путь
                • 0
                  > Дело не в качестве графики, а в количестве информации.

                  Не вижу информации, вижу кучу мельтешащих разноцветных буковок.

                  Количество информации — это как-то так
      • –1
        Ну это только пока канал в интернет у среднего пользователя тормозной (в смысле задержек и пропускной способности).
        Как только появятся гигабитные каналы в каждом доме и пинг до серверов упадет до 1-2мс максимум, все будет Ок. К тому моменту и серверное железо подтянется, сможет все это переварить.
        Вопрос только когда это произойдет).
        • +1
          1-2 мс максимум? Мне кажется в ближайшие 5-10 лет это фантастика :)
          • +2
            Ну вообще-то, я об этом прямо и написал.
    • +4
      Минимальное время путешествия сигнала на другую сторону Земли 133 мс, и это не считая 33 мс лаг на отправку и обработку сигнала на клиенте и сервере = физическое ограничение 200 мс.
      Как только научатся запускать сервера в ядре Земли можно будет ожидать лаг «сети» 43 мс.
      • +1
        И кстати да, скорость распространения сигнала в современном оптоволокне 69% от скорости света.
        Для витой пары категории 7 или 7A это 80% от скорости света, хотя уже сейчас испытывают волокна с полой сердцевиной дающие 99.7% скорости света.
        • 0
          Ещё задержки на роутеры, сетевухи и т.п.
      • +1
        А причём тут лаги?
        • +2
          Лаги при том что есть дополнительная задержка на передачу пакета, обработку железом, серверный тик для обработки данных, клиентский фрейм на отрисовку 30 раз в секунду, задержка при синхронизации кадров (не у каждого стоит nVidia G-Sync).
          Про задержки при передаче можно почитать здесь quik.ru/depot/seminar2014/dbabur_2014.pdf
          • 0
            И какое это отношение имеет к отрытому миру игры без каналов и разбивки на локации?
            • +1
              Ну и опять возращаемся к локальным серверам.
  • +1
    На лекции много говорилось на тему «почему не JSON» и вдруг перешли на JSON :)
    • +2
      На лекции в Технопарке?
      JSON не используется в ресурсной системе. В JSON представлены некоторые данные, которые нужно хранить в базе. JSON занимает меньше места, что для базы важно. Каждому формату — своё место :)
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Ну есть же ссылка в этой статье :)

          habrahabr.ru/company/mailru/blog/182088/
          • НЛО прилетело и опубликовало эту надпись здесь
    • +4
      Долго ждал когда мне зададут этот вопрос.
      Отвечаю: я передумал :)
      • +1
        Почему?:)
        • 0
          Надоело писать конвертеры для бинарных сериализаторов. При простых изменениях JSON может прожить без конвертеров.

          Оказалось, что хранить в базе большой JSON это плохо, но не так уж и плохо как я ожидал. Так что решили попробовать пока так.
          • +1
            А как же скорость сериализации/десериализации? А как же объем данных. Выигрыш в несколько байт на одном сообщении, может значительно сократить затраты при объемах в миллионы подобных сообщений.
            • 0
              Каких сообщений? В JSON мы преобразуем некоторые данные прямо перед сохранением данных в базу. И десериализуем сразу после того как их оттуда загрузили.

              Или вы о чём-то другом говорите? Нигде больше JSON-а у нас нет.
  • +4
    Мне кажется, вы немного лукавите. Фактически, ваш мир не один большой на 100 тысяч человек, а на самом деле разделён на локации, которые к тому же лимитированы по количеству игроков, и для каждых n игроков создаётся «копия» этой локации, и игроки из одной копии не видят и не слышат друг друга. Верно я понимаю?

    Мне интересно, а не думали вы в Мэйл.Ру о создании по-настоящему бесшовного мира? Без любого разделения, то есть все игроки в одном месте видят друг друга и взаимодействют друг с другом, а резкого перехода между нодами нет? На сколько я знаю, ни в одной трёхмерной и популярной MMORPG так ещё не делали из-за высокой сложности.

    Отрадно, что вы используете Java. В ваших лекциях есть интересная и полезная инфа лично для меня. Люблю всегда говорить, что Java — отличная штука для серверов, «вот смотрите, сервер аллодов на Java!»
    • 0
      Один большой бесшовный мир это слабоиграбельно. Достаточно посмотреть на центральные города в том же WOW — пестрит в глазах и никакой видяхи не хватит чтобы всё это отрисовывать.
      • +1
        Ну с этим сложно поспорить, но это всё-таки проблема гейм-дизайнера, а мне интересны мысли программиста на эту тему.
        • +1
          тут всё будет упираться в раму и процессоры
          посмотри хотябы на майнкрафт, вот тебе безшовный мир
          • +1
            Я же не говорю о том, чтобы поднимать сервер на 100 тысяч человек на одной машине — это абсурд.
            • +6
              Со стороны программиста здесь O(n^2), масштабирование на 1000 машин и больше вызовет рост нагрузки на синхронизацию, поэтому линейный прирост только за счет железа не получится достигнуть. Ваш КО.
              • +1
                Я и не говорю о простом распараллеливании на 1000 машин. Такая идея должна быть глубоко заложена в архитектуре.

                Ну если например опустить необходимость поддержки 100 тысяч игроков в одном месте, что совершенно не оправданно с точки зрения гейм-дизайна, то мне кажется очень решаемой идея с плавными переходами между локациями.
                • +2
                  А кто запретит плавно собраться всем в 1 локации? :)
                  • +1
                    Никто, но это лишь одна часть проблемы же :)

                    Я же не говорю о том, что вот почему вы не запилили совсем бесшовный мир, ваше решение не крутое и так далее. Мне с профессиональной точки зрения интересны разные мысли по этому поводу, так как я себе ставлю в будущем цель придумать что-то подобное, чтобы без швов и без лагов на 100 тысяч человек. Пока что идей не очень много :)
                    • +2
                      Очень много зависит от того что хочется получить на выходе, возможно введение локальных ограничений и использование особенностей мира позволит частично решить проблему, например:

                      Больше 3 не собираться — рассчитываем пересечение между объектами, не даем объектам пересекаться. в результате чего 100000 человек не смогут собраться в 1 точке, а будут более менее равномерно распределены по «поверхности»
                      Минусы: затраты на расчет коллизий

                      Моя хата с краю — добавляем радиус обзора, заранее будет известно максимальное количество объектов в зоне видения, зону обзора можно поделить на видимую и эффективную (то что влияет на персонажа). Для FPV это может быть 180 градусов обзора, в 2D это может быть видение по лучу.
                      Минусы: нужно хранить угол обзора

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

                      Есть еще мысли, но в любом случае получаем кучу краевых эффектов.
                      • +1
                        Спасибо, полезная информация чтобы задуматься :)
                  • +2
                    виртуальный омон не позволит собираться компаниями больше 3-х орков!
                    • 0
                      плюс, границы, визы, платная парковка, фейс контроль. В реальности нужно смотреть почему и как где-то собираются миллионы, а где-то не более определенного количества.
    • 0
      Мир на 100к, но играть вместе нельзя. В танчиках ведь нет шардов. Все могут играть со всеми.

      Бесшовный мир у нас был в Аллодах на самом деле. Одна карта была сразу на нескольких серверах… Оказалось технологически сложно и игрокам мало полезно.

      В любом случае это вопрос в первую очередь гейм дизайна. Они считают, что открытый мир не нужен, по крайней мере в нашей игре.
      • +1
        • +2
          «Бесшовный мир у нас был в Аллодах на самом деле»

          Вас интересует как оно было реализовано?
          Просто на границах карты существовали области, при попадании в которые игрок начинал реплицировать на другой сервер.
          • 0
            Да, это весьма логичное решение, к такому я уже приходила, спасибо. :)

            А вот решения, что делать если все внезапно соберутся в одном месте, у меня до сих пор нет…
            • +3
              Такого решения ни у кого нет :)

              Как избавится от этого никто не знает.

              for(Avatar av1: allAvatars)  {
                 for(Avatar av2 : allAvatars)  {
                   replicate(av1, av2);
                 }
              }
              
              • 0
                Решения нет, а проблема есть. У нас, например, изменяемый мир, который является большой и важной частью геймлея, и игроки в основном взаимодействуют через него. Я не могу просто разделить игроков на два мира, он у них должен быть общий. Хотя если задуматься, то наверное могу, а синхронизировать не самих игроков и мир, а только мир…
                • 0
                  Ага. Идёшь ты такой по дороге и вдруг… БАЦ Вокруг тебя дом построили.
              • +1
                P2P? Объединять игроков по территориальному признаку и, если станет совсем плохо, начать отсылать полную инфу 30% игроков, а остальные должны её получить через вторые руки. Через третьи, если совсем смерть. Позднее запросить логи у второй категории и устроить выборочную проверку на вшивость. Для PVP-сражений не годится (см. Demigod), но, хотя бы уж, поможет с провисаниями в Лимбе, когда неписей нет, игроков нет и, скорее всего, тебя сейчас выкинет в главное меню.
                • +1
                  Не взлетит.
                  — программировать сложно
                  — коннектить игроков p2p через фаерволы, модемы и т.п.?
                  — p2p не для реалтайм распространения инфы. пинги будут весьма большими.
                  — можно читить, фильтруя траффик который посылаешь другим.
                  — придётся засветить ипшники игроков друг другу.
                  • 0
                    Ну, слово «реалтайм» и так мало подходит к
                    неписей нет, игроков нет и, скорее всего, тебя сейчас выкинет в главное меню.
                    , читеров, по логам, можно наказать постфактум, а запретить светить свой IP можно одной галочкой в настройках. Проблем, скорее всего, возникнет сильно больше, но конкретно из этого списка совсем нерешаемой со стороны сервера можно назвать только вторую.
                    • +1
                      — Не очень понял про наличие неписей и т.п., но в любом случае добавление новых этапов пересылки обновлений увеличивает пинг и ухудшает боеёвку.

                      — Не понял как искать читеров по логам. Я с читерским клиентом могу просто «тормозить» и передавать пакеты другим пирам с большой задержкой. Или не передавать вовсе. Или рвать соединения. Формально это не читинг.

                      — Если запретить «светить ип», то как пользователи будут друг с другом коннектится?
                      • –1
                        — Я предлагаю такую штуку использовать только в горячих точках, типа тех же столиц фракций. Если выбирать между «с лагами, но дойти до квестового непися и суметь сдать квест» или «пойти гулять на улицу и социально развиваться с другими детьми, пока не перестанет твориться дикий ад», то предпочитетельнее первое. А если смотреть не только на вашу игру, а глобальнее, то и даже для боевки типа Евы Онлайн задержки могут особо ни на что не влиять. Главное — оставаться онлайн и быть в курсе дел.

                        — Клиент посылает серверу сообщение «Чувак, я могу передавать вместо тебя сообщения игрокам с ID 124 2352 и 23542. Средняя задержка — 10 миллисекунд, связь устойчивая». Сервер передает этим игрокам сообщение, что они могут подключаться к серверу-добровольцу и слушать его. Если оказалось, что после этого они десять секунд втыкают в пустой экран, то стучимся обратно, обманщик записывается в серый список. В серый, а не черный, потому что еще неизвестно, кто врет. Если читер отсылал что-то, да не то, то это тоже можно проверить.

                        — Конкретно параноики коннектиться ни к кому и не будут. Вопрос комфорта. Если дорожишь анонимностью, то просто не ходи по горячим точкам, пока локацию штормит.
              • 0
                Как Вам такое решение квадратичной сложности.
                Делаем понятие агента игрока. Он крутится на специальном сервере агентов, один такой сервер хостит несколько агентов. Клиент коннектится по TCP/IP непосредственно к агенту, а не к локации. Агент – это серверная часть, потому доверенная. Когда агент заходит на локацию, то он передает локации сообщение, что он пришел. Локации регистрирует его, и передает всем зарегистрированным агентам идентификатор новичка, а новичку, список всех агентов на локации. Аналогично при уходе агента с локации.
                Когда аватар выполняет действие, анимацию какую-нибудь, перемещается, то посылает сообщение своему клиенту и всем другим агентам в списке, чтобы они послали информацию уже своим клиентам. Таким образом для сервера, где хостится агент — это линейная задача, не квадратичная, а количество серверов можно масштабировать.
                Многие проверки может делать сам агент. Например, проверить, чтобы аватар не прошел сквозь стену, для этого агенту нужно просто иметь локальную копию карты.
                Чтобы игроки не проходили друг через друга они могут послать запрос на перемещение серверу, где хостится локация, и только получив ответ начать перемещение. Локацией кстати может быть достаточно маленькая площадь бесшовного мира или большой карты, тогда агент игрока бы работала бы с данной и соседними локациями. Причем можно сделать чтобы в локацию физически не вошло бы много игроков. В зависимости от карты агенту бы приходилось работать со списком из например 9 локаций на равнине, или 3 локаций в прямом коридоре. В том смысле, что списку игроков из этих локаций ему нужно было бы передавать свое состояние. Размер локации – это расстояние выстрела/видимости на клиенте. Ну и сообщение должно формироваться не таким циклом:
                for(Avatar av1: allAvatars) { for(Avatar av2 : allAvatars) { replicate(av1, av2); } }
                А сначала формируем тело сообщение, а потом сформированный набор байт рассылаем всем агентам в списке. Тоже, экономия процессорной мощности.
                Ну и само собой, сервера локаций тоже могут хостится на разных железных серверах.
                Вот Вам и бесшовный мир, и зависимость, которая хоть и немного выше линейной, но точно не квадратичная. По край не мере позволит собрать на одной карте множество игроков.
                — Подвожу краткий итог предложения:
                — Рассылку сообщений игрокам можно распараллелить и поручить другим серверам, для которых это будет линейной задачей. Конечно в общем задача в этом случае будет квадратичной, но выполнится быстро.
                — Рядом с игроком не должно физически помещаться слишком много персонажей. Это естественным образом уменьшит квадратичную сложность задачи.
                • 0
                  Отвечу в обратном порядке :) Андрей, если что, поправит.
                  Рядом с игроком не должно физически помещаться слишком много персонажей. Это естественным образом уменьшит квадратичную сложность задачи.

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

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

                  То, что вы называете агентом — это и есть аватар. Аватар — это «проявление клиента на сервере» :) Аватару, и его данным, мы доверяем. Мы не доверяем данным, которые приходят с клиента. Эти данные сперва проверяются и только потом применяются на аватаре. С последующей отправкой изменений на клиент.
                  Один сервер игровой механики содержит в себе сразу несколько потоков, способных обслуживать достаточно большое число аватаров, расположенных на разных картах.

                  И да, то, что скрывается за функцией «replicate» — это «сформированный набор байт», высылаемый каждому аватару и содержащий данные как о собственном аватаре игрока, так и данные об аватарах его окружающих.
                  • 0
                    Ок. Я вероятно слишком уделил внимание мелочам в моем сообщении. Основная мысль сообщения: Можно сделать так, чтобы в одном городе было множество игроков, и при этом не тормозило бы на сервере.
                    Я как понял у вас аватар — это просто класс, который хранит данные персонажа. Его сериализуют и передают с одной локации на другую. А сам клиент коннектится к локации (игровой механики, которая соответствует одной локации). Поэтому на один неделимый сервер возникает большая нагрузка. Если же коннект производить к самому аватару, а аватары будут работать на отдельном от игровой механики сервере, то нагрузка будет для данного сервера линейна.
                    • 0
                      Кроме того. По поводу передачи данных. Задача то какая. У нас есть N клиентов, и каждому из них надо передать абсолютно одинаковый набор байт, т.е. информацию об N аватаров. Можно организовать что-то типа udp broadcast. Ну, например, есть игровая механика, к игровой механики присоединено 100 серверов рассылки сообщений. К каждому серверу рассылки сообщений присоединено по 1000 игроков. Всего 100 тысяч игроков на локации. Серверу механики нужно собрать информацию о 100 тысячах аватаров в один пакет, послать его 100 серверам рассылки сообщений, и каждый из этих серверов пошлет еще 1000 клиентам этот пакет.
                      То есть какая работа проделывается: сначала линейная работа по сбору данных в один пакет. Потом линейная задача по отсылки этого пакета каждому клиенту. Количество трафика – квадратично и распределено. Нагрузка на каждый отдельный железный сервер линейна почти.
                      А если разместить дата центры в разных частях мира, чтобы клиенты коннектились к ближайшим серверам рассылки сообщений, то вообще можно еще и с пингом порядочно бороться.
                      • 0
                        В общем, от каналов отказываться не целесообразно с экономической точки зрения. Но с таким подходом можно повысить количество человек в определенной локации, например, разрешить нескольким тысячам человек находиться на эпичном поле боя.
                  • 0
                    Под уменьшением квадратичной сложности я имею в виду следующее.
                    Разбиваем карту на клеточки. Допустим, в каждой клеточке может вместиться максимум 10 персонажей. Каждый персонаж может взаимодействовать только в пределах своей клеточки и соседних. То есть 9 клеточек на плоскости. Итого в самом сложном случае каждый персонаж будет взаимодействовать максимум с 90 персонажами.
                    Итого сложность алгоритма не N*N, а X*N, где X – максимальное число персонажей, которое может находиться рядом с игроком, а N – число персонажей, обрабатываемых на сервере. Т.е. с каждым новым игроком нагрузка вырастает линейно, на величину X
                    • 0
                      Еще иначе можно сказать так. Отображаем игроку только самых близко находящихся X персонажей. Если рядом с ним уже толпа из сотни человек, то он не заметит, что где-то там кого-то не видно (можно еще туман усилить). А если дополнительно игровым дизайном ограничить нахождение такого числа человек рядом с ним, то можно спокойно делать бесшовный мир с огромным количеством игроков.
                    • 0
                      вы сами себе противоречите. То вы говорите — «Размер локации – это расстояние выстрела/видимости на клиенте», а тут — «в каждой клеточке может вместиться максимум 10 персонажей». Вы же про одно и то же сказали в этих фразах. Так не пойдёт.
                      • 0
                        Признаю, что выражаю мысли не совсем корректно. Поясняю.
                        Разбивать на мини локации нужно, чтобы
                        1. Облегчить расчет коллизий на сервере между динамическими объектами.
                        2. Чтобы определить набор ближайших персонажей, и передавать только первую сотню (или другое количество) из них, а других скрывать в тумане, или отображать упрощенно еще одну-две сотни.
                        Если коллизия, то клеточки должны быть маленькими. Так как все равно нельзя столкнуться с тем, кто в 5 метрах и больше. То есть если столпилось несколько тысяч человек в одном месте, столкновение на самом деле проверяется только с теми, кто в этой же и соседней клеточке, а это не так много персонажей.
                        Видимость и дальность выстрела зависит от механики игры. Я сказал «допустим» 10 человек (это выстрел на три шага получается). Если механика предполагает дальнобойные выстрелы, и персонаж видит далеко, мы можем по спирали анализировать ближайшие клеточки, уже не 9 штук, а больше, пока не получим сотню персонажей, или пока не достигнем предела дальности видимости. В нормальной ситуации, когда персонажей не много, игрок будет видеть далеко, но если нагрузка увеличивается, то появится туман, который сделает часть юнитов более размытыми, а часть вообще невидимыми.
                        Опять же, туман может появляться, только когда сервер не справляется с нагрузкой, если справляется, то может передавать всех без ограничения.
                        • 0
                          вопрос на миллион — что чаще происходит — проверка на коллизии, или показ соседей? как бы ваша оптимизация не превратила игрока в
                          ёжика в тумане
                          , который видит пару десятков игроков вокруг. Вы предлагаете одну квадратичную сложность заменить другой.
                          Если цель — обработать десятки тысяч игроков на одной площади, то ваш подход может быть рассмотрен, но использовать тот же подход в обычном режиме с несколькими десятками/сотнями игроков рядом — слишком сложный оверхед.
                          • 0
                            Туман как раз ограничивает по количеству человек, так что он будет ёжиком, который видит не пару десятков, а максимальное число игроков заданное по производительности. Для Skyforge такой максимум 250 игроков. Просто если игроки решат устроить флешмоб, и собраться в одном месте, то будет туман, и игрок будет видеть только ближайшие 250 игроков.
                            По поводу коллизий и показа соседям. Вопрос не однозначный с точки зрения сервера. Показ соседям – это может быть только изменение состояния: сообщение что персонаж начал двигаться из пункта А в пунтк Б. Начал анимацию удара и т.д. Коллизия происходит при расчете движения. В любой момент два движущихся тела могут столкнуться. Все зависит и от механики игры и от способа реализации. Я не вижу однозначного ответа на данный вопрос.
                            Данная оптимизация полезна не только для густонаселенных карт. Она уменьшит трафик и просто на больших картах, так как персонажи за пределами области видимости клиента не будут ему передаваться.
                            • 0
                              Кроме сервера есть еще и клиент, которому нужно обработать и отрисовать присланные изменения. Когда в одном кадре находится толпа аватаров, нагрузка на клиент резко возрастает.
                              И да, на клиент изменения реплицируются не для всех сущностей, а только для тех, которые попадают в радиус репликации (чем-то похоже на ваш «туман»), зависящий от текущей нагрузки на сервере.
                              Необходимость разбивать мир на отдельные карты вызвана так же требованиями по памяти в клиенте, чтобы бегать по миру плавно, нужно держать мир «наготове». Вспомните, даже в одиночных играх локации всегда разделены.
    • 0
      >На сколько я знаю, ни в одной трёхмерной и популярной MMORPG так ещё не делали из-за высокой сложности.
      EVE online… правда оно про кораблики
      и… если SecondLife можно считать трехмерной и популярной MMORPG… то там спокойно можно по миру гулять/летать… (про то как это на практике выглядит там… вопрос другой)

      • 0
        > EVE online… правда оно про кораблики

        Таки не совсем бесшовный, поделён на отдельные солнечные системы, а внутри каждой системы есть отдельные зоны видимости (гриды), которые динамически расширяются, но объединяться не могут. В итоге корабли могут быть друг от друга в 1 км, но друг друга не видеть, ибо в разных гридах находятся. Чуток прополз — увидел того кто за гридом прятался, но перестал видеть тех кто в твоём бывшем гриде остался.
  • 0
    Интересно. А чат будет общий для всех игроков мира? Или опять же в рамках канала?

    P.S. Пригласите на закрытую бету? :)
    • 0
      Чаты вроде как разные есть… Для зоны, для гильды, для группы, по интересам и т.п. Есть ли чат «для всех вообще» я не помню.
      • +1
        Хорошо, а чат для зоны работает в рамках одного канала? Или всех каналов в данной зоне? То есть можно ли искать игрока для совместного прохождения по всем каналам?
        • 0
          Большая часть приключений идёт через кнопку «хочу побегать вооон там вот». Тебе подберут напрника автомагически :)

          Чат в рамках канала, если я ничего не путаю.
    • 0
      Ссылка на запись на бету есть в статье :)
  • +1
    «Клиент в руках врага»
    А как у вас дела обстоят с умениями невидимости? Все много про это пишут, а на деле почти всегда оказывается, что на клиент просто отправляется пометка, что игрок невидим и игра определёнными классами сводится на нет различными «радарами» и недоработками клиента.
    • +1
      Я не помню есть ли у кого-то эффект невидимости, эффекты и классы я не пишу. Но на вскидку выглядит так, что временно перестать реплицировать игрока другим клиентам довольно просто. Так что если невидимость у нас есть, то она честная.
    • +1
      > а на деле почти всегда оказывается, что на клиент просто отправляется пометка, что игрок невидим и игра определёнными классами сводится на нет различными «радарами» и недоработками клиента.

      В еве ничо не отправляется, если не видишь, значит не видишь, пока мимо бобра в 2 километрах какая-нибудь гадость не пролетит и не вышибет из невидимости, или он сам не напорется при маневрировании на какой-нибудь болтающийся в космосе труп, который у него в овервью не отображается.
  • +1
    У вас на скрине довольно много кода на JavaScript. Расскажите, пожалуйста, зачем используете? Я просто тоже использую JavaScript-скриптики на своём сервере, интересно послушать об опыте других. Если вызываете их из сервера, то используете ли вы стандартный JS-движок (Rhino) или какой-то другой, если другой, то почему?
    • +1
      JavaScript используется для внутриигрового портала и для администраторских тулзов с вёб интерфейсом.
      • +1
        А внтуриигровой портал и веб-интерфейс администрирования тоже являются частью сервера?
        • 0
          Ну в некотором смысле да. Только про них я тут ничего не писал :)
  • 0
    Спасибо за статью. А теперь пара вопросов :)

    1) Функционал сущности хранится в HashMap, а чем гарантируется то, что у сущности например есть функционал «здоровье» при «ударе»? Упомянутая Ресурсная система?

    2) Стоим мы с другом и бьем вместе моба, при моем ударе происходит «запрос на удар» ==>… серверная часть… ==> ответ с сервера. В этом ответе будет обновленное состояние моба? Т.е. я узнаю о ударе через ответ на запрос, а друг — через репликацию окружающего мира? Если да, то не может ли быть проблем, допустим, при пвп с большим кол-вом человек?
    • 0
      1) Хороший вопрос. Это как раз основная проблема этого подхода. Функциональности может не быть. Поэтому если функциональности нет, то это надо как-то обрабатывать. Кидать эксепшн и удалять аватара или репортить в лог.

      2) В ответ на запрос вы узнаете что удар прошёл и нанесено столько-то то дамага. Изменение полоски с хэлсом над мобом обновляется через реплику и у вас и у друга.

      Проблемы возникают не при большом кол-ве человек, а при высоком пинге. Тогда ответы от сервера могут прилетать через 1-2 секунды после удара, и игрок не будет чувствовать от боя фидбека. Задержка в обновлении хэлса, это мелочь по большому счёту.
      • +1
        > Поэтому если функциональности нет, то это надо как-то обрабатывать. Кидать эксепшн и удалять аватара

        Стукнул случайно по камушку вместо моба — получай дисконнект?
        • 0
          Интерфейс не даст ударить по камушку. Но вот если сломал клиент и всё-таки ударил… То дисконнект.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Не, это не миксины. Это запчасти в очень широком смысле. «Ноги», «Поверхность», «Таланты», «Периодическая чекалка чего-то там» и т.п.
  • 0
    — опять не туда написал.
  • +2
    Подскажите, а у вас присутствует какая-либо система приоритетов для каналов?
    Например, друзья, могут оказаться на разных каналах (не будучи объединены в группу).
    • 0
      Мы стараемся игроков из одной гильдии или из одной группы сливать в один канал. Такой эверистики для просто друзей нет :)
  • +1
    У меня вопрос по сетевому протоколу. Я правильно понимаю, что сервер игровой механики слушает какой-то TCP-порт, клиент инициирует соединение и дальше они с сервером обмениваются сообщениями, представляющими из себя наборы «ключ-значение»? что-то вроде JSON/BSON?
    • +3
      Да, сервер игровой механики открывает ряд портов, к одному из которых клиент устанавливает соединение. Но сами сообщения — это отнюдь не пары «ключ-значения». Это могут быть как сообщения об изменении определенных параметров системы («обновление реплики»), так и просто игровые события, не содержащие никаких данных. Сам же протокол — свой.
      • +1
        Кстати, я правильно понимаю, что в таких случаях все пишут свой протокол с нуля, с всеми этими реконнектами-таймаутами-ретрансмитами-регистрациями-форвадами и так далее (когда 10% — это сам протокол и 90% — это обработка исключительных ситуаций)? Никто не пробует применять какие-нибудь очереди (ну там 0mq/ampq), или это просто неприменимо/неэффективно?
        • +2
          Свой сетевой слой удобнее, потому что его можно подхачивать на всех уровнях и заставлять работать именно так, как надо нам. На большом проекте проще написать своё, чем заставить 0mq выполнять то, что хочется с максимальным перфомансом. Можно впилить шифрование, компрессию, поиграть с объемом генерируемого мусора и т.п.
        • +2
          В качестве транспорта мы используем TCP, хотя это холиварная тема, т.к. кто-то предпочитает UDP. Свой же протокол — это верхние уровни. Хотя меня не покидает ощущения, что я не понял Ваш вопрос :)
          Насчёт того, почему не используют готовые решения — у меня ответа нет. Когда я пришел на проект, протокол уже был. Может быть кто-то из коллег ворвётся в тредик и даст ответ. Но скорей всего, будет тоже самое, что и про akka :)
          • +2
            Не так сложно сделать свой протокол уровня приложения, по сравнению с получаемым от него профитом. ИМХО, гораздо важнее чтобы каждый переданный байт был действительно необходим, чем сэкономить десяток часов программирования. В конце концов, каналы не резиновые, производительность тоже, а задача не сложная.
            • +1
              Это — продолжение комментария ниже )

              Понятно, что свой протокол — задача, совершенно понятная в реализации; аккуратность и предусмотрительность наше все; но, к примеру, при экономии байтов мы приходим к необходимости версионности/расширяемости протокола — так не стоит ли тут посмотреть на протобуф, реализация которого есть под все? аналогично — и про другие стороны реализации.
              • +2
                Стоит отметить, что есть общение не только клиент-сервер, но и сервер-сервер. И если клиент-серверный протокол мы революционно трогать в ближайшее время не планируем, то в сторону protobuf для сервер-серверного взаимодействия мы смотрим. Потому что появилось требование обратной совместимости при обновлениях для отдельных частей релма.
                • 0
                  Спасибо, любопытно. А какой-нибудь серверный pub/sub/broadcast будете делать сами (ну наверняка же подобные вещи нужны)?
                • 0
                  Не вводи народ в заблуждение :) protobuf у нас если и будет то сбоку, для взаимодействия сторонних сервисов (пусть даже и нашей же разработки) с рилмом.
          • +1
            Да нет, это я плохо выразился :)

            Смотрите, есть классическая задача: есть сервер (кластер), есть клиенты, есть IP-сеть, есть возможные NAT'ы и прочие странности, надо искать друг друга, при этом нужно общаться надежно (таймауты-ретрансмиты, надежность доставки), и до какого-то предела делать это условно «рилтаймово»; возможно, нужно обеспечить pub/sub, нужно минимизировать трафик, нужна поддержка структур данных — при том, что на каждом конце протокола или оператор switch/case, или FSM…

            Я не могу найти — или для этого действительно нет готовых низкоуровневых фреймворков, готовых взять вместо меня решение многих задач? Тот же google protobuf хорошо упаковывает данные, но не волнуется о надежности доставки; тот же zmq ничего не знает про encryption и так далее.

            Есть ощущение, что каждая новая игра, каждый новый условный клиент условного сервера раз за разом решают одни и те же задачи заново? Ну это же если бы как каждый сам писал для своего http роутинг и поддержку сессий.
            • 0
              В ZMQ можно использовать вот — curvezmq.org/
            • 0
              (уточнение) Говоря про «таймауты-ретрансмиты», я говорю о том, что и TCP-соединения рвутся, и UDP (если выбрали) нужно окружать заботой. И конкретная реализация этих вещей не имеет почти никакого отношения к логике игры, это «общие вопросы».
              • +1
                Да, такие вещи приходится писать свои. Мне кажется, что даже вокруг «коробочного» решения всё-равно придется сделать гирлянду из логов и «ифов». Потому что «коробочное» решение является таким, как правило, только на старте. При росте сложности системы и повышению требований время, затрачиваемое на модификацию «коробочного решения», начинает резко расти.
            • +2
              Тот же google protobuf хорошо упаковывает данные, но не волнуется о надежности доставки

              Правильно, ибо это не его ума дело. Он ничего не знает о транспорте через который работает и соответственно не может ничего гарантировать.
              Не надо смешивать все в одну кучу, мухи отдельно — котлеты отдельно.
    • +1
      Сообщение это id класса + поля класса сложенные в байтовый поток.

      Допустим есть класс
      class A  {
       byte x=3;
       short y=5;
      }
      
      
      
      тогда в мессажка будет выглядеть как #1F030005
      где 1F это id классаб 03 — это значение X, 0005 это значение Y

  • +1
    Добрый день, Randll! Вопрос не по самой игре, а по коду. У вас встречаются аннотации NotNull. Я так понял, это аннотации IDEA, которые на этапе компиляции говорят, что тут не может быть null? Если да, то я такие штуки использовал только в академических целях. А как они на большом проекте (почти 2 миллиона строк на Java, как вы сказали)? Во всём коде пишутся эти аннотации? Не слишком ли напрягает это аннотирование и вообще стоит ли оно того?
    • +6
      Аннотации @Nullable/NotNull это аннотации IDEA. Это лучшие аннотации в мире. Они спасли от невероятно количества багла. Писать их совсем не сложно, alt+enter и идея сама их дописывает. Однозначно оно того стоит. Даже в маленьких проектах.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Всё равно чекаем и кидаем эксепшн и это правильно. Сегодня мы точно знаем, что будет NotNull, но завтра кто-то что-то где-то отрефакторит и привет NullPointerException.
  • +2
    Спасибо за статью, очень интересно. Есть вопрос о том, что происходит на клиенте, в то время как сервер обрабатывает сообщение об убийстве собачки. Отображает ли клиент полет фаербола или удар до того, как он подтвержден и обработан сервером?
    • +2
      На клиенте есть некоторое магическое читерство и предсказание, в результате которого мы стараемся совместить анимацию удара и отлетающий урон. Если совсем грубо говоря, то клиент в большинстве случаев ждёт подтверждения от сервера и только потом рисует анимацию. Это может быть подтверждение того, что фаербол полетел, а не того, что он долетел до цели. Иногда он начинает рисовать анимацию до ответа чтобы бой выглядел более динамичным. Но если сервер ответит, что удар не прошёл, анимация некрасиво прервётся :)
      • +2
        а код предсказания на клиенте и код обработки на сервере очень похожи или на клиенте только необходимый минимум без проверок и тд? хорошая ли идея выносить этот код в коммон модуль, который клиент и сервер могут шарить между собой?
        • 0
          Код разный. Кроме того, клиент на c++, сервер на Java. Кодобазу шарить проблематично :)
          • +1
            Так игра чисто клиентская? И никаких прогнозов про запуск под wine?
            • 0
              Чисто клиентская. Под wine врядли что-то будет.
              • 0
                Вот значит с платформами значит определились, а то в информации по ЗБТ это даже не упоминается.
                • 0
                  ---
                • 0
                  Угу. Я даже ломанулся регистрироваться. Но даже сообщение, которое приходит уже после регистрации («наше XYZ на вашей платформе не поддерживается» — хотя кто мешал сразу указать?) и то понятней, чем полная неопределённость. Хорошо хоть тут спросить догадался :)
              • –4
                Очередная анальная система защиты? А зачем, если у вас такой хороший сервер, который клиенту ни в чём не доверяет? Не, сами в такое играйте.
  • +2
    Кстати, у нас тут готовится статья про разработку игровой механики. Не про игровой дизайн, а про архитектуру и технические приемы. Если есть какие-то вопросы, то их можно задать в комментариях, а я их передам будущему автору.
    • 0
      Обещанного 3 года ждут? ))
      Будет статья-то в итоге?
  • +1
    А что если убрать ракурс с игрока и переместить его на мир. Ну т.е. не сокруг игрока обрабатывать радиус (при N игроках требуется N*N обработок и пересылок данных), а поделить мир на радиусы и обрабатывать их? тогда единожды обработанный радуис рассылается всем N игрокам. И проблема с N числом игроков в одной точке переходит от квадратичной к линейной.

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

    И еще вопрос, в вашем мире мобы, стулья, вода и все прочее (с чем игрок может взаимодействовать и что может так же взаимодействовать с игроком) входит в тот лимит канала о котором вы упомянали?
    Т.е. если например на карте очень много интерективных объектов, снижается ли тот лимит игроков которые могут одновременно находится на этой карте/канале?
    • 0
      «И проблема с N числом игроков в одной точке переходит от квадратичной к линейной.»
      Не переходит. Каждому игроку надо переслать сколько-то информации. И количество этой инфы пропорционально количеству аватаров, которые игрок видит. Количество инфы в вашем радиусе увеличивается если аватаров становится больше.

      Мобы и объекты на карте в лимит не входят. У нас просто есть квота для дизайнеров, сколько мобов они могут расставить на карте.
  • +3
    Статья супер. Спасибо огромное.

    1) Не задумывались ли об использовании google protobuf при сериализации Java / C++? Что мотивировало написать свое решение?
    2) Вопрос по «Занятная статистика». На Javascript написано немного-немало 70к строк кода. Для чего используется Javascript?
    3) Интересно как у вас устроена система обновлений сервера. Например, найдена серьезная ошибка в сервисе игровой механики или аккаунт-сервисе. Будет stop the world всего сервера (всех JVM)?
    • +1
      1) Своё решение лучше, потому что туда удобно впиливать конкретно наши фишечки. Кроме того, protobuf требует создания описателя в отдельном файле, мы же генерим код для сериализации прямо по Java файлам. Нам так удобнее. Возможно мы будем использовать protobuf для общения с внешними сервисами.

      2) Отвечал уже :) У нас внутриигровой фейсбучек и вёб админка.

      3) Да. Тут про Аллоды forum.allods.ru/showthread.php?t=91938, у нас примерно так же.
    • 0
      3) It depends. В общем случае, да. Мы, конечно, стремимся минимизировать время простоя. Сервера должны уметь переживать смерть и перезапуск друг друга. Но для надежности и спокойствия лучше уйти на профилактику. Пусть и 10 минутную.
      Какие-то фиксы (по слухам) на Java можно вообще HotSwap'ом накатывать :) Но, искренне надеюсь, до этого не дойдет.
  • +1
    Если я правильно понял, то с одной карты можно пешком дойти до другой. Если так, то как вы реплицируете игроков стоящих на границах карт друг другу, в случае если они рядом, но технически уже на разных картах.
    • +1
      Неправильно поняли. Переходы через телепорты.

      В Аллодах реплицировали. Как? Ну точно также как и на клиент :) на удалённом сервере создаётся прокси аватар. В код основного аватара в сеттеры полей встраивается отсылаение реплики на к проксе.

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

Самое читаемое Разработка