Pull to refresh

Bomberman Online и хабраэффект — 450 игроков на одной карте. Отчёт и детали игрового движка

Reading time 4 min
Views 33K


Как и обещали в топике-анонсе нашей игры, выкладываем отчёт о хабраэффекте и детали игрового движка.


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

 

Статистика за 3 недели


Зарегистрировано игроков: 4 663
Сыграно раундов: 2 252
Кол-во игроков (включая гостей): 13 402
Кол-во фрагов: 469 534
Кол-во смертей: 545 957
Кол-во забитых мячей: 1 707 (WTF?)

[во время хабраэффекта]
Игроков онлайн (максимум): 453
Загрузка ЦП (по графику мунина): 168% (16 ядер — 1600%)
Использование памяти: 1.65 ГБ
Трафик (максимум): исходящий 24 Мбит/с | входящий 9 Мбит/с

(Графики кликабельны)
[Engine] Connections [1]

[Engine] Main cycle time [2]

[Engine] Bytes per send [3]

[Server] CPU usage

[Server] Memory usage


ТТХ


Хостинг: выделенный сервер в ЦОД Владимир (который любезно предоставлен Parking.ru на время хабратестирования)

1. Игровой движок на Java & Jetty
2. HAProxy — проксирование вебсокетов
3. Nginx — статика и проксирование к ноде
4. Веб-функционал — Node.js (packages: express, mysql, request, nodemailer, validator, moment, forever)
5. Клиентсайд — GWT, транслируемый в javascript

 

Детали игрового движка


Далее — от лица Jedi_Knight (автора движка).
Разработка игровых приложений на канвасе — перспективное направление, в котором сейчас двигаются многие группы разработчиков, например эстонский проект mightyfingers.com

Некоторые идеи взяты из статьи www.html5rocks.com/en/tutorials/canvas/performance

Основы Тайлизма на HTML5 canvas


Терминология: тайл — один квадратик, например 32×32; чанк — 8×8 тайлов.

Рассмотрим особенности тайлового движка. Вот модель, к которой я пришёл:

1) Субпиксельная прорисовка для тайлов просто не нужна.

2) Используется несколько прозрачных canvas-ов, расположенных друг на друге. Если terrain не модифицировался, не было скроллинга, то его можно не перерисовывать — это даёт небольшой прирост производительности (по сравнению с оптимизацией кешом).

3) Так же как и в модели игры, всё поле делится на чанки 8×8. Поскольку 64 вызова drawImage для изображений 32×32 стоят гораздо дороже одного вызова для 256×256, видимые чанки отрисовываются в кеш, состоящих из двух слоёв: под игроком и над ним.

4) Кеш ассоциативный, то есть пара canvas'ов используется нескольким чанкам на карте: чанк с координатами (X, Y) использует кеш с координатами (X mod A, Y mod B) где (A, B) — размеры кеша. При этом размеры кеша должны быть такими, чтобы на экране не появлялись одновременно два чанка с общим кешом, будет гонка.

5) Кроме canvas-ов в кеше запоминаются номера тайлов из текстуры, которые генерируются по логической карте. По номеру можно однозначно отрисовать тайл.

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

7) В случае если требуется перерисовать грязный или новый чанк, снала в буфер кладется таблица 10×10 — чанк и окружающие его тайлы. По этому буферу генерируется массив номеров тайлов — он уже 8×8. Номер тайла также может содержать информацию о тени, падающей на этот тайл. В тех местах, где номер тайла изменился с прошлого раза, происходит перерисовка.

8) Для тайлов удобнее использовать не drawImage, а createPattern и fillRect. fillRect также удобнее тем, что им можно нарисовать несколько одинаковых подряд идущих тайлов, и это будет быстрее.
Данный тест раскрывает странный эффект: что для текстур 32×32 есть существенный прирост производительности на айпаде в 2.5 раза:
jsperf.com/canvas-texture-draw-vs-fill
Коммент с дополнением от TheShock

9) И наконец зачем всё это надо: при скроллинге вместо рисования кучи мелких изображений 32×32 происходит рисование из кеша. Вызовов drawImage существенно меньше, это серьёзно влияет на производительность.

Зелёный, жёлтый, тени — рисуются на первом слое, каждый квадрат 32×32 может рисоваться независимо от других, так как номер тайла содержит информацию о подложке, стене и тени.
Красным рисуется второй слой, он сдвинут на 8 пикселей вверх.

 
 
 

Найденные баги


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

1) На большой карте определённого размера внезапно заглючил тайловый кеш, что привело к диким тормозам, с которыми справлялся только хром. Всё дело было в периодической карте: размеры карты (W,H) должны делиться на размеры кеша (A, B).



Пусть W,H=7 A,B=3, угловые чанки попадают на одну позицию кеша, а на периодической карте они попадают на один экран, что приводит к гонке. Та же ситуация наблюдается по всему “шву” карты.

2) Профилировка клиента выявила узкие места в коде, использующим java-коллекции. В этих местах коллекции заменены на самописные структуры.

3) Изменение сетевого протокола и переход с utf8 на бинарные вебсокеты в тех браузерах, где они поддерживаются (Chrome, Firefox) позволили сократить средний трафик клиента до 12 Кбит/с. Используется нативный ArrayBuffer, отмечу что в Firefox не реализован класс DataView, поэтому обходимся обычным Uint8Array.

 

На графиках в начале статьи показаны:


[1] Connections — количество активных соединений с сервером.

[2] Main cycle time — сколько занимают различные стадии главного цикла. Тик длится 100мс, preprocess — проверка команд от клиентов, tick — апдейт игрового состояния, Observers — формирование сообщений для клиентов. Total — всё вместе, и плюс время за которое сообщения уйдут в сетевой буфер. Измеряется в затраченных миллисекундах внутри одной секунды, так что когда Total приближается к 1000 — серверу плохо, начинает появляться overhead.

[3] Bytes per send — чистый трафик, отправляемый клиентам, без заголовков websocket-фреймов, в байтах/сек.

 

Изменения геймплея


Почти всё время ушло на ускорение движка, клиента и сетевого протокола. Была проведена серьезная работа по оптимизации и рефакторингу, устранена масса багов.

Но немножко удалось сделать и по геймплею: запереть врага стало легче. Но самое главное — добавлен мячик и ворота! Забившие получают спасательный щит. Особо проворные собирают по 3 щита и бегают как супергерои :)


 

Новое нагрузочное хабратестирование. Цель — 1000 игроков онлайн на одной карте


В первый раз сервер без проблем и лагов держал 350 игроков онлайн. Сегодня он готов к 1000.

Мы приглашаем всех 8, 9 и 10 июня, с 17 до 22 постараться достигнуть новой планки игроков онлайн. Присоединяйтесь, зовите друзей/коллег — мы ждём вас!
(если у кого-то недоступен домен, заходите напрямую: 141.101.245.117 — ещё не у всех обновились днс после переезда)

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

И да пребудет с вами Сила и Фан.
Tags:
Hubs:
+152
Comments 95
Comments Comments 95

Articles