Veliri. Дневник разработки 2

клиент-серверное общение

https://www.instagram.com/pol_mousquetaire/
https://www.instagram.com/pol_mousquetaire/

Приветствую DTF. Я все еще продолжаю делать ММО игру.)

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

Как это было:

До недавнего времени сетевое общение клиента и сервера было организовано на 2х уровнях:

1) действия завязанные на сервер тик

  1. сбор изменений за прошедший сервер тик(передвижение объектов, обзор (выдел/не видел), обновление объектов)
  2. формирование объектов сообщения по изменениям

  3. проверка на доступность этого сообщения по каждому клиенту (игрок может не видеть тот участок где происходит событие и ему событие не отправляется)

  4. отправка пачек изменений клиенту в формате json, которая выглядит как то так:

// движение объекта: [{ "id": 123, "x": 999, "y": 999, "a": 360 },{ "id": 123, "x": 999, "y": 999, "a": 360 }]

Если таких объектов игрок видит 10 то он получит массив из 10 таких изменений, что логично. Так же он получит в других сообщениях изменения своего обзора, перемещение снарядов которые он видит, изменение объектов(хп, энергия, размер и другие метаданные)

В среднем за 1 сервер тик (64мс) 1 клиент получает 6 сообщений (90 сообщений в секунду) с данными изменения мира которого он видит и размер этих сообщений мог изменится от 10 кбайт/c (кбайт) до 75 кбайт/c в зависимости от экшена творящегося вокруг.

2) Какие то события которые не привязаны жестко к сервер тику т.к. происходят редко или отдаются по запросу, для примера:

  • произведение выстрела
  • использование снаряжения
  • взрыв пули (попадание в коллизию)
  • нотификация об уроне/заработанных очках
  • обновление состояния боевой группы
  • серверный прицел
  1. происходит событие, формируется сообщение
  2. проходит фильтр по обзору или получателю

  3. отправляется в произвольном json формате.

Если сложить оба эти варианта то на 1 клиента в бою сервер посылал 200 сообщений в секунду, трафиком до 100 кбайт.

На стороне клиента js ловит эти 200 сообщений, парсит в json и нехитрым образом обрабатывает (пожалуйста не бейте меня >_<).

if (data.e === "gravity_gun_target") { GravityGunTarget(data) } if (data.e === "gravity_gun_run") { GravityGunRun(data) } if (data.e === "gravity_gun_drop") { GravityGunDrop(data) }

Почему я решил это переделать

В целом меня все устраивало, в сессионной игре на 16 персонажей клиент чувствовал себя +-уверенно и в основном лагал мой код а не сеть. Но я столкнулся с тем что начиная с пинга 50мс++ до сервера клиент начинал очень сильно тормозить в плане управления (у меня авторитарный сервер без предсказаний на стороне клиента).

Проблема была очевидна т.к. сервер тик составлял 64мс то задержка после ввода составляла (25мс + 1-64мс + 64мс + 25мс) + клиент отставал на 1 тик это еще + 64мс (я не уверен в правильности этих расчетов ¯\_(ツ)_/¯), в мое игре не нужен супер быстрый отклик но это уже слишком.

Я не хотел возится с предсказаниям, просто снизил сервер тик до 32мс и задержка стала приемлемой.

Но это повлекло за собой увеличение трафика, кол-во сообщений и кол-во событий на клиенте в 2 раза (а еще кол-во проверок на сервере, но это совсем другая история) и не лучшим образом сказалось на производительности.

Как сейчас

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

Я решил не искать готового решения и решил проблему в лоб, это выглядит примерно так:

server (go):

Veliri. Дневник разработки 2

client (js):

Veliri. Дневник разработки 2

Конкретно это сообщение стало весить в 2 раза меньше (64б против 29б). Я выделил самые частые сообщения и переписал их в этот формат, в итоге трафик уменьшился в 4 раза, но кол-во сообщений все еще было большое.

Для уменьшения кол-ва сообщений я решил формировать большой пакет данных который хранит в себе все события которые произошли за время тика и отправлять каждому клиенту всего 1 сообщение состоящие из набора байт в таком формате.

Veliri. Дневник разработки 2
  • EventID — указывает на команду

  • следующие 4 байта указывают размер сообщения определённой группы данных
  • на 5 байте начинается полезные для клиента данные
  • на 5 байте + размер предыдущей группы данных, начинается следующая группа данных

Группы имеют строгий порядок и даже если сообщений нет то просто будет указана длина полезных данных 0, на клиенте это дело выглядит как то так:

Veliri. Дневник разработки 2

В этот пакет попали все сообщения из 1й группы и самые частые сообщения из второй группы что сократило среднее кол-во сообщений c 400/c до 60/c, что все еще много но уже лучше)

Итог

Трафика в игре стало в 2 раза меньше (без учета изменения сервер тика в 4 раза), а бонусом я получил улучшенную производительность клиента, хотя все это работает только в браузерах на хромиуме

Ну а вообще я решал проблему отклика управления, сейчас более чем играбельно с пингом 50мс (но это не точно).

1717
3 комментария

Как думаешь продвигать игру после релиза?
Успехов в разработке:)

1
Ответить

У меня нет какого то вменяемого плана продвижения

Когда доделаю сессионные игры и реализую сюжетную линию в открытом мире, то думаю запуститься в вк маркете - появится первая аудитория, фитбек и заодно протестирую там все. Дальше по обстоятельствам)

1
Ответить

Красава, продолжай. Помог в понимании

1
Ответить