MMO на WebRTC

в 13:53, , рубрики: node.js

image

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

image

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

Не беритесь за создание MMORPG

Как известно, в каждой шутке есть доля шутки — если у вас нет достаточно ресурсов поддерживать game-dev комманду или вы работаете над игрой один, лучше пересмотреть свои цели. Когда я начинал делать сетевой шутер Auxilium (КПДВ из него), мне было понятно, что придется как-то решать проблему с масштабированием и очевидным решением, которое используется во многих сетевых играх было введение игровых сессий или «боев» — в этом случае можно очень просто распределять эти бои по физическим машинам и держать нагрузку, но это уже была не классическая MMORPG с открытым миром. Далее последовали три ошибки, после которых разработка игры остановилась:

Первой ошибкой было полагать, что хорошие сервера можно найти за вменяемую (для меня) сумму. Начальные замеры производительности игры на Node.JS показали, что один процесс вытягивает игру человек на 20-25. После этого начинается проседание eventloop'а и неустранимые лаги из за задержки расчета физики на сервере. Так как за 350 р. в месяц я нашел VPS с 250 Mb памяти, то больше двух процессов с игрой было не запустить (при старте процесс занимал ~100 Mb, да, можно было закрутить гайки и ужаться). И того 50 игроков. Не очень то уж и Massive получалось.

Второй ошибкой было разрабатывать игру не одному, а с командой (девять женщин могут родить одного ребенка за месяц, так?). Есть хорошая статья про то как сделать MMORPG — «Мы начинали втроем, но уже к концу первого года разработки я остался один». Так и получилось.

Ну и третьей ошибкой было полагать, что с TCP можно мириться. Мы взяли за основу WebSockets и добавили в него бинарное сжатие JSON в надежде, что этого хватит. Как знают многие, TCP — это протокол с гарантированной доставкой пакетов в том порядке, в котором они были отправлены, по этому если один пакет потеряется, то для игрока игра замрет на время, пока этот пакет не переотправят. Хуже всего, что после этого прокрутятся все пакеты, которые стояли в очереди за ним — опять же можно предусмотреть сохранение задержек (добавлять их искусственно), но добавление ювелирных костылей ради костылей (работа с TCP вместо UPD) верный признак неправильного пути.

Что хотели и что почти получилось (слева направо):

image

Но хватит о прошлом — что же поменялось и куда переложили грабли, на которые хочется наступить еще раз?

WebRTC и STCP

Переосмыслив свои ошибки как разработчика, я решил еще раз прочесать поле технологий, которые можно использовать для создания MMORPG в браузере. Для меня все еще принципиально оставаться в рамках браузеров, так как это позволяет очень легко запускать эксперименты ничего не устанавливая (тем более, что уже и Unreal Engine 4 готовится к выходу в браузерном виде).

Как оказалось, за время разработки мир на месте не стоял и новой горячей темой стал WebRTC — все начали делать убийц skype и chatroulette, но что действительно привлекло внимание это пункты unreliable и unordered в используемом протоколе STCP — а это значит, что проблема номер три решена! Но не без ограничений.

Для работы WebRTC нужен так называемый сигнальный сервер, который будет отвечать за начальное соединение двух браузеров по STCP путем передачи сообщений от STUN серверов. Это сделано для обхода NAT и об этом можно подробнее прочитать в статье «Как мы делали сервис на WebRTC» и «WebRTC in the real world: STUN, TURN and signaling». Из последней статьи для себя я вынес то, что если вы предоставляете WebRTC сервис за деньги — то вы в аду (нужно добиваться 100% работы из всех сетей). Хорошо, что это не наш случай.

Теперь архитектура нашей клиент-серверной игры может выглядеть вот таким образом:

image

Как видно, всё тяжелое взаимодействие переложено с сервера на клиентов. Такой подход заманчив, но он вносит весьма не тривиальные задачи для решения:

  1. Оптимально ли использовать полносвязный граф
  2. Как синхронизировать состояние игры для всей сети
  3. Что делать с нечестными игроками

Если на два последних вопроса ответ частично зависит от игровой модели, которую вы разрабатываете, то на первый вопрос можно искать ответ для общего случая. Тем не менее интересно прочитать про huntercoin — проект, использующий аналог bitcoins для синхронизации игры, хотя это не подходит для динамичных моделей и обязывает иметь полную синхронизацию всех транзакций (если я правильно понимаю, как работает bitcoin'ы). Остальные рассуждения по этим пунктам я оставлю для следующей статьи.

Для проверки гипотезы о полном графе (а так же стресс-теста сервера) нам потребуется маленькая демо-игра, которая будет уметь замерять latency между клиентами. Если окажется так, что прямые соединения всегда быстрее передачи сообщения через другого клиента — то полный граф, так полный граф (в чем я сомневаюсь).

MMO — Mouse Multiplayer Online

Demo: mmo.jit.su

Почти как год назад мир увидел crowd-sourced music video. Если вы пропустили его, самое время прерваться и посмотреть (а там есть на что). Идея проста — записываем движения курсора во время видео и добавляем его к уже накопившимся. В итоге получаем весьма занимательный видеоряд из курсоров (в начале мне показалось, что синхронизация происходит в реальном времени, но нет). Посмотрев этот клип еще раз — мне пришла идея для демо-игры — будем двигать курсорами.

Для реализации нам потребуется что-либо облегчающее работу с сигнальным сервером и созданием соединений между клиентами. Хорошим вариантом является Peer.js — который предоставляет готовый сигнальный сервер (правда основанный на restify) и даже хостит публичный сигнальный сервер.

Весь код клиентской части умещается в 100 строчек JavaScript, серверная часть на Node.JS и того меньше — 55. Приводить их в статье я смысла не вижу, так как они лежат в открытом доступе и довольно просты в написании. Сам код выложен под лицензией MIT и я надеюсь он поможет начинающим сделать свою breakable-toy.

Что в итоге?

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

Автор: doubled

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js