Scout — протокол матчей ESports Turbo
Автоматический протокол матчей для киберспортивного оператора ESports Turbo: события Dota 2 попадают из игры в живую таблицу рядом с трансляцией, судья видит счёт и таймер в реальном времени и выгружает готовый протокол в Excel.
Проект под NDA — детали архитектуры и код готов разобрать голосом.

Результат
- 2
- турнирных формата: Dota 2 5×5 Turbo и 1×1 Solo
- 2
- независимых источника данных: GSI и кастомный режим
- 16
- типов событий матча в протоколе
Экраны продукта

Протокол матча в панели судьи: события Dota 2 приходят по вебсокету и ложатся в таблицу с игровым временем и счётом. Соседняя кнопка собирает из этой же таблицы файл Excel для организаторов.

Карточки счёта, игровое время и отметка LIVE обновляются из общей сущности матча, а ниже идёт закупка предметов по игрокам обеих сторон.

Расписание турнирного дня: судья выбирает дату в календаре и переходит из строки матча прямо в протокол.

Публичный сайт лиги: ежедневные турниры Dota 2 Turbo, к которым подключён протокол, и заявка на форматы 1×1 и CS2.
Задача бизнеса
Оператор проводил ежедневные турниры по Dota 2 в двух форматах — 5×5 Turbo и 1×1 Solo — и вёл протокол матча руками: судья смотрел эфир и переносил в таблицу первую кровь, гонки на 5, 10 и 15 убийств, снесённые башни и бараки, Рошана, терзателя, мегакрипов и руны. Данные расходились с картинкой, а по итогам дня протокол нужно было отдавать организаторам файлом. От продукта требовалось забирать события из самой игры, показывать их рядом с плеером трансляции и не мешать турнирам: эфиры шли с 07:00 до 16:00 семь дней в неделю, окно для релиза открывалось только после последнего матча.
Моя роль
Техлид по вебу: отвечал за архитектуру и разработку протокола целиком — бэкенд на NestJS, панель судьи на Next.js, схему данных и деплой. Согласовывал контракт событий с разработчиком кастомного режима Dota 2 на Lua и вёл подрядчика по разбору GSI: описывал модель данных, ревьюил код, принимал задачи. Планировал релизы вокруг расписания эфиров и держал два стенда, чтобы правки проверялись на тесте, пока прод обслуживает турнир.
Решение
Данные приходят из двух независимых источников. Первый — Game State Integration: клиент наблюдателя шлёт состояние карты на серверный роут, бэкенд разбирает его сам, без сторонних обёрток. Второй — кастомный режим на Lua: он ловит события внутри игры и шлёт их точечно, отдельными запросами на события, счёт и таймер. Оба источника пишут в общую сущность лога с идентификатором матча, поэтому один матч собирается из двух потоков без дублей, а если один источник отваливается, протокол продолжает жить на втором. Бэкенд на NestJS с TypeORM и PostgreSQL распознаёт начало и конец матча по состояниям игры, принудительно закрывает предыдущий матч при старте нового и чистит логи через минуту после победы. Панель судьи на Next.js держит на одной странице таблицу событий на русском, карточки счёта, таймер и плеер трансляции; обновления приходят по вебсокету, выгрузка в Excel формируется на клиенте. Доступ закрыт авторизацией, стенды разведены на stage и прод. Турниры, матчи и команды переехали из старой админки в Strapi поверх MongoDB, а расхождение эфира и протокола закрыл локальный ресендер: он придерживает пакеты GSI на нужное число секунд, чтобы таблица не опережала картинку.